Remember-Me란?
Remember-Me는 사용자가 브라우저를 닫았다가 다시 열어도 로그인 상태를 유지하는 기능입니다. 쿠키를 사용하여 구현되며, Spring Security는 두 가지 구현 방식을 제공합니다.
- Simple Hash-Based Token
- Persistent Token (Database)
두가지 다 알아보도록 합시다.
RememberMe 처리과정
구현하기 전에 어떤 과정을 통해 쿠키로 만들어지는지 짚고 넘어가겠습니다.
- 로그인 시 rememberMe 체크박스를 선택하고 로그인
- 토큰생성 : base64(username + ":" + expirationTime + ":" + md5Hex(username + ":" + expirationTime + ":" + password + ":" + key))
- 토큰 저장
- Simple Hash-Based Token : 쿠키 생성
- Persistent Token : 쿠키생성, DB 저장ㅋ
- 사용자 접근 시 토큰 검증
- Simple Hash-Based Token : 쿠키값이 유효한 경우 자동로그인
- Persistent Token : DB 에 저장된 값과 비교하고 유효한 경우 자동로그인
Simple Hash-Based Token
<form th:action="@{/join}" th:method="post">
<input type="text" name="username">
<input type="password" name="password">
<label>
<input type="checkbox" name="remember-me" /> Remember Me
</label>
<input type="submit" value="로그인">
</form>
먼저 jonForm.html에 remember-me 체크박스를 하나 추가해줍시다.
그다음 RememberMeConfig 를 하나 만듭니다.
@Configuration
@RequiredArgsConstructor
public class RememberMeConfig {
private static final String KEY = "MY_KEY";
@Bean
RememberMeServices rememberMeServices(UserDetailsService userDetailsService) {
TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices(KEY, userDetailsService);
rememberMe.setAlwaysRemember(false);
rememberMe.setTokenValiditySeconds(60 * 60 * 24);
return rememberMe;
}
}
KEY는 비밀키 역할을 합니다. TokenBasedRememberMeServices에는 인코딩 알고리즘과 매칭 알고리즘이 존재합니다.
인코딩 알고리즘 기본값은 SHA-256, 매칭 알고리즘은 MD5입니다. 알고리즘은 설정을 통해 수정할 수 있습니다.
UserDetailsService는 저번 글에서 Bean으로 등록해놓았습니다.
@Bean
RememberMeServices rememberMeServices(UserDetailsService userDetailsService) {
RememberMeTokenAlgorithm sha256 = RememberMeTokenAlgorithm.SHA256;
TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices(KEY, userDetailsService, sha256);
rememberMe.setMatchingAlgorithm(RememberMeTokenAlgorithm.MD5);
rememberMe.setAlwaysRemember(false);
rememberMe.setTokenValiditySeconds(60 * 60 * 24);
return rememberMe;
}
마지막으로 SecurityConfig로 돌아와
http.rememberMe(remember -> remember
.rememberMeServices(rememberMeServices)
);
이렇게 설정해줍시다.
설정이 끝났습니다. 이제 로그인할때 rememberMe를 체크하고 로그인하면 쿠키가 살아있는동안 자동로그인이 되는것을 확인할 수 있습니다.
rememberMe 체크 후 로그인 -> 브라우저 종료 -> /user 로 바로 접속
Persistent Token
다음은 DB에 쿠키를 저장하는 방식으로 구현해보겠습니다.
build.gradle
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'
application.properties
# DataSource 설정
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
# JPA 설정
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
@Entity
public class PersistentLogins {
@Id
private String series;
private String username;
private String token;
private LocalDateTime lastUsed;
}
이렇게 추가해주도록 합시다.
PersistentLogins 객체는 공식문서에 적힌대로 작성했습니다. 실제로도 이렇게 해야합니다.
username: 사용자 이름
series: 쿠키의 series 값과 매칭됨
token: 쿠키의 token 값과 매칭됨
last_used: 마지막 사용 시간
Remember-Me Authentication :: Spring Security
Remember-me or persistent-login authentication refers to web sites being able to remember the identity of a principal between sessions. This is typically accomplished by sending a cookie to the browser, with the cookie being detected during future sessions
docs.spring.io
@Bean
RememberMeServices rememberMeServices(UserDetailsService userDetailsService) {
PersistentTokenBasedRememberMeServices service = new PersistentTokenBasedRememberMeServices(KEY, userDetailsService, new CustomInMemoryTokenRepository());
service.setAlwaysRemember(false);
service.setTokenValiditySeconds(2592000);
return service;
}
RememberMeServices에 구현체를 TokenBasedRememberMeServices 에서 PersistentTokenBasedRememberMeServices로 변경해줍니다. PersistentTokenBasedRememberMeServices에는 PersistentTokenRepository가 들어가야하는데 그 구현체는
InMemoryTokenRepositoryImpl과 JdbcTokenRepositoryImpl이 있습니다.
저는 테스트하기 위해 InMemoryRepositoryImpl 을 구현할거고, 로그도 확인하기 위해 CustomInMemoryRepository 를 따로 구현했습니다. 그냥 InMemoryRepositoryImpl 넣으셔도 무방합니다.
public class CustomInMemoryTokenRepository extends InMemoryTokenRepositoryImpl {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void createNewToken(PersistentRememberMeToken token) {
super.createNewToken(token);
logger.info("New token created: {}", token.getUsername());
}
@Override
public void updateToken(String series, String tokenValue, Date lastUsed) {
super.updateToken(series, tokenValue, lastUsed);
logger.info("Token updated - series: {}, lastUsed: {}", series, lastUsed);
}
}
이제 로그인해보면
정상적으로 토큰이 생성되었습니다. 이제 브라우저를 닫고 다시 접속해보면
잘 되는걸 볼 수 있습니다.
비교
따로 장단점을 적지않더라도 Persistent Token이 더 안전하다고 느끼실겁니다.
Persistent Token의 장점
- 토큰이 데이터베이스에 저장되므로 더 안전
- 토큰 탈취 시도를 감지 가능
- 사용자별 로그인 세션 관리 가능
- 특정 사용자의 remember-me 토큰만 선택적으로 만료 가능
Persistent Token 주의사항:
- 반드시 HTTPS 사용 권장
- 토큰 유효기간을 적절히 설정
- 중요한 작업 시에는 재인증 요구
- 정기적으로 만료된 토큰 정리 필요
Persistent Token을 사용할때
service.setUseSecureCookie(true);
로 설정하는게 좋습니다.
- true로 설정 시
- HTTPS 연결에서만 쿠키 전송
- HTTP 연결에서는 쿠키가 전송되지 않음
- 중간자 공격(Man-in-the-Middle) 방지
- 쿠키 탈취 위험 감소
- false로 설정 시
- HTTP, HTTPS 모두에서 쿠키 전송
- 개발 환경에서 주로 사용
- 보안상 취약할 수 있음
개발환경에서는 false, 서비스환경에서는 true로 설정해줘야합니다.
'FrameWork > Spring' 카테고리의 다른 글
[Spring Security] 스프링 시큐리티 SessionManagement (5) (0) | 2024.12.16 |
---|---|
[Spring Security] 스프링 시큐리티 Anonymous과 ExceptionHandling (4) (0) | 2024.12.15 |
[Spring Security] 스프링 시큐리티 시작하기 (로그인, 로그아웃과 권한) (2) (0) | 2024.12.12 |
[Spring Security] 스프링 시큐리티 이해하기 (1) (0) | 2024.12.10 |
[Spring Boot] @Scheduled 스케줄 적용 (0) | 2024.11.11 |