-
Notifications
You must be signed in to change notification settings - Fork 0
Feat/#8 login #10
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
base: dev
Are you sure you want to change the base?
Feat/#8 login #10
Conversation
Co-authored-by: SungHo Jung <65662139+SeongHo5356@users.noreply.github.com>
SeongHo5356
left a comment
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.
우선 구현하느라 고생하셨습니다. 스프링 시큐리티는 다시봐도 구현할 게 많네요. 컨벤션도 잘 지키고, dto도 다 record타입으로 만들어서 사용하시고 노력한 부분이 많이 보여서 좋았습니다~
함수 네이밍에 좀 더 신경쓰고, @Valid를 활용하면 좀 더 가독성 좋고 안정적인 코드가 될 수 있을 것 같습니다.
| // JWT | ||
| implementation 'io.jsonwebtoken:jjwt-api:0.11.5' | ||
| implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' | ||
| implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' |
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.
추후에 목적에 따라 gradle을 나눠볼 수도 있을 것 같아요!
아래 참고블로그 남겨놓을게요
gradl 분리
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.
새로운 정보를 알게 되었습니다. 감사합니다!
| public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { | ||
| return memberRepository.findByEmail(username) | ||
| .map(this::createUserDetails) | ||
| .orElseThrow(() -> new UsernameNotFoundException("User not found")); |
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.
| .orElseThrow(() -> new UsernameNotFoundException("User not found")); | |
| .orElseThrow(() -> new CoreException(GlobalErrorType.MEMBER_NOT_FOUND) |
기존에 정의해놓은 exception 클래스를 활용할 수도 있을 것 같아요
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.
한번 활용해 보겠습니다!!
| import static org.aspectj.weaver.tools.cache.SimpleCacheFactory.path; | ||
|
|
||
| @RequiredArgsConstructor | ||
| public class JwtAuthenticationFilter extends GenericFilterBean { |
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.
| public class JwtAuthenticationFilter extends GenericFilterBean { | |
| public class JwtAuthenticationFilter extends OncePerRequestFilter { |
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.
GenericFilterBean: 한 번의 클라이언트 요청에 대해 여러번 요청하게 되어 자원 낭비가 발생할 수 있다.
OncePerRequestFilter: doFilterInternal 메서드를 구현하여 사용자의 요청 당 한 번만 실행되는 필터를 만들 수 있다.
감사합니다!!
| String token = resolveToken((HttpServletRequest) servletRequest); // header에서 jwt 토큰을 추출 | ||
| if (token != null && jwtTokenProvider.validateToken(token)) { // 토큰이 존재하고 유효할 경우 | ||
| Authentication authentication = jwtTokenProvider.getAuthentication(token); // 토큰에서 Authentication 객체를 얻어 | ||
| SecurityContextHolder.getContext().setAuthentication(authentication); // Spring SecurityContext에 저장 | ||
| } | ||
| filterChain.doFilter(servletRequest, servletResponse); // 다음 필터로 요청을 전달(이걸 호출하지 않으면 요청이 중간에서 끊겨버려서 다음 단계 진행 불가) | ||
| } |
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.
그런 문제가 발생할 것 같습니다. 유효하지 않을 경우 오류를 return하는 부분을 추가해야 할 것 같습니다!
| System.out.println(path); | ||
| // 인증이 필요 없는 경로는 필터 패스 | ||
| if (NO_AUTH_PATHS.contains(path)) { | ||
| System.out.println("No auth needed for path: " + path); |
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.
| System.out.println("No auth needed for path: " + path); | |
| log.debug("No auth needed for {}", path); |
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.
대량의 로그 발생시 서버에 악영향을 주는 Sysout을 지양해야 한다는 사실을 알게됐습니다. 감사합니다!!
| @Entity | ||
|
|
||
| @NoArgsConstructor | ||
|
|
||
| public class Meeting extends BaseEntity { |
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.
수정 해 놓겠습니다!
| private final MemberService memberService; | ||
|
|
||
| @PostMapping("/login") | ||
| public ApiResponse<?> login(@RequestBody LoginRequest loginRequest){ |
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.
| public ApiResponse<?> login(@RequestBody LoginRequest loginRequest){ | |
| public ApiResponse<?> login(@Valid @RequestBody LoginRequest loginRequest){ |
요청 바디의 유효성 검사를 먼저 할 수도 있을 것 같네요
@Valid 참고자료
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.
감사합니다! dto와 연관지어 사용해보겠습니다!
| } | ||
|
|
||
| @PostMapping("/signup") | ||
| public ApiResponse<?> signUp(@RequestBody SignUpRequest signUpRequest){ |
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.
| public ApiResponse<?> signUp(@RequestBody SignUpRequest signUpRequest){ | |
| public ApiResponse<?> signUp(@Valid @RequestBody SignUpRequest signUpRequest){ |
| @Schema(description = "이메일", example = "abc1234@khu.ac.kr") | ||
| String email, | ||
|
|
||
| @Schema(description = "비밀번호", example = "1234") | ||
| String password |
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.
| @Schema(description = "이메일", example = "abc1234@khu.ac.kr") | |
| String email, | |
| @Schema(description = "비밀번호", example = "1234") | |
| String password | |
| @Null | |
| @Schema(description = "이메일", example = "abc1234@khu.ac.kr") | |
| String email, | |
| @Null | |
| @Schema(description = "비밀번호", example = "1234") | |
| String password |
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.
혹시 로그인 기능이니 @notblank를 한번 사용해봐도 괜찮겠죠..?!
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.
넵~ 공부해보시고 쓰고싶으신 거 사용하시면 될 것 같아요
| @Schema(description = "이름", example = "홍길동") | ||
| String name | ||
| ) { | ||
| public Member toEntity(String encodedPassword) { |
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.
| public Member toEntity(String encodedPassword) { | |
| public Member toMember(String encodedPassword) { |
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.
toMember로 하면 훨씬 명확해질 것 같습니다. 감사합니다!
| private long refreshTokenExpirationTime; | ||
|
|
||
| //application.yml에 저장한 secret 값을 가져와서 key에 저장하기 | ||
| @PostConstruct // 스프링 컨테이너가 빈을 생성하고, 모든 의존성 주입이 끝난 뒤 자동으로 호출되는 메서드, 주입된 값을 안전하게 사용 가능 |
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.
@PostConstruct로 하면 Value 값이 잘 들어온후 key값 사용하는거라 key에서 null이 안들어오겠지만
public JWTUtil(MemberRepository memberRepository,
@value("${spring.jwtsecretkey}") String secretKey) {
this.memberRepository = memberRepository;
if (secretKey == null || secretKey.isEmpty()) {
throw new IllegalArgumentException("spring.jwtsecretkey 이 설정되어야 합니다");
}
this.jwtParser = Jwts.parserBuilder()
.setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes()))
.build();
} 생성자 주입할 때 잘 적어줘도 오류가 안나긴 하더라고요 빈 객체 생성 시점에서 생기는거라 생성자 주입에 key 쓰는거 다 넣어주면 괜찮을 거 같아요
Spring Security 설정과 JWT Token을 발급해주는 로그인 기능을 구현했습니다.
email, password, name을 받아 database에 member를 추가하는 회원가입 기능을 구현했습니다.
이후 Redis를 이용한 refresh token 관리 기능을 추가 예정입니다.
member table의 채워지지 않은 값들과 member_preference table에서 회원의 정보들을 받아 저장해야 하므로 이 값들을 받는 회원 프로필 설정 기능을 추가할 예정입니다.