Remember-Me란?

Remember-Me는 사용자가 브라우저를 닫았다가 다시 열어도 로그인 상태를 유지하는 기능입니다. 쿠키를 사용하여 구현되며, Spring Security는 두 가지 구현 방식을 제공합니다. 

 

  • Simple Hash-Based Token
  • Persistent Token (Database)

두가지 다 알아보도록 합시다.

 

 

 

RememberMe 처리과정

구현하기 전에 어떤 과정을 통해 쿠키로 만들어지는지 짚고 넘어가겠습니다.

  1. 로그인 시 rememberMe 체크박스를 선택하고 로그인
  2. 토큰생성 : base64(username + ":" + expirationTime + ":" + md5Hex(username + ":" + expirationTime + ":" + password + ":" + key))
  3. 토큰 저장
    • Simple Hash-Based Token : 쿠키 생성
    • Persistent Token : 쿠키생성, DB 저장ㅋ
  4. 사용자 접근 시 토큰 검증
    • 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의 장점

  1. 토큰이 데이터베이스에 저장되므로 더 안전
  2. 토큰 탈취 시도를 감지 가능
  3. 사용자별 로그인 세션 관리 가능
  4. 특정 사용자의 remember-me 토큰만 선택적으로 만료 가능

Persistent Token 주의사항:

  1. 반드시 HTTPS 사용 권장
  2. 토큰 유효기간을 적절히 설정
  3. 중요한 작업 시에는 재인증 요구
  4. 정기적으로 만료된 토큰 정리 필요

Persistent Token을 사용할때

service.setUseSecureCookie(true);

로 설정하는게 좋습니다.

 

 

  • true로 설정 시
    • HTTPS 연결에서만 쿠키 전송
    • HTTP 연결에서는 쿠키가 전송되지 않음
    • 중간자 공격(Man-in-the-Middle) 방지
    • 쿠키 탈취 위험 감소
  • false로 설정 시
    • HTTP, HTTPS 모두에서 쿠키 전송
    • 개발 환경에서 주로 사용
    • 보안상 취약할 수 있음

개발환경에서는 false, 서비스환경에서는 true로 설정해줘야합니다.

 

+ Recent posts