728x90

- OAuth2AuthorizeRequest 이후, 과정은 이전과 동일하다.
- CustomOAuth2LoginAuthenticationFilter를 사용해서, 요청을 보낸다는 부분만 다르다.
Test
CustomOAuth2AuthenticationFilter
public class CustomOAuth2AuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private static final String DEFAULT_FILTER_PROCESSING_URI = "/oauth2Login/**";
private DefaultOAuth2AuthorizedClientManager oAuth2AuthorizedClientManager;
private OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository;
private OAuth2AuthorizationSuccessHandler successHandler;
private Duration clockSkew = Duration.ofSeconds(3600);
private Clock clock = Clock.systemUTC();
public CustomOAuth2AuthenticationFilter(
DefaultOAuth2AuthorizedClientManager oAuth2AuthorizedClientManager,
OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository) {
super(DEFAULT_FILTER_PROCESSING_URI); //Filter가 동작하기 위해 매칭될 URL 정보를 전달해준다.
this.oAuth2AuthorizedClientRepository = oAuth2AuthorizedClientRepository;
this.oAuth2AuthorizedClientManager = oAuth2AuthorizedClientManager;
this.successHandler = (authorizedClient, principal, attributes) -> { //최종 인가를 받고 난 후, 해당 핸들러 실행
//아직 해당 상태에서는 principal은 비인증 상태이다.
oAuth2AuthorizedClientRepository
.saveAuthorizedClient(authorizedClient, principal,
(HttpServletRequest) attributes.get(HttpServletRequest.class.getName()),
(HttpServletResponse) attributes.get(HttpServletResponse.class.getName()));
System.out.println("authorizedClient = " + authorizedClient);
System.out.println("principal = " + principal);
System.out.println("attributes = " + attributes);
};
oAuth2AuthorizedClientManager.setAuthorizationSuccessHandler(successHandler);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
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 && hasTokenExpired(authorizedClient.getAccessToken())
&& authorizedClient.getRefreshToken() != null) {
authorizedClient = oAuth2AuthorizedClientManager.authorize(authorizeRequest);
}
if (authorizedClient != null) {
OAuth2UserService<OAuth2UserRequest, OAuth2User> oAuth2UserService = new DefaultOAuth2UserService();
ClientRegistration clientRegistration = authorizedClient.getClientRegistration();
OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
OAuth2UserRequest oAuth2UserRequest = new OAuth2UserRequest(clientRegistration, accessToken);
OAuth2User oAuth2User = oAuth2UserService.loadUser(oAuth2UserRequest);
SimpleAuthorityMapper authorityMapper = new SimpleAuthorityMapper();
authorityMapper.setPrefix("SYSTEM_");
Set<GrantedAuthority> grantedAuthorities = authorityMapper.mapAuthorities(
oAuth2User.getAuthorities());
OAuth2AuthenticationToken oAuth2AuthenticationToken = new OAuth2AuthenticationToken(
oAuth2User, grantedAuthorities, clientRegistration.getRegistrationId());
SecurityContextHolder.getContext().setAuthentication(oAuth2AuthenticationToken);
this.successHandler.onAuthorizationSuccess(authorizedClient, oAuth2AuthenticationToken,
createAttributes(request, response));
return oAuth2AuthenticationToken;
}
return null;
}
private boolean hasTokenExpired(OAuth2Token token) {
return this.clock.instant().isAfter(token.getExpiresAt().minus(this.clockSkew));
}
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;
}
}
- 빈으로 등록할 것이 아니기 때문에 생성자를 통해 OAuth2AuthorizedClientManager와 OAuth2AuthorizedClientRepository를 주입받는다.
- SuccessHandler를 인증까지 완료 후에 호출하기 위해 SecurityContextHolder에 인증 Token을 넣은 뒤, onAuthorizationSuccess를 호출해 준다.
- UsernamePasswordAuthenticationFilter 이전에 Filter를 동작시킬 것이기 때문에 authentication은 null일 수밖에 없다.
- 이전에는 controller에서 해당 값을 불러 문제가 없었다(모든 필터를 다 통과하고 호출)
- authentication에 익명사용자용 토큰을 만들어서 넣어주면 된다.
OAuth2ClientConfig
@Configuration(proxyBeanMethods = false)
public class OAuth2ClientConfig {
@Autowired
private DefaultOAuth2AuthorizedClientManager auth2AuthorizedClientManager;
@Autowired
private OAuth2AuthorizedClientRepository authorizedClientRepository;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(
authRequest -> authRequest.antMatchers("/", "/oauth2Login", "/client").permitAll()
.anyRequest().authenticated());
http
.oauth2Client(Customizer.withDefaults());
http
.addFilterBefore(customOAuth2AuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class);
return http.build();
}
private CustomOAuth2AuthenticationFilter customOAuth2AuthenticationFilter() {
CustomOAuth2AuthenticationFilter auth2AuthenticationFilter = new CustomOAuth2AuthenticationFilter(
auth2AuthorizedClientManager, authorizedClientRepository);
auth2AuthenticationFilter.setAuthenticationSuccessHandler(
(request, response, authentication) -> response.sendRedirect("/home"));
return auth2AuthenticationFilter;
}
}
- UsernamePasswordAuthenticationFilter 이전에 내가 만든 필터를 넣어준다.
- 빈으로 등록되어 있는 OAuth2AuthorizedClientManager와 OAuth2AuthorizedClientRepository를 주입받아 생성자로 넣어준다.
- 해당 filter를 성공적으로 통과한 이후에는 /home으로 redirect 해준다
HomeController
@Controller
public class HomeController {
@Autowired
private OAuth2AuthorizedClientService oAuth2AuthorizedClientService;
@GetMapping("/home")
public String home(Model model, OAuth2AuthenticationToken oAuth2AuthenticationToken) {
OAuth2AuthorizedClient authorizedClient = oAuth2AuthorizedClientService.loadAuthorizedClient(
"keycloak", oAuth2AuthenticationToken.getName());
model.addAttribute("oAuth2AuthenticationToken", oAuth2AuthenticationToken);
model.addAttribute("AccessToken", authorizedClient.getAccessToken().getTokenValue());
model.addAttribute("RefreshToken", authorizedClient.getRefreshToken().getTokenValue());
return "home";
}
}
- OAuth2AuthorizedClientService를 주입받아 keycloak으로 설정한 값을 읽어온다.
- 인증이 완료된 객체로 AccessToken, RefreshToken 모두 존재하므로 model에 담아 리턴한다.
728x90
'스프링 시큐리티 OAuth2 > OAuth 2.0 Client - oauth2Client()' 카테고리의 다른 글
| @RegisteredOAuth2AuthorizedClient 이해 및 활용 (0) | 2023.02.24 |
|---|---|
| DefaultOAuth2AuthorizedClientManager - Refresh Token 권한 부여 구현하기 (0) | 2023.02.22 |
| DefaultOAuth2AuthorizedClientManager - Client Credentials 권한 부여 구현하기 (0) | 2023.02.21 |
| DefaultOAuth2AuthorizedClientManager - Resource Owner Password 권한 부여 구현하기 (0) | 2023.02.20 |
| DefaultOAuth2AuthorizedClientManager (0) | 2023.01.29 |