public class CustomOAuth2AuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public static final String DEFAULT_FILTER_PROCESSING_URI = "/oauth2Login/**";
private final DefaultOAuth2AuthorizedClientManager oAuth2AuthorizedClientManager;
private final OAuth2AuthorizationSuccessHandler successHandler;
public CustomOAuth2AuthenticationFilter(DefaultOAuth2AuthorizedClientManager oAuth2AuthorizedClientManager,
OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository) {
super(DEFAULT_FILTER_PROCESSING_URI);
this.oAuth2AuthorizedClientManager = oAuth2AuthorizedClientManager;
this.successHandler = (authorizedClient, principal, attributes) -> {
oAuth2AuthorizedClientRepository
.saveAuthorizedClient(authorizedClient, principal,
(HttpServletRequest) attributes.get(HttpServletRequest.class.getName()),
(HttpServletResponse) attributes.get(HttpServletResponse.class.getName())
);
};
oAuth2AuthorizedClientManager.setAuthorizationSuccessHandler(successHandler);
setSecurityContextRepository(new DelegatingSecurityContextRepository(
new RequestAttributeSecurityContextRepository(),
new HttpSessionSecurityContextRepository()
));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
Authentication authentication = SecurityContextHolder.getContextHolderStrategy()
.getContext().getAuthentication();
// 필터 단계에서는 Authentication이 아직 null일 수 있다.
// 따라서 익명 객체를 선언해준다.
if (authentication == null) {
authentication = new AnonymousAuthenticationToken(
"anonymous",
"anonymousUser",
AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")
);
}
OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
.withClientRegistrationId("keycloak")
.principal(authentication)
.attribute(HttpServletRequest.class.getName(), request)
.attribute(HttpServletResponse.class.getName(), response)
.build();
OAuth2AuthorizedClient authorizedClient = oAuth2AuthorizedClientManager.authorize(authorizeRequest);
if (authorizedClient != null) {
ClientRegistration clientRegistration = authorizedClient.getClientRegistration();
OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
OAuth2UserService<OAuth2UserRequest, OAuth2User> oAuth2UserService = new DefaultOAuth2UserService();
OAuth2User oAuth2User = oAuth2UserService.loadUser(new OAuth2UserRequest(clientRegistration, accessToken));
OAuth2AuthenticationToken oAuth2AuthenticationToken = new OAuth2AuthenticationToken(
oAuth2User,
oAuth2User.getAuthorities(),
clientRegistration.getRegistrationId()
);
SecurityContextHolder.getContextHolderStrategy()
.getContext().setAuthentication(oAuth2AuthenticationToken);
// oAuth2AuthorizedClientManager.authorize() 에서 저장해주는 로직은 있지만, 이때는 pincipal이 anonymous 상태다.
// 완전히 인증 객체를 만들고 나서 다시 저장해준다.
this.successHandler.onAuthorizationSuccess(
authorizedClient,
oAuth2AuthenticationToken,
createAttributes(request, response)
);
return oAuth2AuthenticationToken;
}
return authentication;
}
private static Map<String, Object> createAttributes(HttpServletRequest servletRequest,
HttpServletResponse servletResponse) {
Map<String, Object> attributes = new HashMap<>();
attributes.put(HttpServletRequest.class.getName(), servletRequest);
attributes.put(HttpServletResponse.class.getName(), servletResponse);
return attributes;
}
}