SOLUX-완숙이

spring security+jwt 회원가입, 로그인 #4

leeeehhjj 2022. 1. 23. 15:10

2022.01.17 - [SOLUX-완숙이] - spring security+jwt 회원가입, 로그인 #3

 

spring security+jwt 회원가입, 로그인 #3

2022.01.12 - [SOLUX-완숙이] - spring security+jwt 회원가입, 로그인 #2 spring security+jwt 회원가입, 로그인 #2 2022.01.12 - [SOLUX-완숙이] - spring security+jwt 회원가입, 로그인 #1 spring security+jw..

leeeehhjj.tistory.com


Access token에는 사용자의 정보가 들어있기 때문에 보안에 취약하다. 따라서 access token의 유효 기간은 짧게 하고, 사용자의 정보가 들어있지 않은 refresh token이라는 것을 만들어 만료된 access token을 재발급 해준다.

이때 refresh token을 저장해두어야 하는데 여기서는 db에 저장하도록 했다.

 

과정 : 

사용자의 AccessToken이 만료되면 프론트에서 서버로 access token과 refresh token을 모두 보내고, 서버는 db에 저장해두었던 refresh token과 프론트에서 받은 refresh token이 일치하는지 확인한 후 일치하면 새로운 access token과 refresh token을 생성하여 사용자에게 전달한다.

 

1. 먼저 RefreshToken 클래스를 만들어 Member 클래스와 1:1 단방향 연결을 해준다.

@Entity
@NoArgsConstructor
@Getter
public class RefreshToken {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String refreshTokenInfo;

    @Builder
    public RefreshToken(String refreshTokenInfo) {
        this.refreshTokenInfo = refreshTokenInfo;
    }
}

Member 클래스

...

@OneToOne
    @JoinColumn(name = "refreshToken_id")
    private RefreshToken refreshToken;

    public void update(RefreshToken refreshToken) {
        this.refreshToken = refreshToken;
    }
    
...

member 클래스를 통해 refresh token을 조회할 수 있도록 연관 관계를 만들어줬고, update 메소드를 통해 refresh token과 멤버를 연관시켜줄 수 있도록 했다.

 

2. MemberController

@PostMapping("/reissue")
    public TokenInfoResponseDto reissue(@RequestBody ReissueRequestDto requestDto) {return memberService.reissue(requestDto);}

ReissueRequestDto

@Getter
@NoArgsConstructor
public class ReissueRequestDto {
    private String accessToken;
    private String refreshToken;

    @Builder
    public ReissueRequestDto(String accessToken, String refreshToken) {
        this.accessToken = accessToken;
        this.refreshToken = refreshToken;
    }
}

 

3. Member Service

 @Transactional
    public TokenInfoResponseDto login(LoginDto loginDto) {
        Member member = memberRepository.findByLoginId(loginDto.getLoginId())
                .orElseThrow(() -> new IllegalArgumentException("해당 유저 없음"));

        UsernamePasswordAuthenticationToken authenticationToken = loginDto.toAuthentication();

        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);

        TokenInfoResponseDto tokenInfo = jwtTokenProvider.generateToken(authentication);

        RefreshToken refreshToken = RefreshToken.builder()
                .refreshTokenInfo(tokenInfo.getRefreshToken()).build();
        refreshTokenRepository.save(refreshToken);

        member.update(refreshToken);

        return tokenInfo;
    }

로그인 시 refreshtoken을 저장하고 멤버와 refreshtoken을 연결시켜준다.

@Transactional
    public TokenInfoResponseDto reissue(ReissueRequestDto requestDto) {
        if (!jwtTokenProvider.validateToken(requestDto.getRefreshToken()))
            throw new IllegalArgumentException("refreshToken 검증 실패");

        String accessToken = requestDto.getAccessToken();
        Authentication authentication = jwtTokenProvider.getAuthentication(accessToken);

        Member member = memberRepository.findByLoginId(authentication.getName())
                .orElseThrow(()->new IllegalArgumentException("해당 유저가 없습니다"));
        String refreshToken = member.getRefreshToken().getRefreshTokenInfo();

        if (!refreshToken.equals(requestDto.getRefreshToken())) {
            throw new IllegalArgumentException("잘못된 refreshToken");
        }

        TokenInfoResponseDto responseDto = jwtTokenProvider.generateToken(authentication);
        RefreshToken token = RefreshToken.builder()
                .refreshTokenInfo(responseDto.getRefreshToken()).build();
        refreshTokenRepository.save(token);

        member.update(token);

        return responseDto;
    }

프론트에서 보내온 access token에서 authentication을 가져오고 getName을 통해 사용자의 login id를 가져와서 해당 멤버의 객체를 얻는다. 그 후 그 멤버 객체에 저장되어 있는 refresh token과 프론트에서 받아온 refresh token을 비교하여 같을 경우 새로운 access token과 refresh token을 생성하여 프론트에게 전달한다.