spring security jwt 사용하기 ver.2(access token, refresh token)

박도영·2022년 3월 14일
2

security+jwt+redis

목록 보기
2/3
post-thumbnail

전편

전에 코드가 filter에서 transaction이 설정되어 있다는 것이 마음에 들지 않고 코드나 메소드의 역할( reissueRefreshToken 이 토큰 검증과 토큰 재발행의 다중 역할 담당) 등 우아하지 않다고 생각되어 코드 리펙토링을 했다.

// service layer
 @Transactional
    public TokenResponseDto signIn(String userId, String pw) {
        // uesrId 확인
        UserDetails userDetails = myUserDetailsService.loadUserByUsername(userId);

        // pw 확인
        if(!passwordEncoder.matches(pw, userDetails.getPassword())){
            throw new BadCredentialsException(userDetails.getUsername() + "Invalid password");
        }

        Authentication authentication =  new UsernamePasswordAuthenticationToken(
                userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());

        // refresh token 발급 및 저장
        String refreshToken = jwtTokenProvider.createRefreshToken(authentication);
        RefreshToken token = RefreshToken.createToken(userId, refreshToken);

        // 기존 토큰이 있으면 수전, 없으면 생성
        refreshTokenRepository.findByUserId(userId)
                        .ifPresentOrElse(
                                (tokenEntity)->tokenEntity.changeToken(refreshToken),
                                ()->refreshTokenRepository.save(RefreshToken.createToken(userId, refreshToken))
                        );

        // accessToken과 refreshToken 리턴
        return TokenResponseDto.builder()
                .accessToken("Bearer-"+jwtTokenProvider.createAccessToken(authentication))
                .refreshToken("Bearer-"+refreshToken)
                .build();
    }

기존에 filter 에서 담당하던 jwt refresh token을 이제는 서비스 레이어에서 로그인 할 때 생성 밑 저장해준다. 마찮가지로 refresh token을 검사하고 access token 밑 refresh token을 재발급 해주는 기능도 서비스 레이어로 옮겼다.

@Transactional
    public TokenResponseDto reissueAccessToken(String token) {

        //token 앞에 "Bearer-" 제거
        String resolveToken = resolveToken(token);

        //토큰 검증 메서드
        //실패시 jwtTokenProvider.validateToken(resolveToken) 에서 exception을 리턴함
        jwtTokenProvider.validateToken(resolveToken);

        Authentication authentication = jwtTokenProvider.getAuthentication(resolveToken);
        // 디비에 있는게 맞는지 확인
        RefreshRedisToken refreshRedisToken = refreshRedisRepository.findById(authentication.getName()).get();

        // 토큰이 같은지 확인
        if(!resolveToken.equals(refreshRedisToken.getToken())){
            throw new RuntimeException("not equals refresh token");
        }

        // 재발행해서 저장
        String newToken = jwtTokenProvider.createRefreshToken(authentication);
        RefreshRedisToken newRedisToken = RefreshRedisToken.createToken(authentication.getName(), newToken);
        refreshRedisRepository.save(newRedisToken);

        // accessToken과 refreshToken 모두 재발행
        return TokenResponseDto.builder()
                .accessToken("Bearer-"+jwtTokenProvider.createAccessToken(authentication))
                .refreshToken("Bearer-"+newToken)
                .build();
    }

발급 받은 access token을 헤더에 Authentication에 넣으면 무사히 "/auth/**" 에 접근 가능합니다.

만약 access token이 만료되었을 경우에 "/api/v1/accessToken"경로로 refresh token을 통해 accesstoken을 재발급 받을 수 있습니다.

h2 db를 사용해서 상당히 빠른모습...
다음에는 redis를 사용해보겠습니다.

전체 코드

참고자료 :
https://kukekyakya.tistory.com/entry/Spring-boot-access-token-refresh-token-발급받기jwt

profile
좋은 개발자란?

0개의 댓글