@RequiredArgsConstructor
@Component
public class OAuth2SuccessHandler extends SimpleUrlAuthenticationSuccessHandler{
public static final String REFRESH_TOKEN_COOKIE_NAME = "refresh_token";
...
private final TokenProvider tokenProvider;
private final RefreshTokenRepository refreshTokenRepository;
private final OAuth2AuthorizationRequestBasedOnCookieRepository authorizationRequestRepository;
private final UserService userService;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException{
OAuth2User oAuthUser = (OAuth2User) authentication.getPrincipal();
User user = userService.findByEmail((String) oAuth2User.getAttributes().get("email"));
// 1. 리프레시 토큰 생성 -> 저장 -> 쿠키에 저장
String refreshToken = tokenProvider.generateToken(user, REFRESH_TOKEN_DURATION);
saveRefreshToken(user.getId(), refreshToken); // DB에 유저 아이디와 토큰 저장
// 2. 액레스 토큰 생성 -> 패스에 액세스 토큰 추가
String accessToken = tokenProvider.generateToken(user, ACCESS_TOKEN_DURATION);
String targetUrl = getTargetUrl(accessToken);
// 3. 인증 관련 설정값, 쿠키 제거
clearAuthenticationAttributes(request, response); // 임시 저장된 인증 데이터 제거
// 4. 리다이렉트
getRedirectStrategy().sendRedirect(request, response, targetUrl); // 2로 리다이렉트
}
// 생성된 리프레시 토큰 전달받아 DB 저장
private void savedRefreshToken(Long userId, String newRefreshToken){
RefreshToken refreshToken = refreshTokenRepository.findByUserId(userId)
.map(entity -> entity.update(newRefreshToken))
.orElse(new RefreshToken(userId, newRefreshToken));
refreshTokenRepository.save(refreshToken);
}
// 생성된 리프레시 토큰을 쿠키에 저장
private void addRefreshTokenToCookie(HttpServletRequst request, HttpServletResponse response, String refreshToken){
int cookieMaxAge = (int) REFRESH_TOKEN_DURATION.toSeconds();
CookieUtil.deleteCookie(request, response, REFRESH_TOKEN_COOKIE_NAME);
CookieUtil.addCookie(response, REFRESH_TOKEN_COOKIE_NAME, refreshToken, cookieMaxAge);
}
// 인증 관련 설정값, 쿠키 제거
private void clearAuthenticationAttributes(HttpServletRequest request, HttpServletResponse response){
super.clearAuthenticationAttributes(request);
authorizationRequestRepository.removeAuthorizationRequestCookies(request, response); //OAuth 인증 위해 저장된 정보 삭제
}
}
@RequiredArgsConstrucotr
@Configuration
public class WebOAuth2SecurityConfig{
private final OAuth2UserCustomerService oAuth2UserCustomService;
private final TokenProvider tokenProvider;
private final RefreshTokenRepository refreshTokenRepository;
private final UserService userService;
...
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
...
http.oauth2Login()
.login("/login")
.authorizationEndpoint()
.authorizationRequestRepository(oAuth2AuthorizationRequestBasedOnCookieRepository())
.and()
.successHandler(oAuth2SuccessHandler())
.userInfoEndPoint()
.userService(oAuth2UserCustomService);
http.logout()
.logoutSuccessful("/login");
...
}
@Bean
public OAuth2SuccessHandler oAuth2SuccessHandler(){
return new OAuth2SuccessHandler(tokenProvider, refreshTokenRepository, oAuth2AuthorizationRequestBasedOnCookieRepository(), userService);
}
}
@Log4j2
public class ClubLoginSuccessHandler implements AuthenticationSuccessHandler{
private RedirectStategy redirectStrategy = new DefaultRedirectStrategy();
public ClubLoginSuccessHandler(PasswordEncoder passwordEncoder){
this.passwordEncoder = passwordEncoder;
}
@Overrid
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException{
ClubAuthMenberDTO authMember = (ClubAuthMemberDTO) authentication.getPrincipla();
boolean fromSocial = authMember.isFromSocial();
log.inco(" 회원 정보 수정 필요 여부 : "+fromSocial);
boolean passwordResult = passwordEncoder.matches("1111", authMember.getPassword());
if(fromSocial && passwordResult){
redirectyStrategy.sendRedirect(request, response, "/member/modify?from=social");
}
}
}
대상 URL을 다르게 지정하는 용도
public class SecurityConfig{
...
@Bean
public ClubLoginSuccessHandler successHandler(){
return new ClubLoginSuccessHandler(passwordEncoder);
}
}
public class ApiLoginFailHandler implements AuthenticationFailureHandler{
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException{
log.info(exception.getMessage());
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
// json 리턴
response.setContentType("application/json; charset=utf-8");
JSONObject json = new JSONObject();
String message = exception.getMessage();
json.put("code", "401");
json.put("message", message);
PrintWriter out = response.getWriter();
out.print(json);
}
}
- 인증 실패시 '401' 상태 코드 반환
- SecurityConfig 적용
public class SecurityConfig{
...
ApiLoginFilter apiLoginFilter = new ApiLoginFilter("/api/login");
apiLoginFilter.setAuthenticationManager(authenticationManager());
apiLoginFilter.setAuthenticationFailureHandler(new ApiLoginFailHnadler());
return apiLoginFilter
}