Light Blue Pointer
본문 바로가기
Developing/개발일지

2023-12-28, Today I Learned

by Greedy 2023. 12. 28.

오늘 한 일

회원가입, 로그인 기능 개발

코드 리뷰받고 수정해보기!

 

알고리즘 스터디

 

회원가입 기능 개발중 ...

 

⭐생성자 대신 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을 한다는 것을 알게되어서 그렇게 수정했더니 잘 검증된다!

 

⭐ @email

 

⭐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로 받아오는 방법이 있다고 한다!

내일 해봄

'Developing > 개발일지' 카테고리의 다른 글

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