Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

-6차 세미나 과제 #6

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

-6차 세미나 과제 #6

wants to merge 1 commit into from

Conversation

eunseo5343
Copy link
Contributor

@eunseo5343 eunseo5343 commented Jun 1, 2024

이 주의 과제

로그인을 진행할 때 AccessToken과 Refresh Token을 함께 반환하는 로직
Redis를 활용해 Refresh Token으로 Access Token을 재발급 받는 로직을 구현

+깃이 단단히 꼬여버려서 풀리케스트 전에 과제가 머지 되었습니다...........

요구사항 분석

  1. JwtTokenProvider - RefreshToken 발급 로직 추가
    구현한 내용: 사용자 인증 정보를 기반으로 RefreshToken을 발급
    만료 시간(14일)을 갖게 설정
private static final Long REFRESH_TOKEN_EXPIRATION_TIME = 24 * 60 * 60 * 1000L * 14;


public String issueRefreshToken(final Authentication authentication) {
    return generateToken(authentication, REFRESH_TOKEN_EXPIRATION_TIME);
}
  1. Token - Redis 저장을 위한 도메인 클래스
    구현한 내용: RefreshToken을 Redis에 저장하기 위한 도메인 객체@RedisHash를 사용하여 Redis 내에 저장되며, 자동으로 만료 시간이 설정됨
@RedisHash(value="", timeToLive= 60 * 60 * 24 * 1000L * 14)
@AllArgsConstructor
@Getter
@Builder

public class Token {
    @Id
    private Long id;

    @Indexed
    private String refreshToken;

    public static Token of(
            final Long id,
            final String refreshToken
    ){
        return Token.builder()
                .id(id)
                .refreshToken(refreshToken)
                .build();
    }
}
  1. RedisTokenRepository - CRUD 리포지토리
    구현한 내용: Redis에 저장된 Token 객체를 관리하기 위한 리포지토리 인터페이스
    Spring Data의 CrudRepository를 상속받아 기본적인 CRUD 연산 및 ID로의 조회 기능을 제공.
public interface RedisTokenRepository extends CrudRepository<Token, String> {
    Optional<Token> findByRefreshToken(final String refreshToken);
    Optional<Token> findById(final Long id);
}
  1. UserJoinResponse - 사용자 인증 응답 DTO
    구현한 내용: 로그인 과정에서 생성된 AccessToken 및 RefreshToken을 클라이언트에 반환하기 위한 데이터 전송 객체(DTO)
    이 객체를 통해 사용자 인증 정보와 함께 토큰들이 전달됨
package org.sopt.spring.common.dto;

public record UserJoinResponse(
        String accessToken,
        String refreshToken,
        String userId
) {

    public static UserJoinResponse of(
            String accessToken,
            String refreshToken,
            String userId
    ) {
        return new UserJoinResponse(accessToken, refreshToken, userId);
    }
}
  1. RedisConfig - Redis 연결 구성
    구현한 내용: 애플리케이션과 Redis 서버 간의 연결을 구성
@Configuration
public class RedisConfig {

    @Value("${spring.data.redis.host}")
    private String host;

    @Value("${spring.data.redis.port}")
    private int port;

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(host, port);
    }
    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}
  1. MemberController - 토큰 재발급 컨트롤러
    구현한 내용: 만료된 AccessToken을 갱신하기 위해 사용자의 RefreshToken을 받아 새로운 AccessToken을 발급하는 API 엔드포인트를 제공
@PostMapping("/refresh")
public ResponseEntity<UserJoinResponse> refreshToken(){
    UserJoinResponse userJoinResponse = memberService.refreshToken(
            principalHandler.getUserIdFromPrincipal()
    );
    return ResponseEntity.status(HttpStatus.CREATED)
            .header("Location", userJoinResponse.userId())
            .body(
                    userJoinResponse
            );
}

7.MemberService - 로그인 및 토큰 관리 서비스
구현한 내용: 로그인 시 사용자를 인증하고 AccessToken 및 RefreshToken을 발급

RedisTokenRepository의 사용: 이 클래스는 Redis 데이터베이스에서 RefreshToken 관리를 담당. 이를 활용하여 RefreshToken의 존재 유무를 확인하고, 새로운 RefreshToken을 저장

if (!redisTokenRepository.existsById(memberId.toString())) {
    throw new UnauthorizedException(ErrorMessage.INVALID_REFRESH_TOKEN);
}
redisTokenRepository.save(Token.of(memberId, refreshToken));

AccessToken과 RefreshToken 발급: JwtTokenProvider를 사용하여 사용자 인증 후 AccessToken과 RefreshToken을 발급하는 로직

String accessToken = jwtTokenProvider.issueAccessToken(
    UserAuthentication.createUserAuthentication(memberId)
);
String refreshToken = jwtTokenProvider.issueRefreshToken(
    UserAuthentication.createUserAuthentication(memberId)
);

사용자 회원가입 및 토큰 반환: 사용자가 시스템에 회원가입할 때 AccessToken과 RefreshToken을 발급하고, 이를 반환하는 로직
이 부분에서 사용자 정보를 데이터베이스에 저장하고, 발급된 토큰들과 함께 UserJoinResponse DTO를 통해 응답

Member member = memberRepository.save(
    Member.create(memberCreate.name(), memberCreate.part(), memberCreate.age())
);
return UserJoinResponse.of(accessToken, refreshToken, memberId.toString());

RefreshToken 유효성 검증 및 토큰 재발급: 사용자의 RefreshToken이 유효한지 Redis에서 검사하고, 유효한 경우 새로운 AccessToken과 RefreshToken을 발급

public UserJoinResponse refreshToken(Long memberId) {
    if (!redisTokenRepository.existsById(memberId.toString())) {
        throw new UnauthorizedException(ErrorMessage.INVALID_REFRESH_TOKEN);
    }
    findById(memberId);  // 유저 ID 검증

    return issueTokens(memberId);  // 토큰 재발급
}

8.ErrorMessage - 오류 코드 관리

INVALID_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED.value(), "REFRESH 토큰이 만료되었습니다."),
;

구현 고민 사항

질문있어요!

Copy link

@jinkonu jinkonu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR 잘 읽었습니다!!
변경된 파일들을 따로 볼 수 없어 안타깝네요 🥹🥹
(그리고 코드 스니펫을 사용하실 때 'java'를 넣어서 자바 코드에 맞게 색을 바꿔주시면 가독성을 늘릴 수 있을 것 같습니다..!)

Copy link

@bbabbi bbabbi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고하셨습니다 🍀✨

위에서 언급해주신 PR에 코드 작성하는 부분은 채은(@chaentopia )님이 노션에 작성해주신 글 참고하셔도 좋을 것 같아요! 샤라웃 투 채은~
(https://sopt-official.notion.site/PR-b62d5d5f0f144346bf1f117239ef150f?pvs=4)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Week 06] 6주차 과제
3 participants