Spring/Spring Security

[Spring Security] Remember-Me Authentication

공대키메라 2022. 7. 23. 22:48

지난 시간에는 Spring Security의 Session Management에 대해 알아보았다.

(지난 내용이 궁금하면 여기 클릭!)

 

이번 시간에는 Remember-me에 대해 공부하려고 한다. 

 

참고한 사이트는 다음과 같다. 

 

출처:

인프런 정수원 강사님의 코어 스프링 시큐리티(https://www.inflearn.com/course/%EC%BD%94%EC%96%B4-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0)

https://docs.spring.io/spring-security/reference/servlet/authentication/rememberme.html

https://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/web/authentication/RememberMeServices.html

 

 

1. Remember-Me Authentication 구조

공식문서를 읽는 것이 원래의 의도나 깊은 설명을 듣기에는 좋지만, 무언가 그림을 그렇게 많이 제공하지는 않아서 이해하는데 조금 불편함이 있는 섹션들이 많았다.

 

그래서 필자가 이 섹션을 이해하기 위해 보면 좋을 그림을 가져왔다.

 

출처 : 인프런 코어-스프링-시큐리티 (정수원 강사)

 

위 그림을 보고 대략 이런 흐름으로 흐른다 생각하고 공식 문서 설명을 보면 좀 더 이해가 잘 되는 것 같다.

 

Remember-Me Authentication 

 

2. 개요(overview)

Remember-me 혹은 persistent login 인증은 세션들 사이에 principal의 신원을 기억하기 위한 웹사이트들에서 언급된다. 

 

이것은 전형적으로 브라우저에 쿠키를 보냄으로써 이뤄진다. 미래 세션동안 감지된 쿠키를 가지고, 그리고 일어날 자동화된 로그인을 발생시키면서 말이다. 

 

Spring Security는 일어날 이러한 작동을 위한 필수적인 hook들을 제공한다. 그리고 구체적인 remember-me 구현체를 가지고 있다. 

 

하나는 cookie 기반 토큰의 보안을 보존하기 위해 hashing을 사용하는 것이고, 다른 하나는 생성된 토큰을 저장하기 위해   database를 사용하거나 다른 영구 저장 메커니즘을 이용하는 것이다. 

 

두개의 구현체틀이 모두 UserDetailService 를 필요로 한다는 것을 유의하라. 

 

만약 UserDetailService를 사용하지 않는 authentication provider를 사용중이라면(LDAP 같은거 말이다), application context에서 UserDetailsService 빈을 가지지 않는다면 작동하지 않을 것이다. 

 

3. Simple Hash-Based Token Approach

이 접근은 유용한 remember-me 전략을 이루기 위해 hashing을 사용한다. 

 

본질적으로 쿠키는 성공적인 상호작언 인증을 통해 browser에서 보내진다. 

 

 base64(username + ":" + expirationTime + ":" +
md5Hex(username + ":" + expirationTime + ":" password + ":" + key))

username: As identifiable to the UserDetailsService
password: That matches the one in the retrieved UserDetails
expirationTime: The date and time when the remember-me token expires, expressed in milliseconds
key: A private key to prevent modification of the remember-me token

 

4. Persistent Token Approach

이 접근은 몇몇 minor한 수정을 가진 http://jaspan.com/improved_persistent_login_cookie_best_practice 에 기반을 둔다.

 

이것을 사용하기 위해, datasource reference를 공급해야 한다.

 

5. Remember-Me Interfaces and Implementations

Remember-me는 UsernamePasswordAuthenticationFilter와 함께 사용된다. 그리고 AbstractAuthenticationProcessingFilter의 superclass에서 hooks를 통해 구현된다. 

 

또한, BasicAuthenticationFilter 내부에서도 사용된다. 

 

Hooks들은 적절한 시간에 구체적인 RememberMeService들을 호출할 것이다. 

 

Authentication autoLogin(HttpServletRequest request, HttpServletResponse response);

void loginFail(HttpServletRequest request, HttpServletResponse response);

void loginSuccess(HttpServletRequest request, HttpServletResponse response,
	Authentication successfulAuthentication);

잠시 RememberMeService에 대한 설명이 나왔는데, Spring Security 공식 사이트에서는 이에 대해 설명해주지 않고 있었다. 그래서 docs 사이트를 찾아보았다.

 

RememberMeService에 대해 docs에서 다음과 같이 설명한다.

 

"remember-me service를 제공할 수 있게 해주는 구현체"

 

Spring Security filter( AbstractAuthenticationProcessingFilter와 RememberMeAuthenticationFilter)가 이 인터페이스의 구현체에 의해 제공된 메소드를 호출할 것이다. 

 

AbstractAuthenticaionProcessingFilter를 우선 들여다 보았다.

 

AbstractAuthenticaionProcessingFilter.class 변수 선언부

 

 

NullRememberMeService.class 캡쳐

 

메소드 설명

autoLogin()

=> 이 메소드는 SecurityContextHolder가 Authentication 객체를 포함하지 않거나 Spring Security가 구현체에게 remember-me 기능을 사용해서 요청 인증을 제공하려고 할 때 마다 호출된다. 

loginFail()

=> 이 메소드는 상호작용하는 인증 시도가 생길 때 마다 호출되지만 사용자에 의해 공급된 credentials는 피하고 그렇지 않으면 무효화한다. 구현체들은 HttpServletRequest에서 나타내진 어떤 혹은 모든 remember-me 토큰들을 무효화한다. 

loginSuccess()

=> 이 메소드는 상호작용하는 인증 시도가 성공적일때 호출된다. 이 구현체는 아마 HttpServletResponse에 있는 remember-me 토큰을 자동적으로 세팅할 것이다. (추천되지 않더라도!)

출처 : https://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/web/authentication/RememberMeServices.html

 

그렇다고 한다...

 

그래서 NullRememberService라는 것 자체가 autoLogin 자체를 안하게 하려고, 즉 autoLogin 을 막기 위해 Null로 반환해줘서 아마 NullRememberService라고 명명한거 같다. (맞나요... 몽둥이 존슨? ㅎㅎ)

 

그래서 다시 AbstractAuthenticationProcessingFilter로 돌아가보면 인증 성공, 실패시에(successfulAuthentication, unsuccessfulAuthentication) remember-me를 호출해서 사용중이다.

 

AbstractAuthenticationProcessingFilter.class

 


그 메소드들이 무엇을 하는지에 대해 더 큰 토론을 위해서 Javadoc을 참고해라.

(뭔가 please 며 명령문을 사용하니까...뉘앙스가... 굉장히... 이에 대해서 잘 알면 많이 도움이 될 것이다!)

 

비록 이 글에서 AbstractAuthenticationProcessingFilter가 loginFail()과 loginSuccess()만 호출하더라고 말이다.

(위에서 보니 그러고 있었다! 계속 읽어보자)

 

autoLogin()메소드는 SecurityContextHolder가 Authentication을 포함하지 않을 때마다 RememberMeAuthenticationFilter에서 호출된다.

 

이 인터페이스는 그러므로 미리 rememeber-me 구현을 인증 관련된 이벤트들의 충분한 알림과 함께 제공된다. 그리고 후보자의 웹 요청이 쿠키를 포함하고 기억하기를 바랄 때 마다 구현체를 위임한다. 

 

이 디자인은 모든 수의 remember-me 구현 전략을 허용한다. 

 

우리는 위에서 Spring Security에서 두가지의 구현체를 제공하는 것을 봐왔다. 차례대로 이것들을 볼 것이다. 

(이제 RememberMeService에 대한 구현체들을 알아볼 것이다!)

 

6. TokenBasedRememberMeServices

이 구현체는 Simple Hash-Based Token Approach에 묘사된 간단한 접근을 지원한다. 

 

TokenBasedRememberMeServices는 RememberMeAuthenticationToken을 생성한다.  

 

키는 이것의 authentication provider와 TokenBasedRememberMeServices들 사이에 공유된다. 

 

이에 더해서, TokenBasedRememberMeSerivces는 서명 비교 목적을 위해 username과 password를 가져올 수 있는 UserDetailService를 필요로 한다.

 

그리고 올바른 GrantedAuthrity를 포함하기 위해 RemembermeAuthenticationToken을 생성한다.

 

몇몇 로그아웃 요청은 사용자가 요청한다면 쿠키를 무효화하는 어플리케이션에서 제공되어야만 한다. 

 

 TokenBasedRememberMeServices는 또한 Spring Security의 LogoutHandler를 구현한다. 그래서 자동적으로 cookie를 지우는 LogoutFilter에서 사용될 수 있다. 

 

정리를 하자면, RememberMeService interface의 구현체인 TokenBasedRememberMeSerivce가 있고, 

Simple Hashed-Based Token Approach 방식으로 구현되어 있다. RememberMeAuthenticationToken을 내부적으로 생성하고, LogoutFilter내에서 사용이 가능하다.

 

당신의 RememberMeSerivce 구현체를 UsernamePasswordAuthenticationFilter.setRememberMeServices() property를 추가하고, RememberMeAuthenticationProvider를 AuthenticaionManager.setProviders() 리스트에 포함하고, RememberMeAuthenticationfilter를 FilterChainProxy안에 추가하는것을 잊지 마세요! (전형적으로 즉각적으로 UsernamePasswordAuthenticationfiler 뒤에 말이다.)

 

여기서 말하고자 하는 바는, 내 생각으로는 우리가 직접 구현한 구현체를 위에서 언급된 곳에 추가를 잘 해줘야 우리가 만든 RememberMeSerivce의 구현체가 잘 작동한다는 것을 당부한 말인 것 같다.

 

7. PersistentTokenBasedRememberMeServices

이 클래스는 TokenBasedRememberMeService와 같은 방식으로 사용된다. 하지만, 추가적으로 토큰을 저장하기 위해 PersistentTokenRepository를 설정할 필요가 있다. 

 

여기 두 개의 표준 구현체가 있다.

 

1. InMemoryTokenRepositoryImpl - 테스팅 용으로 의도됨

 

2. JdbcTokenRepositoryImpl - 데이터베이스에 토큰을 저장함.

 


이번 시간에는 Spring Security의 Remember-me에 대해서 알아 보았다.