Skip to content
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

feat: JWT 기반 인증 및 Spring Security 설정 구현 #23 #26

Merged
merged 23 commits into from
Aug 26, 2024

Conversation

medoeun
Copy link
Collaborator

@medoeun medoeun commented Aug 24, 2024

🎟️ 관련 이슈

Fixes #23

👩‍💻 구현 내용

  • JwtUtil 생성
  • JwtAuthenticationFilter 구현
  • SecurityConfig 설정 파일 작성
  • CustomUserDetailsServiceCustomUserDetails 구현

💬 코멘트

  • JwtUtil에서 JWT 토큰 생성 및 검증 로직을 구현하였습니다.
  • JwtAuthenticationFilter를 통해 JWT 토큰을 검증하고, 인증 정보를 설정하도록 하였습니다.
  • CustomUserDetailsService는 UserDetailsService를 구현하여 사용자 인증 정보를 관리하도록 하였습니다.
  • SecurityConfig에서 Spring Security 설정과 JWT 필터를 적용하였습니다.
  • JWT 관련 예외 처리 및 테스트, 인증 필요한 엔드포인트 구분이 남아 있습니다. 이후 작업에서 계속 진행할 예정입니다.

💭 고려한 점

  • JwtUtil에서 토큰을 생성할 때 memberId와 account 정보를 포함시켰습니다.
  • deprecated 코드가 없도록 최신 jjwt 권장 방식을 사용했습니다.
  • 가능하면 기본적인 보안 설정을 유지하면서 커스텀을 적용하도록 노력하였습니다.
  • UsernameNotFoundException 예외 처리를 위한 StatusCode, 핸들러를 추가했습니다.

medoeun added 14 commits August 23, 2024 13:53
- 작업자 이해를 돕기 위한 주석 포함
- SecurityConfig 작성
- JwtAuthenticationFilter 작성
- JwtUtil 보충
- 작업자 이해를 돕기 위한 주석 포함
- SecurityConfig 작성
- JwtAuthenticationFilter 작성
- JwtUtil 보충
- 작업자 이해를 돕기 위한 주석 포함
- UserDetails에서 객체를 받아오게 JwtUtil 변경
- jjwt 최신 변경 방식에 따라 코드 수정
- UsernameNotFoundException 처리 핸들러 추가
- StatudsCode에 상태코드 추가
- CustomUserDetailsService 구현 진행 중
@medoeun medoeun added the 💫 enhancement New feature or request label Aug 24, 2024
@medoeun medoeun self-assigned this Aug 24, 2024
jooda00
jooda00 previously approved these changes Aug 24, 2024
Copy link
Collaborator

@jooda00 jooda00 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

로그인 쉽지 않으셨을텐데 고생하셨습니다!👍👍 저도 좀 더 알아볼 수 있으면 알아보고 리뷰 달아볼게요ㅎㅎ

import team05.integrated_feed_backend.module.member.entity.Member;
import team05.integrated_feed_backend.module.member.repository.MemberRepository;

@Service
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙋‍♀️ 저번에 저희 클래스 영역에서 @Transactional(readOnly = true) 해주기로 했던 것 같은데, 보니까 로그인은 post만 있는 것 같아서 딱히 필요없는 것 같기도 하고.. 다른 분들 의견 궁금합니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

누락인데 필요할 것 같습니다!! 수정할게요!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어노테이션들 추가했습니다 b69b667

import team05.integrated_feed_backend.module.member.entity.Member;

public interface MemberRepository extends JpaRepository<Member, Long> {
Optional<Member> findByAccount(String account);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💭 Optional 사용하셔서 null 체크가 더 효율적인 것 같네요!

Copy link
Collaborator

@hye-on hye-on left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨어요! 👍

Copy link
Collaborator

@uijin-j uijin-j left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

로그인 부분이 많이 까다로운데, 로깅까지 꼼꼼하게 해주셔서 감사합니다☺️👍🏻
늦게 리뷰한 주제에 리뷰 양이 많아서 죄송해요 허허😂 단순한 의견이니 모두 반영하실 필요없고, 넘어가 주셔도 괜찮습니당!!


@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헉 요부분은 BCryptPasswordEncoder 말고 Argon2PasswordEncoder를 쓸 것 같아요!
얘가 조금 더 보안이 좋다고 하더라구요! 근데 이건 제가 작업하면서 바꿔놓겠습니당😉

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그렇군요!! 저도 더 알아보겠습니당

public BaseApiResponse<Void> handleUsernameNotFoundException(UsernameNotFoundException e) {
log.warn(e.getMessage(), e);

return BaseApiResponse.of(StatusCode.USER_NOT_FOUND);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💭 요건 유저가 없는 거긴 한데, 토큰이 잘못된거니까 404에러 보다는 401 Unauthorized 에러는 어떠세용?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그게 맞는 것 같아 반영하면서 import도 수정해두었습니당! 8d5f488

Comment on lines 26 to 35
private final Key secretKey;

//jjwt: String secretKey -> Key 객체 방식으로 대체됨
public JwtUtil(@Value("${jwt.secret}") String secret) {
byte[] keyBytes = Base64.getDecoder().decode(secret);
this.secretKey = new SecretKeySpec(keyBytes, SignatureAlgorithm.HS256.getJcaName());
}

@Value("${jwt.token-validity-in-seconds}")
private long tokenValidityInseconds;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙋🏻‍♀️ 필드는 필드끼리 모여 있으면 좋을 것 같습니당!

Suggested change
private final Key secretKey;
//jjwt: String secretKey -> Key 객체 방식으로 대체됨
public JwtUtil(@Value("${jwt.secret}") String secret) {
byte[] keyBytes = Base64.getDecoder().decode(secret);
this.secretKey = new SecretKeySpec(keyBytes, SignatureAlgorithm.HS256.getJcaName());
}
@Value("${jwt.token-validity-in-seconds}")
private long tokenValidityInseconds;
private final Key secretKey;
@Value("${jwt.token-validity-in-seconds}")
private long tokenValidityInseconds;
//jjwt: String secretKey -> Key 객체 방식으로 대체됨
public JwtUtil(@Value("${jwt.secret}") String secret) {
byte[] keyBytes = Base64.getDecoder().decode(secret);
this.secretKey = new SecretKeySpec(keyBytes, SignatureAlgorithm.HS256.getJcaName());
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그리고 @Value로 yml에 있는 환경변수를 받아 오고 계신데,
@ConfigurationProperties() 애너테이션을 사용해서 해당 설정들을 가진 객체(ex. JWTProperties)로 만들어서 주입할수도 있어요! 요건 그냥 지식 공유였습니다😉

}

// JWT 토큰 유효성 검증
public boolean validateToken(String token) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💭 사소한 내용이긴한데, validateToken()은 반환값이 없을 것 같은 네이밍이라, isValidToken(String token) 같은 네이밍은 어떠세욤?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영했습니다! b848cb5

Comment on lines 79 to 83
return Long.parseLong(Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(token)
.getBody()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💭 요부분 밑에 getAccount()랑 반복되는 로직이 있어서, 메서드 분리해서 재사용하는 것도 좋을 것 같아요!

return null;
}

public Authentication getAuthentication(String token, UserDetails userDetails) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❗️호옥시 token을 인자로 받는 이유가 있을까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어디까지 token이 활용되는지 잘 몰라서 일단 받아오게 써둔 것 같아요...! 지켜보고 안쓰이면 빼겠습니당

Comment on lines 45 to 46
claims.put("memberId", memberId);
claims.put("account", account);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💭 요런 "memberId", "account" 같은 string은 private static final String 으로 상수화 해도 될 것 같아요!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영했습니다! 85b7ff6

@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

private final JwtUtil jwtUtil;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💭 요것도 정말 사소한데.. 인스턴스화 해서 사용하면 Util이라는 네이밍보단 JwtManager, JwtHandler 같은 네이밍은 어떠세욤?? 그리고 JwtUtil 클래스를 보면 HttpServletRequest, UserDetails 등 웹 계층이랑 의존성이 있어서 순수 유틸클래스는 아닌 것 같아서요! 근데 개인적인 의견이고 안바꾸셔도 됩니다!!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JwtManager 좋은 것 같아요! 반영했습니다 c91c9ab

Copy link
Collaborator

@yerim123456 yerim123456 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

주석 하나하나 달아주셔서 이해가 좀 더 쉬웠습니다. 어려운 부분 구현하느라 정말 고생 많으셨습니다!🥰

public class Member extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long memberId;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💭: 아주 사소한 의견인데요! 저희 저번에 강의에서 보안이 중요한 데이터는 UUID로 id를 정의하면 좋다고 했어서 혹시 나중에 시간 괜찮으시다면 한번 생각해봐주시면 감사할 것 같습니다:)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 생각해보겠습니다!!

# Conflicts:
#	build.gradle
#	src/main/java/team05/integrated_feed_backend/module/post/entity/Hashtag.java
#	src/main/java/team05/integrated_feed_backend/module/post/entity/Post.java
#	src/main/resources/application-template.yml
- swagger ui가 빈 페이지 반환하는 문제 확인, ui 관련 요청을 필터에서 제외
- `SecurityConfig`에서 Swagger ui관련 접근 허용 설정
- `JwtAuthenticationFilter`에서 `filterChain.doFilter` 조건문 안에 있어 필터로 전달 안되는 문제 확인 후 수정
Copy link
Collaborator

@toughCircle toughCircle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 공부가 필요한 부분인데, 도움이 많이 되었습니다! 고생 많으셨습니다! 👍

Copy link

📊 Jacoco Test Coverage

Overall Project 13.71% -15.23% 🍏
Files changed 31.8% 🍏

File Coverage
SecurityConfig.java 100% 🍏
BaseEntity.java 100% 🍏
CustomUserDetailsService.java 14.29% -85.71% 🍏
JwtManager.java 12.5% -87.5% 🍏
JwtAuthenticationFilter.java 5.88% -94.12% 🍏
CustomUserDetails.java 0% 🍏
PostController.java 0% 🍏
PostStatisticsController.java 0% 🍏
BaseApiResponse.java 0% 🍏
MemberController.java 0% 🍏
GlobalExceptionHandler.java 0% -4.91% 🍏
StatusCode.java 0% -21.05% 🍏
BadRequestException.java 0% 🍏
BusinessException.java 0% 🍏
DataNotFoundException.java 0% 🍏
ForbiddenException.java 0% 🍏
DuplicateResourceException.java 0% 🍏

@medoeun medoeun merged commit ddf7eea into main Aug 26, 2024
2 checks passed
@medoeun medoeun deleted the feature/jwt-auth branch August 26, 2024 06:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
💫 enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

JWT를 사용한 인증 기능 구현
6 participants