-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[✨6주차 실습과제 제출✨] #10
base: main
Are you sure you want to change the base?
[✨6주차 실습과제 제출✨] #10
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
코드를 잘 읽어보았습니다..! PR에 wrapper파일까지 모두 올라가 있어 .gitignore에 추가하여 필요한 부분만 코드를 올리면 더 좋을 것 같다는 생각이 들었어요!!! LoginFilter를 따로 구현하여 로그인 부분을 처리하신 부분이 인상깊었습니다..!
과제하시느라 고생많으셨습니다 😊
@PatchMapping("/blog/{blogId}/title") | ||
public ResponseEntity updateBlogTitle( | ||
@PathVariable Long blogId, | ||
@Valid @RequestBody BlogTitleUdateRequest blogTitleUdateRequest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 코드 부분에 오타가 있는 것 같습니다!!
'BlogTitleUdateRequest' -> 'BlogTitleUpdateRequest'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
우왓 감사합니다!
@GetMapping("/{memberId}") | ||
public ResponseEntity<ApiResponse<?>> findMemberById(@PathVariable("memberId") Long memberId) { | ||
return ApiUtils.success(HttpStatus.OK, memberService.findMemberById(memberId)); | ||
} | ||
|
||
@DeleteMapping("/{memberId}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
위의 Controller의 @RequestMapping이 ("/api/v1") 이라서 만약 다른 Entity의 정보를 불러오는 GET api를 구현 시 주소가 일치하면서 오류들
(1)블로그 GET API ("/api/v1/{postId}") -> 실제 주소 : /api/v1/1
(2)멤버 GET API ("/api/v1/{memberId}") -> 실제 주소 : /api/v1/1 *구분이 어려움
이 생길 수 있어 "/members/{memberId}"로 바꾼다면 그러한 오류를 방지할 수 있을 것 같다는 생각이 들어서 적어보았습니다..!:)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
헉 이 부분 제가 빠뜨렸네요... 말씀해주신 부분이 오류 방지 측면에서 맞는 것 같습니다! 지적 감사해요😀
|
||
private String cotent; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이부분에 오타가 있는 것 같습니다...!
'cotent' -> 'content' 로 바꾸면 더 좋을 것 같아요!!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 감사합니다!
private static final String[] AUTH_WHITE_LIST = {"/api/v1/members", "/", "/api/v1/members/reissue"}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기에서 "/" 는 전체 경로를 추가하는 표현인가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저도 "/"의 의도가 궁금합니다~ 추가로 알고 계실 것 같지만 와일드카드 표현도 있으니 찾아보시는 것 권장드립니당
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
부족하지만 요청하셨길래 이번에는 달아보았습니다.
} | ||
|
||
@GetMapping("/{memberId}") | ||
public ResponseEntity<ApiResponse<?>> findMemberById(@PathVariable("memberId") Long memberId) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ApiResponse를 쓰더라도 안에 와일드카드가 아니라 실제 DTO값을 사용하시는게 같이 작업하는 사람 입장에서 코드의 가독성이 더 좋아질 것 같아요!
private static final String[] AUTH_WHITE_LIST = {"/api/v1/members", "/", "/api/v1/members/reissue"}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저도 "/"의 의도가 궁금합니다~ 추가로 알고 계실 것 같지만 와일드카드 표현도 있으니 찾아보시는 것 권장드립니당
@RequiredArgsConstructor | ||
public class LoginFilter extends UsernamePasswordAuthenticationFilter { | ||
|
||
private final AuthenticationManager authenticationManager; | ||
|
||
@Override | ||
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) { | ||
// 사용자가 제공한 인증 정보 추출 | ||
String email = request.getParameter("email"); | ||
String password = request.getParameter("password"); | ||
|
||
// UsernamePasswordAuthenticationToken 생성(이때 authentication 는 인증 여부를 확인하는 authenticated 값이 false) | ||
UserAuthentication authentication = UserAuthentication.createUserAuthentication(email, password); | ||
|
||
/** | ||
* 실제 인증을 수행하도록 AuthenticationManager에게 위임 | ||
* 사용자가 입력한 email, password가 UserDetails를 통해 읽어온 UserDetails 객체에 들어있는 emial, password와 일치하는지 확인 | ||
* 인증이 성공적으로 완료되면 SecurityContextHolder에 저장된다. | ||
*/ | ||
return authenticationManager.authenticate(authentication); | ||
} | ||
|
||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
흥미롭네요~
.exceptionHandling(exception -> | ||
{ | ||
exception.authenticationEntryPoint(customJwtAuthenticationEntryPoint); | ||
exception.accessDeniedHandler(customAccessDeniedHandler); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
따로 role 사용하는 API 없었는데 accessDeniedHandler까지 하셨네요 짱입니다~
|
||
import java.io.IOException; | ||
|
||
public class CustonAuthenticaitonFailureHandler implements AuthenticationFailureHandler { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오타가 있는 것 같습니다. 그리고 제가 Security를 잘 몰라서 그러는데 해당 부분은 어떤 역할을 하려고 만드신건지 궁금합니다! (진짜 궁금)
public TokenInfo reissue(String refreshToken) { | ||
jwtTokenProvider.validateToken(refreshToken); | ||
String userEmail = jwtTokenProvider.getUserFromJwt(refreshToken); | ||
System.out.println("userEmail = " + userEmail); | ||
Member member = findMember(userEmail); | ||
|
||
//리프레시 토큰 탈취여부 확인(탈취범이 정상 유저보다 먼저 재발급 받았을 경우 -> 재로그인 유도) | ||
jwtTokenProvider.matchRefreshToken(refreshToken, findRefreshToken(member.getId()).getRefreshToken()); | ||
|
||
UserAuthentication userAuthentication = UserAuthentication.createUserAuthentication(userEmail); | ||
TokenInfo token = issueTokenAndStoreRefreshToken(userAuthentication, member.getId()); | ||
|
||
return token; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
링크 걸어주신 블로그 읽고 같이 고민하신 흔적이 보여서 좋은 것 같습니다. 다만 블로그에서는 마지막에 결국 해결 불가능한 문제 같다고 찝찝해하는 내용이 있었던 것 같은데 제가 아는 선에서는 token의 경우 개발자의 잘못으로 유출되는 것이 아닌 유저가 잘못된 사용으로 인해 노출하지 않는 한 탈취될 위험이 없는 것으로 압니다.
카드 발급해줬더니 카드 잃어버렸다고 카드 발급한 사람 탓하면 안되듯이 개발자는 이정도로 최대한 보안을 신경쓰며 만드는 선이 최선이라고 저는 생각합니다~
private RefreshToken findRefreshToken(Long userId) { | ||
return refreshTokenRepository.findById(userId) | ||
.orElseThrow(() -> new NotFoundException(ErrorMessage.EXPIRED_REFRESH_TOKEN)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아예 잘못된 rtk를 보낼 수도 있는데 expired 보다는 그냥 not_found라는 표현이 적절하지 않을까 조심스레... 적어봅니다
과제하시느라 고생하셨습니다!! RTR 방식은 처음 접해봤는데요, 토큰 탈취범이 유저보다 먼저 리프레시 토큰을 발급할 때의 경우의 수까지 고려하는 것은 생각지도 못한 부분이었네요!! 링크 걸어주신 자료까지 잘 봤습니다! 탈취 시나리오도 앞으로 잘 고려해야 겠네요 |
1️⃣ 로그인시 ATK와 RTK 함께 반환하는 API(
/api/v1/members
)따로 로그인 API를 만들지는 않았고, 저번 세미나에서 이어서 회원가입시에 ATK와 RTK를 함께 반환하도록 했습니다!
issueTokenAndStoreRefreshToken라는 메서드를 따로 만들어서, jwtTokenProvider에서 한꺼번에 ATK와 RTK를 생성하고 TokenInfo dto를 받아온 후, RTK를 생성한후 저장하도록 했습니다.
JungYoonShin/week_6/6thSeminar/src/main/java/com/sopt/seminar/controller/MemberController.java
Lines 22 to 31 in 46e4dff
JungYoonShin/week_6/6thSeminar/src/main/java/com/sopt/seminar/service/MemberService.java
Lines 33 to 48 in 46e4dff
JungYoonShin/week_6/6thSeminar/src/main/java/com/sopt/seminar/service/MemberService.java
Lines 102 to 112 in 46e4dff
2️⃣ ATK 재발급 API(/api/v1/members/reissue)
우선 RTK rotation 방식을 적용해보았습니다! JWT가 가진 여러 이점이 있지만, 토큰이 탈취되었을 경우에 생기는 큰 취약점을 보완하고자 해당 방식을 적용해보았는데요, 로직은 아래와 같이 구성했습니다.
JungYoonShin/week_6/6thSeminar/src/main/java/com/sopt/seminar/controller/MemberController.java
Lines 33 to 37 in 46e4dff
JungYoonShin/week_6/6thSeminar/src/main/java/com/sopt/seminar/service/MemberService.java
Lines 50 to 63 in 46e4dff
JungYoonShin/week_6/6thSeminar/src/main/java/com/sopt/seminar/jwt/JwtTokenProvider.java
Lines 78 to 82 in 46e4dff
Refresh token rotaion 참고 링크
많은 피드백 해주시면 대환영입니다🙇♀️
files changed에 다른 주차 과제도 같이 올라와버린 이슈가....죄송합니다아...🥲