OAuth2 인증 방식도 UsernamePassword 인증과 같이 인증을 진행하기 위한 Filter 가 존재한다. OAuth2LoginAuthenticationFilter 에서는 Authentication Server 로부터 Authorization Code 를 받은 후 Acess Token 과 Reflesh Token 을 받기 위한 과정을 진행한다.
ClientRegistrationRepository
ClientRegistration 정보를 저장 및 가져오기 위한 Class
OAuth2AuthorizedClient
인증된 Client 정보를 관리하기 위한 Class
Access Token, Reflesh Token, ClientRegistration 정보를 관리하는 Class
OAuth2AuthorizedClientRepository
OAuth2AuthorizedClient 정보를 저장 및 가져오기 위한 Class
인증이 진행중인 Client 정보를 가져온다.
기본 구현체는 HttpSessionOAuth2AuthorizedClientRepository
OAuth2AuthorizedClientService
Application Level 에서 OAuth2AuthorizedClient 정보를 가져오기 위한 Class
인증이 완료된 Client 정보를 저장소 에서 가져올 때 사용한다.
메모리에 OAuth2AuthorizedClient 객체를 저장하는 InMemoryOAuth2AuthorizedClientService
데이터 베이스에 OAuth2AuthorizedClient 객체를 저장하는 JdbcOAuth2AuthorizedClientService
AuthorizationRequestRepository
인증 요청에서 인증 응답을 받을때 까지 OAuth2AuthorizationRequest 의 지속성을 보장하기 위한 Class
기본 구현체는 Session 에 저장하는 HttpSessionOAuth2AuthorizationRequestRepository
OAuth2AuthorizationRequestResolver
registrationId 와 HttpServletRequest 를 OAuth2AuthorizationRequest 객체를 생성하기 위한 Class
기본 구현체는 DefaultOAuth2AuthorizationRequestResolver
OAuth2AccessTokenResponseClient
Authorization Code 를 Access Token 으로 교환하는데 사용하는 Class
기본 구현체는 DefaultAuthorizationCodeTokenResponseClient
인증 과정
AuthorizationRequestRepository 객체에서 OAuth2AuthorizationRequest 정보를 가져온 후 해당 객체를 지운다.
ClientRegistration, OAuth2AuthorizationRequest, OAuth2AuthorizationResponse 객체를 이용해 OAuth2LoginAuthenticationToken 객체를 생성한다.
AuthenticationProvider 객체를 이용해 인증을 진행한 후 새로운 OAuth2LoginAuthenticationToken 객체를 반환 받는다.
Access Token 정보와 Reflesh Token 정보 ClientRegistration 정보를 담는 OAuth2AuthorizedClient 객체를 생성
OAuth2AuthorizedClientRepository 에 성성된 OAuth2AuthorizedClient 객체를 저장한다.
@Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
// Request Parameter 에 code, state 가 있는지 확인한다. if (!OAuth2AuthorizationResponseUtils.isAuthorizationResponse(params)) { OAuth2Erroroauth2Error=newOAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST); thrownewOAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); }
// AuthorizationRequestRepository 객체에서 state 정보에 해당하는 OAuth2AuthorizationRequest 객체를 가져온후 해당 값을 AuthorizationRequestRepository 에서 지운다. // AuthorizationRequestRepository 객체 기본 구현체는 HttpSessionOAuth2AuthorizationRequestRepository OAuth2AuthorizationRequestauthorizationRequest=this.authorizationRequestRepository .removeAuthorizationRequest(request, response);
if (authorizationRequest == null) { OAuth2Erroroauth2Error=newOAuth2Error(AUTHORIZATION_REQUEST_NOT_FOUND_ERROR_CODE); thrownewOAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); }
// Registration 에 맞는 client_id 와 client_secret 정보를 가져와 ClientRegistration 객체를 생성한다. ClientRegistrationclientRegistration=this.clientRegistrationRepository.findByRegistrationId(registrationId); if (clientRegistration == null) { OAuth2Erroroauth2Error=newOAuth2Error(CLIENT_REGISTRATION_NOT_FOUND_ERROR_CODE, "Client Registration not found with Id: " + registrationId, null); thrownewOAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); }
// ClientRegistration 객체와 OAuth2AuthorizationRequest 객체와 OAuth2AuthorizationResponse 객체를 이용해 OAuth2LoginAuthenticationToken 객체를 만든다. OAuth2LoginAuthenticationTokenauthenticationRequest=newOAuth2LoginAuthenticationToken(clientRegistration, newOAuth2AuthorizationExchange(authorizationRequest, authorizationResponse)); authenticationRequest.setDetails(authenticationDetails);
// AuthenticationProvider 객체를 이용해 인증을 진행한다. 인증시 ClientRegistration 정보와 Authorization_code 정보를 사용한다. // 인증을 진행한 후 Acess Token, Refresh Token 정보가 담긴 Authentication(Token) 객체를 가져온다. OAuth2LoginAuthenticationTokenauthenticationResult= (OAuth2LoginAuthenticationToken) this .getAuthenticationManager() .authenticate(authenticationRequest);
OAuth 방식을 이용해 인증을 진행하고 Authentication(인증) 객체를 생성하는 Provider
OAuth2AuthorizationExchange
OAuth2AuthorizationRequest 객체와 OAuth2AuthorizationResponse 객체를 관리하는 객체
ClientRegistration
client_id, client_secret, scope 정보를 관리하는 객체
OAuth2LoginAuthenticationToken
OAuth2 인증시 사용하는 Authentication 객체
OAuth2AuthorizationCodeAuthenticationProvider
Authorization Code 를 이용해 인증을 진행하는 Provider
Authorization Code 를 이용해 Access Token 을 가져오는 OAuth2AccessTokenResponseClient 객체를 갖고 있다.
인증에 성공하면 Access Token 과 Refresh Token 을 Resource Server 로부터 받게 된다.
OAuth2AuthorizationCodeAuthenticationToken
Authorization Code 를 이용한 인증 후 생성되는 Authentication 객체
OAuth2AccessToken(accessToken) 과 OAuth2RefreshToken(refreshToken) 을 관리한다.
OAuth2AccessToken
OAuth2 에서 사용하는 Access Token 을 관리하는 객체
OAuth2RefreshToken
OAuth2 에서 사용하는 Reflesh Token 을 관리하는 객체
OAuth2User
사용자 Principal 를 관리하는 객체
OAuth2User 인터페이스의 구현 인스턴스는 OAuth2AuthenticatedPrincipal 인터페이스로 표현할 수 있다.
Authentication.getPrincipal() 를 이용해 값을 얻어올 수 있다.
인증 절차
Authorization Code 를 이용해 Resource Server 로부터 Access Token 과 Refresh Token 을 발급 받는다.
Access Token 을 이용해 Resource Server 로부터 사용자 정보를 가져온후 OAuth2User 객체를 반환 받는다.
반환 받은 OAuth2User 객체와 Access Token 과 Refresh Token, Client Registration 정보를 이용해 새로운 AuthenticationToken(OAuth2LoginAuthenticationToken) 객체를 생성 후 반환한다.
@Override public Authentication authenticate(Authentication authentication)throws AuthenticationException { // Oauth2 인증을 통해 생성되는 Authentication 객체 OAuth2LoginAuthenticationTokenloginAuthenticationToken= (OAuth2LoginAuthenticationToken) authentication;
if (loginAuthenticationToken.getAuthorizationExchange().getAuthorizationRequest().getScopes().contains("openid")) { returnnull; }
OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthenticationToken; try { // Authorization Code 를 이용해 인증 진행 후 Acces Token 정보를 가져온다. authorizationCodeAuthenticationToken = (OAuth2AuthorizationCodeAuthenticationToken) this.authorizationCodeAuthenticationProvider .authenticate(newOAuth2AuthorizationCodeAuthenticationToken( loginAuthenticationToken.getClientRegistration(), loginAuthenticationToken.getAuthorizationExchange())); } catch (OAuth2AuthorizationException ex) { OAuth2Erroroauth2Error= ex.getError(); thrownewOAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex); }
DefaultOAuth2UserService 주 역할은 Access Token 을 이용해 Resource Server 로부터 사용자 정보를 가져와 OAuth2User 객체를 생성하는 역할이다.
userNameAttributeName 정보가 있는지 확인
Resource Server 에서 사용자 정보를 식별하기 위한 Id 역할을 하는 정보
Resource Server 로부터 사용자 정보를 가져온다.
가져온 사용자 정보를 이용해 OAuth2User 객체를 생성한다.
현재 코드는 예외 코드를 제외한 로직만 있다.
@Override public OAuth2User loadUser(OAuth2UserRequest userRequest)throws OAuth2AuthenticationException {
// userNameAttributeName 정보가 있는지 확인 StringuserNameAttributeName= userRequest.getClientRegistration() .getProviderDetails() .getUserInfoEndpoint() .getUserNameAttributeName();
// Resource Server 로부터 사용자 정보를 가져온다. RequestEntity<?> request = this.requestEntityConverter.convert(userRequest); ResponseEntity<Map<String, Object>> response = getResponse(userRequest, request); Map<String, Object> userAttributes = response.getBody(); Set<GrantedAuthority> authorities = newLinkedHashSet<>(); authorities.add(newOAuth2UserAuthority(userAttributes)); OAuth2AccessTokentoken= userRequest.getAccessToken(); for (String authority : token.getScopes()) { authorities.add(newSimpleGrantedAuthority("SCOPE_" + authority)); }
// Resource Server 로부터 가져온 사용자 정보를 이용해 OAuth2User 객체를 만든다. returnnewDefaultOAuth2User(authorities, userAttributes, userNameAttributeName); }
// RestOperations 을 이용해 Resource Server 로 요청을 보낸다. private ResponseEntity<Map<String, Object>> getResponse(OAuth2UserRequest userRequest, RequestEntity<?> request) { try { returnthis.restOperations.exchange(request, PARAMETERIZED_RESPONSE_TYPE); } .... }
OAuth2LoginAuthenticationToken
OAuth 인증시 사용하는 Authentication 객체 Principal, Client Registration, Access Token, Refresh Token 등의 정보를 관리한다.