레스토랑 예약 사이트 만들기 13 - 인가

JWT로부터 데이터 가져오기

public class JwtUtil {

private Key key;

public JwtUtil(String secret) {
this.key = Keys.hmacShaKeyFor(secret.getBytes());
}

public String createToken(Long userId, String name) {

String token = Jwts.builder()
.claim("userId", 1004L)
.claim("name", name)
.signWith(key, SignatureAlgorithm.HS256)
.compact();

return token;
}

public Claims getClaims(String token){
Claims claims = Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody();

return claims;
}
}
@Test
@DisplayName("Claims를 가져온다.")
public void getClaims(){
String token = "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEwMDQsIm5hbWUiOiJUZXN0ZXIifQ.I4DNdunio2m54tfUEaXHC_E-gvCQo6ZhHO15Ewkat6U";
Claims claims = jwtUtil.getClaims(token);

assertThat(claims.get("userId", Long.class)).isEqualTo(1004L);
assertThat(claims.get("name")).isEqualTo("Tester");
}

JWTFilter추가

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Value("${jwt.secret}")
private String secret;

@Override
protected void configure(HttpSecurity http) throws Exception {
Filter filter = new JwtAuthenticationFilter(authenticationManager(), jwtUtil());

http
.cors().disable()
.csrf().disable()
.formLogin().disable()
.headers().frameOptions().disable();

http
.addFilter(filter)
.sessionManagement()
// Session을 사용하지 않음
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
;
}

@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}

@Bean
public JwtUtil jwtUtil(){
return new JwtUtil(secret);
}
}

JWT 인증 Filter

public class JwtAuthenticationFilter extends BasicAuthenticationFilter {

private JwtUtil jwtUtil;

public JwtAuthenticationFilter(AuthenticationManager authenticationManager, JwtUtil jwtUtil) {
super(authenticationManager);
this.jwtUtil = jwtUtil;
}

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
Authentication authentication = getAuthentication(request);

if(authentication != null){
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(authentication);
}

chain.doFilter(request, response);
}

private Authentication getAuthentication(HttpServletRequest request){
// Header에서 Data를 얻어야 한다.
String token = request.getHeader("Authorization");
if(token == null){
return null;
}


Claims claims = jwtUtil.getClaims(token.substring("Bearer ".length()));
Authentication authentication = new UsernamePasswordAuthenticationToken(claims, null);

return authentication;
}
}

요청시 Token값을 가지고 있는지 확인

@RestController
@RequiredArgsConstructor
public class ReviewController {

private final ReviewService reviewService;

@PostMapping("/restaurants/{restaurantId}/reviews")
public ResponseEntity<?> create(
Authentication authentication,
@PathVariable Long restaurantId,
@Valid @RequestBody Review resource
) throws URISyntaxException {
Claims claims = (Claims) authentication.getPrincipal();

String name = claims.get("name", String.class);

Review review = reviewService.addReview(restaurantId, resource);

String url = "/restaurants/" + restaurantId + "/reviews/" + review.getId();
return ResponseEntity.created(new URI (url)).body("{}");
}
}

테스트 코드 작성

@Test
public void 리뷰를_생성한다() throws Exception {
String token = "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEwMDQsIm5hbWUiOiJUZXN0ZXIifQ.I4DNdunio2m54tfUEaXHC_E-gvCQo6ZhHO15Ewkat6U";

given(reviewService.addReview(eq(1L), any())).willReturn(
Review.builder()
.id(1004L)
.name("JOKER")
.score(3)
.description("Mat-it-da")
.build());


mockMvc.perform(post("/restaurants/1/reviews")
.header("Authorization", "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON)
.content("{\"name\":\"JOKER\", \"score\":3,\"description\" : \"Mat-it-da\"}"))
.andExpect(status().isCreated())
.andExpect(header().string("location", "/restaurants/1/reviews/1004"));

verify(reviewService).addReview(eq(1L), any());
}
Share