Published 2023. 4. 13. 21:48
Remember Me 설정
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
//region rememberMe
http
.rememberMe()
.rememberMeParameter("remember") // 기본 파라미터명은 remember-me
.tokenValiditySeconds(3600) // 유지 시간 Default 는 14일
.alwaysRemember(false) // 리멤버 미 기능을 활성화하지 않아도 계속 실행할 것인지
//endregion
;
return http.build();
}
}
전체 코드
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated();
http
.formLogin()
// .loginPage("/login") //사용자 정의 로그인 페이지
//region 핸들러없이 바로 이동페이지 설정
// .defaultSuccessUrl("/") // 로그인 성공 후 이동 페이지
// .failureUrl("/login") // 로그인 실패 후 이동페이지
//endregion
//region 제공해주는 로그인페이지 파라마터 name 및 Action 셋팅
.usernameParameter("userId") // 아이디 파라미터명 설정
.passwordParameter("passwd") //패스워드 파라미터명 설정
.loginProcessingUrl("/login_proc") //로그인 form Action url 설정
//endregion
// 로그인 성공후 핸들러
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
System.out.println("authentication" + authentication.getName());
response.sendRedirect("/");
}
})
// 로그인 실패 후 핸들러
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
System.out.println("exception" + exception.getMessage());
response.sendRedirect("/login");
}
})
.permitAll(); //로그인페이지를 모든 사용자가 접근 가능하도록 설정
//region logout
http
.logout() //Post로 진행해야함
.logoutUrl("/logout") // 로그아웃 처리 url
.logoutSuccessUrl("/login") // 로그아웃 성공 후 이동페이지
.addLogoutHandler(new LogoutHandler() { // 로그아웃 핸들러
@Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
HttpSession session = request.getSession();
session.invalidate();
}
})
.logoutSuccessHandler(new LogoutSuccessHandler() { // 로그아웃 성공 후 핸들러
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.sendRedirect("/login");
}
})
.deleteCookies("JSESSIONID", "remember-me"); // 로그아웃 후 쿠키 삭제
// endregion
//region rememberMe
http
.rememberMe()
.rememberMeParameter("remember") // 기본 파라미터명은 remember-me
.tokenValiditySeconds(3600) // 유지 시간 Default 는 14일
.alwaysRemember(false) // 리멤버 미 기능을 활성화하지 않아도 계속 실행할 것인지
//endregion
;
return http.build();
}
}
화면 플로우
rememberMe 를 설정하면 아래와 같이 로그인 폼에 Remember me on this computer. 라는 체크 박스가 생긴다. 체크를 해주고 로그인을 하면 JSESSIONID이 삭제되어도 로그인유지가 가능하다.
그 이유는 remember-,me 라는 쿠키가 생겼기 때문인데. 설정해준 기간만큼 유지가 가능하다.
실제로 JSESSIONID를 삭제 후 새로고침을 해도 유지가 되는 것을 확인 할 수 있다.
코드 플로우
AbstractAuthenticationProcessingFilter.class의 successfulAuthentication() 에서 10번째 줄을 보면 loginSuccess 메소드가 실행된다.
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authResult);
SecurityContextHolder.setContext(context);
this.securityContextRepository.saveContext(context, request, response);
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult));
}
this.rememberMeServices.loginSuccess(request, response, authResult);
if (this.eventPublisher != null) {
this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
}
this.successHandler.onAuthenticationSuccess(request, response, authResult);
}
타고 들어가면 rememeber-me 쿠키가 만들어져 Response에 담게된다.
@Override
public void onLoginSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication successfulAuthentication) {
String username = retrieveUserName(successfulAuthentication);
String password = retrievePassword(successfulAuthentication);
// If unable to find a username and password, just abort as
// TokenBasedRememberMeServices is
// unable to construct a valid token in this case.
if (!StringUtils.hasLength(username)) {
this.logger.debug("Unable to retrieve username");
return;
}
if (!StringUtils.hasLength(password)) {
UserDetails user = getUserDetailsService().loadUserByUsername(username);
password = user.getPassword();
if (!StringUtils.hasLength(password)) {
this.logger.debug("Unable to obtain password for user: " + username);
return;
}
}
int tokenLifetime = calculateLoginLifetime(request, successfulAuthentication);
long expiryTime = System.currentTimeMillis();
// SEC-949
expiryTime += 1000L * ((tokenLifetime < 0) ? TWO_WEEKS_S : tokenLifetime);
String signatureValue = makeTokenSignature(expiryTime, username, password);
setCookie(new String[] { username, Long.toString(expiryTime), signatureValue }, tokenLifetime, request,
response);
if (this.logger.isDebugEnabled()) {
this.logger.debug(
"Added remember-me cookie for user '" + username + "', expiry: '" + new Date(expiryTime) + "'");
}
}