JWT
토큰 제공자 추가
application.yaml.파일 수정
jwt:
issue : email@gmail.com
secert_key : TOKEN_KEY
config/jwt 패키지에 TokenProvider.java
@SpringBootTest
class TokenProviderTest {
@Autowired
private TokenProvider tokenProvider;
@Autowired
private UserRepository userRepository;
@Autowired
private JwtProperties jwtProperties;
@DisplayName("generateToken(): 유저 정보와 만료 기간을 전달해 토큰을 만들 수 있다.")
@Test
void generateToken() {
// given
User testUser = userRepository.save(User.builder()
.email("user@gmail.com")
.password("test")
.build());
// when
String token = tokenProvider.generateToken(testUser, Duration.ofDays(14));
// then
Long userId = Jwts.parser()
.setSigningKey(jwtProperties.getSecretKey())
.parseClaimsJws(token)
.getBody()
.get("id", Long.class);
assertThat(userId).isEqualTo(testUser.getId());
}
@DisplayName("validToken(): 만료된 토큰인 경우에 유효성 검증에 실패한다.")
@Test
void validToken_invalidToken() {
// given
String token = JwtFactory.builder()
.expiration(new Date(new Date().getTime() - Duration.ofDays(7).toMillis()))
.build()
.createToken(jwtProperties);
// when
boolean result = tokenProvider.validToken(token);
// then
assertThat(result).isFalse();
}
@DisplayName("validToken(): 유효한 토큰인 경우에 유효성 검증에 성공한다.")
@Test
void validToken_validToken() {
// given
String token = JwtFactory.withDefaultValues()
.createToken(jwtProperties);
// when
boolean result = tokenProvider.validToken(token);
// then
assertThat(result).isTrue();
}
@DisplayName("getAuthentication(): 토큰 기반으로 인증정보를 가져올 수 있다.")
@Test
void getAuthentication() {
// given
String userEmail = "user@email.com";
String token = JwtFactory.builder()
.subject(userEmail)
.build()
.createToken(jwtProperties);
// when
Authentication authentication = tokenProvider.getAuthentication(token);
// then
assertThat(((UserDetails) authentication.getPrincipal()).getUsername()).isEqualTo(userEmail);
}
@DisplayName("getUserId(): 토큰으로 유저 ID를 가져올 수 있다.")
@Test
void getUserId() {
// given
Long userId = 1L;
String token = JwtFactory.builder()
.claims(Map.of("id", userId))
.build()
.createToken(jwtProperties);
// when
Long userIdByToken = tokenProvider.getUserId(token);
// then
assertThat(userIdByToken).isEqualTo(userId);
}
}
JwtFactory.java
@Getter
public class JwtFactory {
private String subject = "test@email.com";
private Date issuedAt = new Date();
private Date expiration = new Date(new Date().getTime() + Duration.ofDays(14).toMillis());
private Map<String, Object> claims = emptyMap();
@Builder
public JwtFactory(String subject, Date issuedAt, Date expiration,
Map<String, Object> claims) {
this.subject = subject != null ? subject : this.subject;
this.issuedAt = issuedAt != null ? issuedAt : this.issuedAt;
this.expiration = expiration != null ? expiration : this.expiration;
this.claims = claims != null ? claims : this.claims;
}
public static JwtFactory withDefaultValues() {
return JwtFactory.builder().build();
}
public String createToken(JwtProperties jwtProperties) {
return Jwts.builder()
.setSubject(subject)
.setHeaderParam(Header.TYPE, Header.JWT_TYPE)
.setIssuer(jwtProperties.getIssuer())
.setIssuedAt(issuedAt)
.setExpiration(expiration)
.addClaims(claims)
.signWith(SignatureAlgorithm.HS256, jwtProperties.getSecretKey())
.compact();
}
}
리프레시 토큰 도메인 구현 (RefreshToken.java)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Entity
public class RefreshToken {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", updatable = false)
private Long id;
@Column(name = "user_id", nullable = false, unique = true)
private Long userId;
@Column(name = "refresh_token", nullable = false)
private String refreshToken;
public RefreshToken(Long userId, String refreshToken) {
this.userId = userId;
this.refreshToken = refreshToken;
}
public RefreshToken update(String newRefreshToken) {
this.refreshToken = newRefreshToken;
return this;
}
}
레파지토리 생성(RfreshTokenRepository.java)
public interface RefreshTokenRepository extends JpaRepository<RefreshToken, Long> {
Optional<RefreshToken> findByUserId(Long userId);
Optional<RefreshToken> findByRefreshToken(String refreshToken);
}
토큰 필터 구형(config/TokenAuthenticationFilter.java)
@RequiredArgsConstructor
public class TokenAuthenticationFilter extends OncePerRequestFilter {
private final TokenProvider tokenProvider;
private final static String HEADER_AUTHORIZATION = "Authorization";
private final static String TOKEN_PREFIX = "Bearer ";
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String authorizationHeader = request.getHeader(HEADER_AUTHORIZATION);
String token = getAccessToken(authorizationHeader);
if (tokenProvider.validToken(token)) {
Authentication authentication = tokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
private String getAccessToken(String authorizationHeader) {
if (authorizationHeader != null && authorizationHeader.startsWith(TOKEN_PREFIX)) {
return authorizationHeader.substring(TOKEN_PREFIX.length());
}
return null;
}
}
토큰 API 구현
UserService.java
@RequiredArgsConstructor
@Service
public class UserService {
private final UserRepository userRepository;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
public Long save(AddUserRequest dto) {
return userRepository.save(User.builder()
.email(dto.getEmail())
.password(bCryptPasswordEncoder.encode(dto.getPassword()))
.build()).getId();
}
public User findById(Long userId) {
return userRepository.findById(userId)
.orElseThrow(() -> new IllegalArgumentException("Unexpected user"));
}
}
service/RfreshTokenService.java
@RequiredArgsConstructor
@Service
public class RefreshTokenService {
private final RefreshTokenRepository refreshTokenRepository;
public RefreshToken findByRefreshToken(String refreshToken) {
return refreshTokenRepository.findByRefreshToken(refreshToken)
.orElseThrow(() -> new IllegalArgumentException("Unexpected token"));
}
}
TokenService.java
@RequiredArgsConstructor
@Service
public class TokenService {
private final TokenProvider tokenProvider;
private final RefreshTokenService refreshTokenService;
private final UserService userService;
public String createNewAccessToken(String refreshToken) {
// 토큰 유효성 검사에 실패하면 예외 발생
if(!tokenProvider.validToken(refreshToken)) {
throw new IllegalArgumentException("Unexpected token");
}
Long userId = refreshTokenService.findByRefreshToken(refreshToken).getUserId();
User user = userService.findById(userId);
return tokenProvider.generateToken(user, Duration.ofHours(2));
}
}
'Java' 카테고리의 다른 글
묘공단-스프링부트3 8장 (0) | 2023.10.23 |
---|---|
묘공단-스프링부트3 7장 (0) | 2023.10.23 |
묘공단-스프링부트3 6장 (0) | 2023.10.10 |
묘공단-스프링부트3 5장 (0) | 2023.10.03 |
묘공단-스프링부트3 4장 (0) | 2023.09.30 |