오늘 한 일
회원가입, 로그인 기능 개발
코드 리뷰받고 수정해보기!
알고리즘 스터디
회원가입 기능 개발중 ...
⭐생성자 대신 builder 쓰는법
Bag bag = Bag.builder()
.name("name")
.money(1000)
.memo("memo")
.build();
UserService에서 이렇게 하고 있던걸 builder로 바꿔봄
//register user
User user = new User(username,password);
userRepository.save(user);
→
//register user
User user = User.builder()
.email(email)
.password(password)
.nickname(nickname)
.build();
🚩문제 : userRepository.save(user); 빨간줄 뜸
왜?
UserRepository에 <User,Long>중 User가 내가 정의한 entity가 아니라 spring core것이 import되어있었다!
⛳해결 : 내가 정의한 Entity인 User를 다시 import해줌!
🚩문제 : getUserDetails가 안 되고 있음
public UserDetailsImpl getUserDetails(String email) {
User user = userRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException(email+" is not found"));
return new UserDetailsImpl(user);
}
import org.springframework.security.core.userdetails.User;
이것도 보니까 import가 잘못 되어 있었음
⛳해결 : import 문 다음과 같이 고침
import org.nbc.account.trollo.domain.user.entity.User;
🚩문제 : getUserDetails를 쓰는데에서 동작이 안 됨
이거 빨간줄임
UserDetailsImpl userDetails = userDetailsService.getUserDetails(email);
UserDetailsImpl가보니까
public UserDetailsImpl(User user) {
this.user = user;
}
이건 빨간줄은 아닌데 1 related problem으로 뜸
// username -> search user
String email = info.getSubject();
SecurityContext context = SecurityContextHolder.createEmptyContext();
// -> put this in userDetails
UserDetailsImpl userDetails = userDetailsService.getUserDetails(email);
저 위에 info.getSubject()가 뭘 하고있는건지 모르겠음
따라가보니 Claims.java가 나온다
/**
* Returns the JWT
* sub (subject) value or {@code null} if not present. * * @return the JWT {@code sub} value or {@code null} if not present. */ String getSubject();
token에서 뭘 가져오는 거 같음
내 token builder에 가봄
public String createToken(String userId) {
Date date = new Date();
// token will be expired in 60 mins
long TOKEN_TIME = 60 * 60 * 1000;
return BEARER_PREFIX +
Jwts.builder()
.setSubject(userId)
.setExpiration(new Date(date.getTime() + TOKEN_TIME))
.setIssuedAt(date)
.signWith(key, signatureAlgorithm)
.compact();
}
여기서 setSubject를 userId로 하고있었음
email로 고침
public String createToken(String email) {
Date date = new Date();
// token will be expired in 60 mins
long TOKEN_TIME = 60 * 60 * 1000;
return BEARER_PREFIX +
Jwts.builder()
.setSubject(email)
.setExpiration(new Date(date.getTime() + TOKEN_TIME))
.setIssuedAt(date)
.signWith(key, signatureAlgorithm)
.compact();
}
public UserDetailsImpl getUserDetails(String email) {
User user = userRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException(email+" is not found"));
return new UserDetailsImpl(user);
}
이게 회색줄이 뜨고
UserDetailsImpl userDetails = userDetailsService.getUserDetails(email);
이게 갈데가 없다고 빨간줄이 뜨는거 보니 내가 넣어주는 email이 String이 아닌가봄..?
/**
* Returns the JWT
* sub (subject) value or {@code null} if not present. * * @return the JWT {@code sub} value or {@code null} if not present. */ String getSubject();
String 맞는데..?
Claims info = jwtUtil.getUserInfoFromToken(token);
public Claims getUserInfoFromToken(String token) {
return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
}
import org.springframework.security.core.userdetails.UserDetailsService;
import 를 잘못함
⛳ 해결 : 내가 정의한 UserDetailsService를 import해줌
@Getter
@RequiredArgsConstructor
public class SignupReq {
@Size(min = 3,max = 15, message ="username should be longer than 3 and shorter than 15")
@Pattern(regexp = "^[a-zA-Z_0-9]*$", message = "only lowercases and numbers are allowed for username")
private String username;
@Size(min = 4,max = 15, message ="pw should be longer than 4 and shorter than 15")
@Pattern(regexp = "^[a-zA-Z_0-9]*$", message = "only alphabets and numbers are allowed for pw")
private String password;
private String passwordCheck;
}
원래는 이렇게 하고 있었는데
이메일을 받아오면서 @~.com 이런 형식인지도 검증하고 싶어짐!
https://www.baeldung.com/java-email-validation-regex
- local part = username
- @ = @
- domain = domain.com
이런 테스트코드를 보고
@Test
public void testUsingSimpleRegex() {
emailAddress = "username@domain.com";
regexPattern = "^(.+)@(\\\\S+)$";
assertTrue(EmailValidation.patternMatches(emailAddress, regexPattern));
}
@Size(min = 5,max = 25, message ="username should be longer than 3 and shorter than 15")
@Pattern(regexp = "^(.+)@(\\\\S+) $.", message = "only email@domain.com is allowed")
private String email;
걍 이렇게 해봄
안 됐다
{
"email" : "email@dot.com",
"password" : "passpass",
"passwordCheck" : "passpass"
}
2023-12-28T11:12:59.836+09:00 ERROR 23916 --- [nio-8080-exec-6] o.n.a.t.d.u.controller.UserController : email field : only email@domain.com is allowed
2023-12-28T11:12:59.839+09:00 WARN 23916 --- [nio-8080-exec-6] .m.m.a.ExceptionHandlerExceptionResolver : Failure in @ExceptionHandler org.nbc.account.trollo.global.exception.GlobalExceptionHandler#handleIllegalArgumentException(IllegalArgumentException)
@Pattern(regexp = "^(.+)@(\\\\S+) $.", message = "only email@domain.com is allowed")
이게 잘못됐나?
2023-12-28T11:15:48.055+09:00 ERROR 23916 --- [nio-8080-exec-8] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.IllegalArgumentException: 닉네임 또는 패스워드를 확인해주세요.] with root cause
bindingResult 검증하는 메서드에서 filed Error 로그로 찍게해놔서 에러메시지가 나가니까 저게 잘못된게 맞는듯
⭐Regex 공부시간!
https://en.wikipedia.org/wiki/Regular_expression
@Pattern(regexp = "(^[a-zA-Z_0-9]*$)@(^[a-zA-Z_0-9]*$).(^[a-z]*$)", message = "only email@domain.com is allowed")
private String email;
→ 응 안됨
https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html
^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$
^[a-zA-Z_0-9]*@[a-zA-Z_0-9].[a-z]$
이렇게 만들어봄
{
"email" : "email3@dot.com",
"nickname" : "닉2",
"password" : "passpass",
"passwordCheck" : "passpass"
}
ㅋㅋ안됨
{
"status": 400,
"message": "이메일 또는 패스워드를 확인해주세요.",
"data": null
}
2023-12-28T12:44:43.586+09:00 ERROR 15528 --- [nio-8080-exec-3] o.n.a.t.d.u.controller.UserController : email field : only email@domain.com is allowed
그냥 DTO에 @email을 달아주면 알아서 이메일 주소 형식인지 Validation을 한다는 것을 알게되어서 그렇게 수정했더니 잘 검증된다!
⭐DTO Validation을 하는 두가지 방법
1. Controller layer에서 처리한다
controller의 method parameter 가@Valid 나 @Validated가 달린 @RequestBody나 @ModelAttribute 형식이어야 함
메서드에 BindingResult가 달려있지 않으면 BindingError가 발생했을때 MethodArgumentNotValidException를 발생시킨다
2. Bean의 method layer에서 처리한다
spring bean에 @Validated가 달려있어야 함
method argument에 bean validation annotaion만 달려있어야 함(@Valid, @Size 등)
AOP에 기반한 방법
method interceptor는 MethodValidationInterceptor이다
request와 response 모두를 validate할 수 있다
validation error가 발생하면 ConstraintViolationException을 throw한다
Interceptor로 하는거 더 찾아봤는데 많은 사람들이 규모가 커질수록 뭔가 문제가 발생할 확률이 높아지는 방법이라고 한다
Q. 왜?
application.yml에 정해진 값을 넣어놓고 가져와보기 해봄
# jwt
jwt:
secret:
key: 7Iqk7YyM66W07YOA7L2U65Sp7YG065+9U3ByaW5n6rCV7J2Y7Yqc7YSw7LWc7JuQ67mI7J6F64uI64ukLg==
expired:
period: 3600000
#60 * 60 * 1000
이렇게 넣어두고
@Value("${jwt.expired.period}")//token expiring period
private String expiredPeriod;
필요한 곳에서 이렇게 가져와서 씀!
그리고 여태까지는 Cookie에 jwt를 넣으면서 기간을 설정하지 않았었는데
그래서 항상 한참 지난다음에 Postman으로 로그인 하려하면 필터에서 걸러지고 this token is not valid 가 떴던걸까란 생각이 들었다
저거 하면서
return BEARER_PREFIX +
Jwts.builder()
.setSubject(email)
.setExpiration(new Date(date.getTime() + Long.parseLong(expiredPeriod)))
.setIssuedAt(date)
.signWith(key, signatureAlgorithm)
.compact();
Jwt는 Long을 더해줘야 하길래 Long을 더해주고
cookie.setMaxAge(Integer.parseInt(expiredPeriod));
Cookie는 int값을 넣어줘야 하길래 int를 넣어줬는데
받아올때부터 Long/Integer로 받아오는 방법이 있다고 한다!
내일 해봄
'개발일지' 카테고리의 다른 글
2024-01-03, Today I Learned (0) | 2024.01.03 |
---|---|
2024-01-02, Today I Learned (0) | 2024.01.02 |
2023-12-27, Today I Learned (0) | 2023.12.27 |
2023-12-26, Today I Learned (0) | 2023.12.26 |
2023-12-21, Today I Learned (1) | 2023.12.21 |