레스토랑 예약 사이트 만들기 12 - JWT

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 {

// application.yml에 정의된 JWT Secret값을 가져온다.
@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("{\"email\":\"tester@example.com\",\"password\":\"test\"}")
.content(content)
);

resultActions
.andExpect(status().isCreated())
.andExpect(header().string("location", "/session"))
// AccessToken을 사용하는지 확인한다.
.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());
}
Share