SpringBoot 는 SpringSecurity 와 함께 자동 로그 인 기능 을 실현 하 는 코드 입 니 다.

자동 로그 인 은 우리 가 소프트웨어 개발 을 할 때 매우 흔히 볼 수 있 는 기능 이다.예 를 들 어 우리 가 QQ 메 일 에 로그 인 하 는 것 이다.
在这里插入图片描述
많은 사이트 에서 우 리 는 로그 인 할 때 비슷 한 옵션 을 볼 수 있다.왜냐하면 항상 사용자 에 게 사용자 이름 비밀 번 호 를 입력 하 게 하 는 것 은 매우 번 거 로 운 일이 기 때문이다.
자동 로그 인 기능 은 사용자 가 로그 인 에 성공 한 후에 특정한 시간 동안 사용자 가 브 라 우 저 를 닫 고 다시 열 거나 서버 가 다시 시작 되면 사용자 가 다시 로그 인 할 필요 가 없 으 며 사용 자 는 인터페이스 데 이 터 를 직접 방문 할 수 있다 는 것 이다.
흔히 볼 수 있 는 기능 으로서 우리 의 Spring Security 도 해당 하 는 지 지 를 제 공 했 을 것 이다.본 고 는 Spring Security 에서 이 기능 을 어떻게 실현 하 는 지 살 펴 보 자.
1.remember-me 가입
설정 편 의 를 위해 두 개의 의존 도 를 추가 하면 됩 니 다.
在这里插入图片描述
설정 클래스 에 다음 코드 를 추가 합 니 다:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 @Bean
 PasswordEncoder passwordEncoder(){
 return NoOpPasswordEncoder.getInstance();
 }

 @Override
 protected void configure(AuthenticationManagerBuilder auth) throws Exception {
 auth.inMemoryAuthentication()
  .withUser("yolo")
  .password("123").roles("admin");
 }

 @Override
 protected void configure(HttpSecurity http) throws Exception {
 http.authorizeRequests()
  .anyRequest().authenticated()
  .and()
  .formLogin()
  .and()
  .rememberMe()
  .and()
  .csrf().disable();
 }
}
보시 다시 피 여기 하나.rememberMe()만 추가 하면 자동 로그 인 기능 이 성공 적 으로 추 가 됩 니 다.
다음 에 우 리 는 마음대로 테스트 인 터 페 이 스 를 추가 합 니 다.

@RestController
public class HelloController {
 @GetMapping("/hello")
 public String hello(){
 return "Hello Yolo !!!";
 }
}
在这里插入图片描述
이 럴 때 기본 로그 인 페이지 에 옵션 이 하나 더 생 겼 습 니 다.바로 저 를 기억 하 는 것 입 니 다.우 리 는 사용자 이름 비밀 번 호 를 입력 하고 이 상 자 를 기억 하고 로그 인 단 추 를 누 르 면 로그 인 작업 을 수행 합 니 다.
로그 인 데이터 에는 username 과 password 외 에 도 remember-me 가 있 습 니 다.이 를 보 여 주 는 이 유 는 로그 인 페이지 를 사용자 정의 할 필요 가 있다 면 Remember Me 라 는 옵션 의 key 를 어떻게 써 야 하 는 지 알려 드 리 려 는 것 입 니 다.
로그 인 에 성공 하면 자동 으로 hello 인터페이스 로 넘 어 갑 니 다.시스템 이 hello 인 터 페 이 스 를 방문 할 때 가지 고 있 는 쿠키:
在这里插入图片描述
여러분,여기 하나 더remember-me가 생 겼 습 니 다.이것 이 바로 여기 서 실현 하 는 핵심 입 니 다.이것remember-me에 대해 제 가 잠시 후에 설명 하 겠 습 니 다.저희 가 먼저 효 과 를 테스트 하 겠 습 니 다.
다음은 브 라 우 저 를 닫 고 브 라 우 저 를 다시 엽 니 다.정상 적 인 상황 에서 브 라 우 저 를 닫 고 다시 엽 니 다.hello 인 터 페 이 스 를 다시 방문 하려 면 다시 로그 인 해 야 합 니 다.그러나 이때 우 리 는 hello 인 터 페 이 스 를 다시 방문 한 결과 다시 로그 인 하지 않 아 도 바로 접근 할 수 있다 는 것 을 알 게 되 었 습 니 다.이것 은 우리 의 Remember Me 설정 이 효력 이 발생 한 다 는 것 을 설명 합 니 다(즉,다음 자동 로그 인 기능 이 효력 이 발생 합 니 다).
원리 분석
이치 대로 말 하면 브 라 우 저 를 닫 고 다시 켜 면 다시 로그 인 해 야 하 는데 지금 은 기다 릴 필요 가 없다.그렇다면 이 기능 은 어떻게 이 루어 진 것 일 까?
먼저 쿠키 에 많이 나 오 는 이것remember-me을 분석 해 보 겠 습 니 다.이 값 은 딱 봐 도 Base 64 코드 를 바 꾼 문자열 입 니 다.저 희 는 인터넷 에 있 는 온라인 도 구 를 사용 하여 디 코딩 할 수 있 고 두 줄 의 코드 를 간단하게 써 서 디 코딩 할 수 있 습 니 다.

@Test
 void contextLoads() {
 String s = new String(
  Base64.getDecoder().decode("eW9sbzoxNjAxNDczNTY2NTA1OjlmMGY5YjBjOTAzYmNjYmU3ZjMwYWM0NjVlZjEzNmQ5"));
 System.out.println("s = " + s);
 }
이 코드 를 실행 하면 출력 결 과 는 다음 과 같 습 니 다.
s = yolo:1601473566505:9f0f9b0c903bccbe7f30ac465ef136d9
이 Base 64 문자열 은 실제:로 구분 되 어 세 부분 으로 나 뉘 어 있 음 을 알 수 있 습 니 다.
(1)첫 번 째 단 계 는 사용자 이름 이 므 로 의심 할 필요 가 없습니다.
(2)두 번 째 단 계 는 시간 스탬프 로 보 입 니 다.우 리 는 온라인 도구 나 자바 코드 를 통 해 분석 한 결과 이것 은 2 주 후의 데이터 입 니 다.
(3)세 번 째 단락 에서 저 는 뜸 을 들 이지 않 겠 습 니 다.이것 은 MD5 해시 함 수 를 사용 하여 계산 한 값 입 니 다.그의 명문 형식 은?username + ":" + tokenExpiryTime + ":" + password + ":" + key마지막 키 는 해시 소금 값 으로 영패 가 수정 되 는 것 을 예방 할 수 있 습 니 다.
쿠키remember-me의 의 미 를 알 게 된 후에 저 희 는 제 로그 인 절 차 를 기억 하 는 것 에 대해 서도 쉽게 알 수 있 습 니 다.
브 라 우 저 를 닫 은 후에 다시 열 면 사용 자 는 hello 인 터 페 이 스 를 방문 합 니 다.이때 쿠키 에 있 는remember-me을 가지 고 서버 에 갑 니 다.서 비 스 를 받 은 후에 사용자 이름과 만 료 시간 을 편리 하 게 계산 한 다음 에 사용자 이름 에 따라 사용자 비밀 번 호 를 조회 한 다음 에 MD5 해시 함 수 를 통 해 해시 값 을 계산 할 수 있 습 니 다.계 산 된 해시 값 과 브 라 우 저가 전달 하 는 해시 값 을 비교 하면 이 토 큰 이 효과 가 있 는 지 확인 할 수 있 습 니 다.
절 차 는 바로 이런 절차 이다.그 다음 에 우 리 는 소스 코드 를 분석 함으로써 이 절차 가 맞 는 지 검증 해 보 자.
3.소스 코드 분석
이어서 우 리 는 소스 코드 를 통 해 우리 가 위 에서 말 한 것 이 맞 는 지 검증 해 보 자.
여기 서 주로 두 가지 측면 에서 소개 합 니 다.하 나 는 remember-me 라 는 영패 가 생 성 되 는 과정 이 고 다른 하 나 는 이 를 분석 하 는 과정 입 니 다.
1.생 성
생 성의 핵심 처리 방법 은 다음 과 같다.TokenBasedRememberMeServices#onLoginSuccess:

@Override
public void onLoginSuccess(HttpServletRequest request, HttpServletResponse response,
 Authentication successfulAuthentication) {
 String username = retrieveUserName(successfulAuthentication);
 String password = retrievePassword(successfulAuthentication);
 if (!StringUtils.hasLength(password)) {
 UserDetails user = getUserDetailsService().loadUserByUsername(username);
 password = user.getPassword();
 }
 int tokenLifetime = calculateLoginLifetime(request, successfulAuthentication);
 long expiryTime = System.currentTimeMillis();
 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);
}
protected String makeTokenSignature(long tokenExpiryTime, String username,
 String password) {
 String data = username + ":" + tokenExpiryTime + ":" + password + ":" + getKey();
 MessageDigest digest;
 digest = MessageDigest.getInstance("MD5");
 return new String(Hex.encode(digest.digest(data.getBytes())));
}
(1)먼저 로그 인 에 성공 한 Authentication 에서 사용자 이름/비밀 번 호 를 추출 합 니 다.
(2)로그 인 에 성공 하면 비밀번호 가 지 워 질 수 있 으 므 로 처음부터 비밀 번 호 를 받 지 못 하면 UserDetails Service 에서 사용 자 를 다시 불 러 오고 비밀 번 호 를 다시 가 져 옵 니 다.
(3)그 다음 에 토 큰 의 유효기간 을 가 져 옵 니 다.토 큰 의 유효기간 은 기본적으로 2 주 입 니 다.
(4)그 다음 에makeTokenSignature방법 으로 해시 값 을 계산 합 니 다.사실은 username,토 큰 유효기간 과 password,key 에 따라 해시 값 을 계산 합 니 다.이 키 를 설정 하지 않 았 다 면 기본 값 은RememberMeConfigurer#getKey방법 에서 설정 되 었 습 니 다.값 은 UUID 문자열 입 니 다.
(5)마지막 으로 사용자 이름,영패 유효기간 및 계 산 된 해시 값 을 쿠키 에 넣 습 니 다.
네 번 째 시 에 대해 서 다시 한 번 말씀 드 리 겠 습 니 다.
키 를 설정 하지 않 았 기 때문에 키 의 기본 값 은 UUID 문자열 입 니 다.서버 에서 다시 시작 하면 키 가 변 합 니 다.그러면 이전에 보 낸 모든remember-me자동 로그 인 토 큰 이 효력 을 잃 기 때문에 이 키 를 지정 할 수 있 습 니 다.지정 한 방식 은 다음 과 같 습 니 다.

@Override
protected void configure(HttpSecurity http) throws Exception {
 http.authorizeRequests()
  .anyRequest().authenticated()
  .and()
  .formLogin()
  .and()
  .rememberMe()
  .key("yolo")
  .and()
  .csrf().disable();
}
키 를 설정 하면 서버 가 다시 시작 하 더 라 도 브 라 우 저 를 열 고 닫 아 도 hello 인터페이스 에 접근 할 수 있 습 니 다.
이것 은 remember-me 영패 가 생 성 되 는 과정 입 니 다.어떻게 onLogin Success 방법 에 이 르 렀 는 지 에 대해 서 는 아 이 디 어 를 살짝 알려 드릴 수 있 습 니 다.
AbstractAuthenticationProcessingFilter#doFilter -> AbstractAuthenticationProcessingFilter#successfulAuthentication -> AbstractRememberMeServices#loginSuccess -> TokenBasedRememberMeServices#onLoginSuccess。
2.해석
그렇다면 사용자 가 브 라 우 저 를 끄 고 열 면/hello인 터 페 이 스 를 다시 방문 합 니 다.이때 인증 절 차 는 어 떻 습 니까?
우리 가 전에 말 했 듯 이 Spring Security 의 일련의 기능 은 모두 하나의 필터 체인 을 통 해 이 루어 진 것 이다.RememberMe이 기능 도 물론 예외 가 아니다.
Spring Security 에서RememberMeAuthenticationFilter관련 일 을 전문 적 으로 하 는 방법 을 제 공 했 습 니 다.RememberMeAuthenticationFilterdoFilter방법 을 살 펴 보 겠 습 니 다.

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
 throws IOException, ServletException {
 HttpServletRequest request = (HttpServletRequest) req;
 HttpServletResponse response = (HttpServletResponse) res;
 if (SecurityContextHolder.getContext().getAuthentication() == null) {
 Authentication rememberMeAuth = rememberMeServices.autoLogin(request,
 response);
 if (rememberMeAuth != null) {
 rememberMeAuth = authenticationManager.authenticate(rememberMeAuth);
 SecurityContextHolder.getContext().setAuthentication(rememberMeAuth);
 onSuccessfulAuthentication(request, response, rememberMeAuth);
 if (this.eventPublisher != null) {
 eventPublisher
 .publishEvent(new InteractiveAuthenticationSuccessEvent(
  SecurityContextHolder.getContext()
  .getAuthentication(), this.getClass()));
 }
 if (successHandler != null) {
 successHandler.onAuthenticationSuccess(request, response,
 rememberMeAuth);
 return;
 }
 }
 chain.doFilter(request, response);
 }
 else {
 chain.doFilter(request, response);
 }
}
이 방법 에서 가장 중요 한 것 은SecurityContextHolder에서 현재 로그 인 사용자 인 스 턴 스 를 얻 을 수 없다 면rememberMeServices.autoLogin논리 로 로그 인 하 는 것 입 니 다.이 방법 을 살 펴 보 겠 습 니 다.

public final Authentication autoLogin(HttpServletRequest request,
 HttpServletResponse response) {
 String rememberMeCookie = extractRememberMeCookie(request);
 if (rememberMeCookie == null) {
 return null;
 }
 logger.debug("Remember-me cookie detected");
 if (rememberMeCookie.length() == 0) {
 logger.debug("Cookie was empty");
 cancelCookie(request, response);
 return null;
 }
 UserDetails user = null;
 try {
 String[] cookieTokens = decodeCookie(rememberMeCookie);
 user = processAutoLoginCookie(cookieTokens, request, response);
 userDetailsChecker.check(user);
 logger.debug("Remember-me cookie accepted");
 return createSuccessfulAuthentication(request, user);
 }
 catch (CookieTheftException cte) {
 
 throw cte;
 }
 cancelCookie(request, response);
 return null;
}
이 를 통 해 알 수 있 듯 이 여 기 는cookie정 보 를 추출 하고cookie정 보 를 디 코딩 한 다음 에processAutoLoginCookie방법 으로 검 사 를 하 는 것 입 니 다.processAutoLoginCookie방법의 코드 는 붙 이지 않 겠 습 니 다.핵심 절 차 는 먼저 사용자 이름과 만 료 시간 을 얻 은 다음 에 사용자 이름 에 따라 사용자 비밀 번 호 를 조회 한 다음 에 MD5 해시 함 수 를 통 해 해시 값 을 계산 하 는 것 입 니 다.받 은 해시 값 과 브 라 우 저가 전달 한 해시 값 을 비교 하면 이 영패 가 효과 가 있 는 지 확인 하고 로그 인 이 효과 가 있 는 지 확인 할 수 있 습 니 다.
3.총화
위의 글 을 보면 우리 가RememberMe기능 을 켜 면 가장 핵심 적 인 것 은cookie에 놓 인 토 큰 이라는 것 을 알 수 있 을 것 이다.이 토 큰 은session의 제한 을 돌파 했다.서버 가 재 부팅 되 더 라 도 브 라 우 저 를 닫 고 다시 열 어도 이 토 큰 이 기한 이 지나 지 않 으 면 데 이 터 를 방문 할 수 있다.
토 큰 을 잃 어 버 리 면 다른 사람 이 이 토 큰 을 들 고 우리 시스템 에 마음대로 로그 인 할 수 있 습 니 다.이것 은 매우 위험한 조작 입 니 다.
그러나 실제 적 으로 이것 은 역설 이다.사용자 체험(적은 로그 인)을 향상 시 키 기 위해 우리 시스템 은 안전 문 제 를 피 할 수 없 지만 우 리 는 기술 을 통 해 안전 위험 을 최소 화 할 수 있다.
SpringBoot 가 SpringSecurity 와 함께 자동 로그 인 기능 을 수행 하 는 코드 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 SpringSecurity 자동 로그 인 내용 은 저희 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 읽 어 주시 기 바 랍 니 다.앞으로 도 많은 응원 부탁드립니다!

좋은 웹페이지 즐겨찾기