티스토리 뷰
이 기능은 '병원 리뷰 사이트'에서 Review를 등록 할 때 Post요청의 Header로 전달받은 Token에서 UserName을 꺼내 댓글을 쓸 때 활용하는 로직 입니다.
ReviewCreateRequest
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class ReviewCreateRequest {
private Long hospitalId;
private String title;
private String content;
}
리뷰를 쓰기 위해 요청은 총 3가지를 받습니다. 리뷰를 작성하고 싶은 병원의 Id와 리뷰 제목, 내용 이렇게 3가지 입니다. 여기에는 리뷰를 쓰는 사용자에 대한 정보가 들어있지 않습니다.
Id에 해당하는 userName은 JWT Token을 통해 받습니다.
요청은 위와 같이 요청을 합니다. Post 요청의 Header에 Authorization에 Baearer 토큰으로 로그인 단계에서 받은 토큰을 넣어주어 리뷰 작성 요청 API를 호출 합니다.
ReviewController
@RestController
@RequestMapping("/api/v1/reviews")
@Slf4j
@RequiredArgsConstructor // 필요한 argument를 넣어줌
public class ReviewController {
private final ReviewService reviewService;
// Authentication에서 Name을 받아와서 create할 때 넣어줍니다.
@PostMapping
public Response<Void> create(@RequestBody ReviewCreateRequest request, Authentication authentication) {
log.info("userName:{}", authentication.getName());
reviewService.createReview(request, authentication.getName());
return Response.success(null);
}
}
Controller에 오기 전에 JwtTokenFilter를 거치기 때문에 Authentication에는 UserName이 들어있습니다.
AuthenticationConfig.java
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class AuthenticationConfig {
@Value("${jwt.secret-key}")
private String secretKey;
private final UserService userService;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
.httpBasic().disable()
.csrf().disable()
.cors().and()
.authorizeRequests()
.antMatchers("/api/**").permitAll()
.antMatchers("/api/v1/users/join", "/api/v1/users/login").permitAll() // join, login은 언제나 가능
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // jwt사용하는 경우 씀
.and()
.addFilterBefore(new JwtTokenFilter(userService, secretKey), UsernamePasswordAuthenticationFilter.class) //UserNamePasswordAuthenticationFilter적용하기 전에 JWTTokenFilter를 적용 하라는 뜻 입니다.
.build();
}
JwtTokenFilter 초기 모습
request에서 token을 받고 filterChain.doFilter()를 이용해 다음 chain으로 넘겨줍니다.
@Slf4j
public class JwtFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// Token 꺼내기
String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
filterChain.doFilter(request, response);
}
}
JwtTokenFilter
@Slf4j
@RequiredArgsConstructor
public class JwtTokenFilter extends OncePerRequestFilter {
private final UserService userService;
private final String key;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// token을 header에 넣었기 때문에 header에서 꺼내야 합니다.
// token에 Claim으로 UserName을 넣었기 때문에 token.Claim에서 꺼낼 예정입니다.
final String header = request.getHeader(HttpHeaders.AUTHORIZATION);
log.info("header:{}", header);
if (header == null || !header.startsWith("Bearer ")) {
log.error("헤더를 가져오는 과정에서 에러가 났습니다. 헤더가 null이거나 잘못되었습니다.");
filterChain.doFilter(request, response);
return;
}
try{
final String token = header.split(" ")[1].trim();
// Token Expired Time이 안지났는지
JwtTokenUtils.isExpired(token, key);
String userName = JwtTokenUtils.userName(token, key);
// User가져오기
User user = userService.getUserByUserName(userName);
log.info("user:{}", user.getUserName());
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(user.getUserName(), null, List.of(new SimpleGrantedAuthority(user.getRole().toString())));
// Detail을 넣어줍니다.
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
} catch (RuntimeException e){
log.error("error:{}", e.toString());
filterChain.doFilter(request, response);
return;
}
filterChain.doFilter(request, response);
}
}
JwtTokenFilter 초기 모습
JwtTokenFilter에서는 아래와 같이 3가지 작업을 합니다.
1. Token이 만료되지 않았는지 Check하기
2. Token에서 userName꺼내기
3. Authentication에 Username과 Role정보 넣기
public class JwtTokenUtils {
public static boolean isExpired(String token, String key) {
Date expiredDate = extractClaims(token, key).getExpiration(); // expire timestamp를 return함
return expiredDate.before(new Date()); // 현재보다 전인지 check를 합니다.
}
private static Claims extractClaims(String token, String key) {
return Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody();
}
public static String userName(String token, String key) {
return extractClaims(token, key).get("userName", String.class);
}
public static String generateToken(String userName, String key, long expiredTimeMs) {
Claims claims = Jwts.claims();
claims.put("userName", userName); // Token에 담는 정보를 Claim이라고 함
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + expiredTimeMs))
.signWith(SignatureAlgorithm.HS256, key)
.compact();
}
}
ReviewService
@Service
public class ReviewService {
private final HospitalRepository hospitalRepository;
private final ReviewRepository reviewRepository;
public ReviewService(HospitalRepository hospitalRepository, ReviewRepository reviewRepository) {
this.hospitalRepository = hospitalRepository;
this.reviewRepository = reviewRepository;
}
public void createReview(ReviewCreateRequest dto, String userName) {
// Hospital불러오기
Optional<Hospital> hospitalOptional = hospitalRepository.findById(dto.getHospitalId());
// ReviewEntity만들기
Review review = Review.builder()
.id(null)
.title(dto.getTitle())
.content(dto.getContent())
.patientName(userName)
.hospital(hospitalOptional.get())
.build();
// 저장
Review savedReview = reviewRepository.save(review);
}
}
728x90
'Spring > Spring Boot(스프링 부트)' 카테고리의 다른 글
SpringBoot Gradle빌드, Dockerbuild후 gitlab-ci (0) | 2022.12.13 |
---|---|
SpringBoot 2.7.5 WebMvcTest 작성할때 유의할 점 (1) | 2022.12.10 |
SpringBoot 일관된 Response 리턴하기 (0) | 2022.11.24 |
Saga Pattern에서 Saga의 뜻 (0) | 2022.08.09 |
Spring Boot Rest API 만들기 (0) | 2022.03.29 |
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- docker container tissue
- 도커각티슈케이스
- Linux
- docker container whale
- Sh
- 싱가폴
- vim
- 2017 티스토리 결산
- 도커티슈박스
- 도커티슈케이스
- 이직
- shellscript
- docker container
- docker container tissue box
- 도커각티슈박스
- 도커컨테이너
- docker container case
- 개발자
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
글 보관함