나와바리 - Spring Security + JWT를 이용한 로그인 API 구현(2)

Sungmin·2023년 5월 11일
0

JwtService의 코드설명

build.gradle

implementation 'com.auth0:java-jwt:4.2.1'

application-jwt.yml

jwt:
  secretKey: 512비트 이상의 문자

  access:
    expiration: 3600000 # 1시간(60분) (1000L(ms -> s) * 60L(s -> m) * 60L(m -> h))
    header: Authorization

  refresh:
    expiration: 1209600000 #  (1000L(ms -> s) * 60L(s -> m) * 60L(m -> h) * 24L(h -> 하루) * 14(2주))
    header: Authorization-refresh

프로퍼티 주입

    @Value("${jwt.secretKey}")
    private String secretKey;

    @Value("${jwt.access.expiration}")
    private Long accessTokenExpirationPeriod;

    @Value("${jwt.refresh.expiration}")
    private Long refreshTokenExpirationPeriod;

    @Value("${jwt.access.header}")
    private String accessHeader;

    @Value("${jwt.refresh.header}")
    private String refreshHeader;

@Value 를 사용하여 jwt설정파일 application-jwt.yml의 프로퍼티들을 주입

AccessToken & RefreshToken

public String createAccessToken(String email) {
        Date now = new Date();
        return JWT.create() 
                .withSubject(ACCESS_TOKEN_SUBJECT) 
                .withExpiresAt(new Date(now.getTime() + accessTokenExpirationPeriod)) 
                .withClaim(EMAIL_CLAIM, email)
                .sign(Algorithm.HMAC512(secretKey)); 
    }

    public String createRefreshToken() {
            Date now = new Date();
            return JWT.create()
                    .withSubject(REFRESH_TOKEN_SUBJECT)
                    .withExpiresAt(new Date(now.getTime() + refreshTokenExpirationPeriod))
                    .sign(Algorithm.HMAC512(secretKey));
    }

JWT.create로 JWT토큰을 생성하는 빌더를 생성.
.withSubject에는 생성할 값인 AccessToken과 RefreshToken을 각각 넣음.
.withExpiresAt은 설정해 놓은 토큰 만료 시간을 넣음(유효기간 설정)
.withClaim은 설정해놓은 클레임인 email을 넣어서 설정
(클레임은 Payload에 넣어 토큰에 담아 전송.)
.sign는 JWT토큰을 서명함. 이 때 사용되는 알고리즘은 HMAC512로, 이를 위해 secretKey값이 사용. 서명된 토큰은 문자열 형태로 반환.

AccessToken + RefreshToken 헤더에 실어서 보내기

    public void sendAccessAndRefreshToken(HttpServletResponse response, String accessToken, String refreshToken) {
        response.setStatus(HttpServletResponse.SC_OK);

        setAccessTokenHeader(response, accessToken);
        setRefreshTokenHeader(response, refreshToken);
    }

헤더에서 RefreshToken & AccessToken 추출

public Optional<String> extractRefreshToken(HttpServletRequest request) {
        return Optional.ofNullable(request.getHeader(refreshHeader))
                .filter(refreshToken -> refreshToken.startsWith(BEARER))
                .map(refreshToken -> refreshToken.replace(BEARER, ""));
    }


    public Optional<String> extractAccessToken(HttpServletRequest request) {
        return Optional.ofNullable(request.getHeader(accessHeader))
                .filter(refreshToken -> refreshToken.startsWith(BEARER))
                .map(refreshToken -> refreshToken.replace(BEARER, ""));
    }

Bearer~~에서 Bearer를 제외하고 순수 토큰만 가져오기 위해 replace로 Bearer삭제

이메일 추출

    public Optional<String> extractEmail(String accessToken) {
        try {
            return Optional.ofNullable(JWT.require(Algorithm.HMAC512(secretKey))
                    .build() 
                    .verify(accessToken) 
                    .getClaim(EMAIL_CLAIM) 
                    .asString());
        } catch (Exception e) {
            log.error("액세스 토큰이 유효하지 않습니다.");
            return Optional.empty();
        }
    }

토큰 유효성 검사하는데 사용할 알고리즘이 있는 JWT verifier builder 반환.
반환된 빌더로 JWT verifier 생성.
accessToken을 검증하고 유효하지않으면 예외 발생
getClaim으로 (Email)가져오기

헤더 설정

    public void setAccessTokenHeader(HttpServletResponse response, String accessToken) {
        response.setHeader(accessHeader, accessToken);
    }


    public void setRefreshTokenHeader(HttpServletResponse response, String refreshToken) {
        response.setHeader(refreshHeader, refreshToken);
    }

RefreshToken 업데이트

    public void updateRefreshToken(String email, String refreshToken) {
        memberRepository.findByEmail(email)
                .ifPresentOrElse(
                        user -> user.updateRefreshToken(refreshToken),
                        () -> new Exception("일치하는 회원이 없습니다.")
                );
    }

토큰 유효성 검사

    public boolean isTokenValid(String token) {
        try {
            JWT.require(Algorithm.HMAC512(secretKey)).build().verify(token);
            return true;
        } catch (Exception e) {
            log.error("유효하지 않은 토큰입니다. {}", e.getMessage());
            return false;
        }
    }
profile
Let's Coding

0개의 댓글