본문 바로가기

공부/Spring

스프링 부트 3 백엔드 개발자 되기_19

@SpringBootTest
public class TokenProviderTest {
    @Autowired
    private TokenProvider tokenProvider;
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private JwtProperties jwtProperties;

    //generateToken() 검증 테스트
    @DisplayName("generateToken(): 우저 정보와 만료 기간을 전달해 토큰을 만들 수 있다")
    @Test
    void generateToken() {
        //given  토큰에 유저 정보를 추가하기 위한 테스트 유저를 만듭니다
        User testUser = userRepository.save(User.builder()
                .email("user@gmail.com")
                .password("test")
                .build());
        //when 토큰 제공자의 generateToken() 메서드를 호출해 토큰을 만듭니다
        String token = tokenProvider.generateToken(testUser, Duration.ofDays(14));

        //then  jjwt 라이브러리를 통해 토큰 복호화,토큰만들 때 클레임으로 넣어둔 id값이 given절에서 만든 유저ID와 동일한지 확인
        Long userId = Jwts.parser()
                .setSigningKey(jwtProperties.getSecretKey())
                .parseClaimsJws(token)
                .getBody()
                .get("id", Long.class);

        assertThat(userId).isEqualTo(testUser.getId());
    }

    //validToken() 검증 테스트
    @DisplayName("validToken(): 만료된 토큰인 때에 유효성 검증에 실패한다.")
    @Test
    void validToken_invalidToken() {
        //given jjwt라이브러리를 사용해 토큰 생성,이때 만료시간은 1970년1.1부터 지금까지 시간을 밀리초 단위로 치환한 값에 1000을뺴 이미 만료된 토큰으로
        //생성
        String token = JwtFactory.builder()
                .expiration(new Date(new Date().getTime() - Duration.ofDays(7).toMillis()))
                .build()

                .createToken(jwtProperties);
        //when 토큰 제공자의 validToken() 메서드를 호출해 유효한 토큰인지 검증한 후 결과값 반환
        boolean result = tokenProvider.validToken(token);
        //then 반환값이 false(유효한 토큰이 아님)인 것을 확인
        assertThat(result).isFalse();
    }

    @DisplayName("validToken(): 유효한 토큰인 때에 유효성 검증에 성공한다.")
    @Test
    void validToken() {
        //given jjwt라이브러리를 사용해 토큰 생성, 만료시간은 현재 시간으로부터 14일 뒤로 만료되지 않은 토큰으로 생성
        String token = JwtFactory.withDefaultValues().createToken(jwtProperties);
        //when 토큰 제공자의 validToken()메서드를 호출해 유효한 토큰인지 검증한 뒤 결과값 반환
        boolean result = tokenProvider.validToken(token);
        //then 반환값이 true인(유효한 토큰)인 것을 확인
        assertThat(result).isTrue();
    }


    // getAuthentication() 검증 테스트
    @DisplayName("getAuthentication(): 토큰 기반으로 인증 정보를 가져올 수 있다.")
    @Test
    void getAuthentication() {
        //given jjwt라이브러리를 사용해 토큰을 생성, 토큰 제목인 subject는 "user@email.com"라는 값 사용
        String userEmail = "user@email.com";
        String token = JwtFactory.builder()
                .subject(userEmail)
                .build()
                .createToken(jwtProperties);
        //when 토큰 제공자의 getAuthentication() 메서드를 호출해 인증 객체를 반환받음
       Authentication authentication = tokenProvider.getAuthentication(token);
       //then 반환받은 인증 객체의 유저 이름을 가져와 given절에서 설정한 subject값인 "user@email.com"과 같은지 확인
        assertThat(((UserDetails) authentication.getPrincipal()).getUsername()).isEqualTo(userEmail);
    }

    //getUserId() 검증 테스트
    @DisplayName("getUserId(): 토큰으로 유저 ID를 가져올 수 있다.")
    @Test
    void getUserId(){
        //given jjwt라이브러리를 사용해 토큰을 생성, 이때 클레임을 추가한다 키는"id", 값은1이라는 유저 ID
        Long userId = 1L;
        String token = JwtFactory.builder()
                .claims(of("id", userId))
                .build()
                .createToken(jwtProperties);
        //when 토큰 제공자의 getUserId() 메서드를 호출해 유저 ID를 반환받음
        Long userIdByToken = tokenProvider.getUserId(token);

        //then 반환받은 유저 ID가 given절에서 설정한 유저 ID값인 1과 같은지 확인
        assertThat(userIdByToken).isEqualTo(userId);
    }
}

 

 

=> 돌렸는데 

ava.lang.IllegalArgumentException: Base64-encoded key bytes may only be specified for HMAC signatures.  If using RSA or Elliptic Curve, use the signWith(SignatureAlgorithm, Key) method instead.

 

오류남

 

https://velog.io/@layl__a/Spring-jwt-%EC%95%94%ED%98%B8%ED%99%94-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98

 

[Spring] jwt 암호화 알고리즘

jwt 토큰를 user에 적용하고 난 후 swagger에서 api test 를 하던 도중 "msg" : "Base64-encoded key bytes may only be specified for HMAC signatures. If using RSA or Ellipti

velog.io

참고해서 찾아봤더니

JwtFactory 클래스에 토큰생성하는 부분에서 HS256  가아니라 ES256를 쓰고 있었음
//수정전
//jjwt 라이브러리를 사용해 JWT 토큰 생성
    public String createToken(JwtProperties jwtProperties) {
        return Jwts.builder()
                .setSubject(subject)
                .setHeaderParam(Header.TYPE, Header.JWT_TYPE)
                .setIssuer(jwtProperties.getIssuer())
                .setIssuedAt(issuedAt)
                .setExpiration(expiration)
                .addClaims(claims)
                .signWith(SignatureAlgorithm.ES256, jwtProperties.getSecretKey())
                .compact();
    }

//수정후
//jjwt 라이브러리를 사용해 JWT 토큰 생성
    public String createToken(JwtProperties jwtProperties) {
        return Jwts.builder()
                .setSubject(subject)
                .setHeaderParam(Header.TYPE, Header.JWT_TYPE)
                .setIssuer(jwtProperties.getIssuer())
                .setIssuedAt(issuedAt)
                .setExpiration(expiration)
                .addClaims(claims)
                .signWith(SignatureAlgorithm.HS256, jwtProperties.getSecretKey())
                .compact();
    }

 

 

수정했더니  //generateToken() 검증 테스트 빼고는 전부 성공했음

 

근데 여기도 같은 오류가 떠서 java.lang.IllegalArgumentException: Base64-encoded key bytes may only be specified for HMAC signatures.  If using RSA or Elliptic Curve, use the signWith(SignatureAlgorithm, Key) method instead.

 

TokenProvider 클래스에 토큰 생성하는 메서드에서 HS256으로 바꿨더니 됨
 
  //JWT 토큰 생성 메서드
    private String makeToken(Date expiry, User user){
        Date now = new Date();

        return Jwts.builder()
                .setHeaderParam(Header.TYPE, Header.JWT_TYPE) //헤더 typ : JWT
                //내용 iss : ajufresh@gmail.com(propertise 파일에서 설정한 값)
                .setIssuer(jwtProperties.getIssuer())
                .setIssuedAt(now)   //내용 iat: 현재 시간
                .setExpiration(expiry)  //내용 exp : expiry 맴버 변수값
                .setSubject(user.getEmail())  //내용 sub : 유저의 이메일
                .claim("id", user.getId())  //클레임 id : 유저 id
                //서명 : 비밀값과 함께 해시값을 HS256방식으로 암호화
                .signWith(SignatureAlgorithm.HS256, jwtProperties.getSecretKey())
                .compact();
    }

 

 

9.2.4 토큰 필터 구현

 

필터는 실제로 각종 요청이 요청을 처리하기 위한 로직으로 전달되기 전후에 URL 패턴에 맞는 모든 요청을 처리하는 기능을 제공

요청이 오면 헤더값을 비교해서 토큰이 있는지 확인하고 유효 토큰이라면 시큐리티 콘텍스트 홀더에 인증 정보를 저장

 

시큐리티 홀더: 인증 객체(시큐리티 컨텍스트 객체)가 저장되는 보관소

    여기서 인증정보가 필요할 때 언제든지 인증 객체를 꺼내 사용 가능

     이 클래스(시큐리티 컨텍스트 객체)는 스레드마다 공간을 할당하는 즉 스레드 로컬에 저장돼서 코드의 아무 곳에서나 참조할 수 있고 다른 스레         드와 공유하지 않아 독립적으로 사용할 수 있다.