1. Oauth2.0 이란?
- The OAuth 2.0 Authorization Framework
- OAuth 2.0 동작 방식의 이해|작성자 MDS인텔리전스
- 인증을 위한 개방형 표준 프로토콜
2. 스프링부트 및 시큐리티의 Oauth2.0
OAuth2AuthorizationRequestRedirectFilter
public class OAuth2AuthorizationRequestRedirectFilter extends OncePerRequestFilter {
/**
* The default base {@code URI} used for authorization requests.
*/
public static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization";
private final ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();
private final RedirectStrategy authorizationRedirectStrategy = new DefaultRedirectStrategy();
private OAuth2AuthorizationRequestResolver authorizationRequestResolver;
private AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = new HttpSessionOAuth2AuthorizationRequestRepository();
private RequestCache requestCache = new HttpSessionRequestCache();
// 생략
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestResolver.resolve(request);
if (authorizationRequest != null) {
this.sendRedirectForAuthorization(request, response, authorizationRequest);
return;
}
}
catch (Exception ex) {
this.unsuccessfulRedirectForAuthorization(request, response, ex);
return;
}
// 생략
}
- 리소스 서버에 인증 요청을 수행할 때, 가장 먼저 Oauth2.0 인증과 관련된 동작을 수행하는 필터이다.
- 이 때 oauth2 기능을 트리깅하는 엔드포인트 url 은 '/oauth2/authorization/{provider_id}' 로 해당 클래스의 DEFAULT_AUTHORIZATION_REQUEST_BASE_URI 로 설정되어 있음을 위 코드로 확인할 수 있을 것이다.
- 아래의 DefaultOAuth2AuthorizationRequestResolver 클래스를 통해 요청을 리졸브(build)하고, DefaultRedirectStrategy에 정의된 정해진 전략에 따라 (리디렉션을 이용하여) 인증 과정 동작을 수행한다.
DefaultOAuth2AuthorizationRequestResolver
- 정해진 패턴에 따라 인증 프로바이더(구글, 네이버, 카카오 등)를 구분하고, oauth2 로직을 수행할 request 객체를 생성한다.
- 생성된 OAuth2AuthorizationRequest 는 아래와 같다.
- 여기서 인증 프로바이더 관련된 정보는 InmemoryClientRegistrationRepository 를 통해 가져온다. (스프링 설정 파일에 관련 정보를 세팅해두면, 해당 Repository 클래스에 정보가 저장될 것이다.)
DefaultRedirectStrategy
- 인증 과정에 수반되는 리다이렉트 동작 로직을 갖고 있는 클래스
OAuth2LoginAuthenticationFilter
- AbstractAuthenticationProcessingFilter 를 상속받아 구현된다.
- 프로바이더 서버에서 authorization_code를 포함해 특정 url(/login/oauth2/code)로 리디렉션을 수행하여 나의 서버로 다시 요청이 도착하면 거치게 되는 필터이다.
- 즉, authorization_code 를 활용해 실제 로그인 과정을 수행하는 파트를 담당하는 필터이다.
- 주입된 authenticationManager를 이용해 authenticate 과정을 수행하고 토큰 결과값을 리턴한다.
public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
/**
* The default {@code URI} where this {@code Filter} processes authentication
* requests.
*/
public static final String DEFAULT_FILTER_PROCESSES_URI = "/login/oauth2/code/*";
private static final String AUTHORIZATION_REQUEST_NOT_FOUND_ERROR_CODE = "authorization_request_not_found";
private static final String CLIENT_REGISTRATION_NOT_FOUND_ERROR_CODE = "client_registration_not_found";
private ClientRegistrationRepository clientRegistrationRepository;
private OAuth2AuthorizedClientRepository authorizedClientRepository;
private AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = new HttpSessionOAuth2AuthorizationRequestRepository();
private Converter<OAuth2LoginAuthenticationToken, OAuth2AuthenticationToken> authenticationResultConverter = this::createAuthenticationResult;
// 생략
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
MultiValueMap<String, String> params = OAuth2AuthorizationResponseUtils.toMultiMap(request.getParameterMap());
if (!OAuth2AuthorizationResponseUtils.isAuthorizationResponse(params)) {
OAuth2Error oauth2Error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}
OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestRepository
.removeAuthorizationRequest(request, response);
if (authorizationRequest == null) {
OAuth2Error oauth2Error = new OAuth2Error(AUTHORIZATION_REQUEST_NOT_FOUND_ERROR_CODE);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}
// 생략
this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, oauth2Authentication, request, response);
return oauth2Authentication;
}
//...
}
3. 커스텀해보기
- https://docs.spring.io/spring-security/site/docs/5.2.12.RELEASE/reference/html/oauth2.html#oauth2Client-auth-code-redirect-uri
- 위 링크에서 클라이언트 설정 예시를 자세히 확인할 수 있다.
- 나는 아래와 같이 시큐리티를 설정하여 최초 oauth2 로그인을 유발하는 엔드포인트와 프로바이더 서버에서 돌아오는 리디렉션 주소를 변경했다.
- baseUri() 들을 변경할 때는 스프링 설정파일에 들어가는 oauth2 프로바이더 설정값에도 동일하게 리디렉션 주소값을 설정해두어야 하는 것에 유의한다.
@RequiredArgsConstructor
@EnableWebSecurity
@Configuration
public class SecurityConfig {
//...생략
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
//...생략
.oauth2Login()
.authorizationEndpoint()
.baseUri("/api/oauth2/authorization")
.authorizationRequestRepository(oAuth2CookieAuthorizationRequestRepository)
.and()
.redirectionEndpoint()
.baseUri("/api/login/oauth2/code/*")
.and()
.userInfoEndpoint()
.and()
.successHandler(oAuth2AuthenticationSuccessHandler)
.failureHandler(oAuth2AuthenticationFailureHandler)
.and()
.exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint) // 401
.accessDeniedHandler(jwtAccessDeniedHandler); // 403
return http.build();
}
}
OAuth2AuthorizationCodeAuthenticationProvider
'Spring' 카테고리의 다른 글
Spring Security 기본 (0) | 2023.10.05 |
---|---|
[SpringBoot]로그백 필터 활용하기 (0) | 2023.09.25 |
Java config를 이용하여 스프링 컨테이너(ApplicationContext) 설정하기 (0) | 2021.07.10 |
스프링 Ioc/DI 컨테이너 사용 (xml 파일 이용) (0) | 2021.07.08 |
forward & redirect (0) | 2021.07.01 |