spring boot jwt 로그아웃

육희영·2023년 3월 15일

회고록

목록 보기
13/24

jpa, jwt, spring security, redis로 구현한 로그인,회원가입을 가지고 로그아웃 기능을 추가해야 하는 상황이었다.

그냥 spring security 로그아웃 처리보다 여러가지로 복잡한 것이 많고 처리할 게 많다는 글들이 더러 있어서 하기도 전에 사실 겁먹었다.

내가 구현한 jwt 로그아웃 방식은 서버에서 쿠키 유효시간을 0으로 줘서 쿠키를 만료시키고
클라이언트에서 만료된 쿠키를 삭제하는 것이다.

먼저 넘길 페이지 설정을 SecurityConfig.java에서 해준다.

 .antMatchers("/user/logout").permitAll()

CookieUtil.java에 쿠키 유효시간을 0으로 줘서 만료하는 로직을 넣는다.

  public void deleteCookie(HttpServletRequest request, HttpServletResponse response, String cookieName) {
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(cookieName)) {
                    cookie.setValue(null);
                    cookie.setMaxAge(0);
                    cookie.setPath("/");
                    response.addCookie(cookie);
                }
            }
        }
    }

MemberController.java에서 CookieUtil에 있는 deleteCookie를 가져와 토큰을 처리해주고 redisUtil을 사용하여 refreshJwt데이터를 삭제한다.

 @PostMapping("/logout")
    public Response logout(HttpServletRequest request, HttpServletResponse response) {
        try {
            Cookie refreshToken = cookieUtil.getCookie(request, JwtUtil.REFRESH_TOKEN_NAME);
            String refreshJwt = refreshToken.getValue();
            redisUtil.deleteData(refreshJwt);
            cookieUtil.deleteCookie(request, response, JwtUtil.REFRESH_TOKEN_NAME);
            cookieUtil.deleteCookie(request, response, JwtUtil.ACCESS_TOKEN_NAME);
            return new Response("success", "로그아웃 되었습니다.", null);
        } catch (Exception e) {
            return new Response("error", "로그아웃에 실패했습니다.", e.getMessage());
        }
    }

JwtUtil.java에서도 token 관련 처리를 해준다.

@Component
public class JwtUtil {

    public final static long TOKEN_VALIDATION_SECOND = 1000L * 10;
    public final static long REFRESH_TOKEN_VALIDATION_SECOND = 1000L * 60 * 24 * 2;

    final static public String ACCESS_TOKEN_NAME = "accessToken";
    final static public String REFRESH_TOKEN_NAME = "refreshToken";

    @Value("${spring.jwt.secret}")
    private String SECRET_KEY;

    private Key getSigningKey(String secretKey) {
        byte[] keyBytes = secretKey.getBytes(StandardCharsets.UTF_8);
        return Keys.hmacShaKeyFor(keyBytes);
    }

    public Claims extractAllClaims(String token) throws ExpiredJwtException {
        return Jwts.parserBuilder()
                .setSigningKey(getSigningKey(SECRET_KEY))
                .build()
                .parseClaimsJws(token)
                .getBody();
    }

    public String getUsername(String token) {
        return extractAllClaims(token).get("username", String.class);
    }

    public Boolean isTokenExpired(String token) {
        final Date expiration = extractAllClaims(token).getExpiration();
        return expiration.before(new Date());
    }

    public String generateToken(Member member) {
        return doGenerateToken(member.getUsername(), TOKEN_VALIDATION_SECOND);
    }

    public String generateRefreshToken(Member member) {
        return doGenerateToken(member.getUsername(), REFRESH_TOKEN_VALIDATION_SECOND);
    }
    public String doGenerateToken(String username, long expireTime) {
        Claims claims = Jwts.claims();
        claims.put("username", username);

        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        long expMillis = nowMillis + expireTime;
        Date exp = new Date(expMillis);

        String jwt = Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(now)
                .setExpiration(exp)
                .signWith(getSigningKey(SECRET_KEY), SignatureAlgorithm.HS256)
                .compact();

        return jwt;
    }
    public Boolean validateToken(String token, UserDetails userDetails) {
        final String username = getUsername(token);

        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }
    public Boolean isTokenExpired(String token, long expireTime) {
        final Date expiration = extractAllClaims(token).getExpiration();
        final long expirationTime = expiration.getTime();
        final long now = System.currentTimeMillis();
        return now > expirationTime + expireTime;
    }
}

0개의 댓글