JWT를 이용해 AccesToken관리하기
JwtUtil 클래스에서는 JWT를 생성하기 위한 createToken메서드가 존재한다. JWT에는 userId와 name을 넣을 것이고 HMAC-SHA256 해싱 알고리즘을 사용해 토큰의 유효성을 검사할 것이다.
public class JwtUtil {
private String secret;
public JwtUtil(String secret) { this.secret = secret; }
public String createToken(Long userId, String name) {
Key key = Keys.hmacShaKeyFor(secret.getBytes());
String token = Jwts.builder() .claim("userId", 1004L) .claim("name", name) .signWith(key, SignatureAlgorithm.HS256) .compact();
return token; } }
|
JWT가 올바르게 생성됐는지 확인하는 테스트 코드
class JwtUtilTest {
@Test public void createToken(){ String secret = "12345678901234567890123456789012"; JwtUtil jwtUtil = new JwtUtil(secret); String token = jwtUtil.createToken(1004L, ""); assertThat(token).contains("."); } }
|
JwtUtil을 Bean으로 등록
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${jwt.secret}") private String secret;
@Override protected void configure(HttpSecurity http) throws Exception { http .cors().disable() .csrf().disable() .formLogin().disable() .headers().frameOptions().disable(); }
@Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); }
@Bean public JwtUtil jwtUtil(){ return new JwtUtil(secret); } }
|
사용자 요청 결과로 JWT반환하는 로직을 구현
/session
경로로 사용자 정보를 받으면 우선 사용자 유효성을 확인한 뒤, 등록된 사용자인 경우 사용자 정보를 JWT에 일부 담아 Response Body에 넣어 사용자에게 반환한다.
@PostMapping("/session") public ResponseEntity<SessionResponseDto> create(@RequestBody SessionRequestDto resource){ String email = resource.getEmail(); String password = resource.getPassword(); User user = userService.authenticate(email, password);
String url = "/session"; String accessToken = jwtUtil.createToken(user.getId(), user.getName());
SessionResponseDto sessionResponseDto = SessionResponseDto.builder() .accessToken(accessToken) .build();
return ResponseEntity.created(URI.create(url)).body(sessionResponseDto); }
|
사용자 요청에 JWT를 반환하는지 확인하는 테스트 코드 작성
@Autowired private ObjectMapper objectMapper;
...
@MockBean private JwtUtil jwtUtil;
@Test @DisplayName("AccessToken을 반환하는지 확인한다.") public void create() throws Exception { Long id = 1004L; String email = "tester@example.com"; String password = "test"; String name = "Tester";
User mockUser = User.builder() .id(id) .email(email) .name(name) .password(password) .level(3L) .build();
String content = objectMapper.writeValueAsString(mockUser);
given(userService.authenticate(email, password)).willReturn(mockUser); given(jwtUtil.createToken(id, name)).willReturn("header.payload.signature");
ResultActions resultActions = mockMvc.perform(post("/session") .contentType(MediaType.APPLICATION_JSON)
.content(content) );
resultActions .andExpect(status().isCreated()) .andExpect(header().string("location", "/session")) .andExpect(content().string(containsString("{\"accessToken\":\"header.payload.signature\"}")));
verify(userService).authenticate(eq(email), eq(password)); }
|
@Test @DisplayName("사용자를 생성한다.") public void create() throws Exception { String email = "tester@example.com"; String name = "Tester"; String password = "test";
User mockUser = User.builder() .id(1004L) .email(email) .password(password) .name(name) .build();
given(userService.registerUser(email, name, password)) .willReturn(mockUser);
ResultActions resultActions = mockMvc.perform(post("/users") .contentType(MediaType.APPLICATION_JSON) .content("{\"email\" : \"tester@example.com\", \"name\" : \"Tester\", \"password\" : \"test\"}"));
resultActions .andExpect(status().isCreated()) .andExpect(header().string("location", "/users/1004"));
verify(userService).registerUser(any(), any(), any()); }
|