이번 시간에는 저번에 JWT로 바꾼 password를 다시 원상태로 뽑아올려고 한다.
JWT 분석
eyJ0eXBlIjoiand0IiwiYWxnIjoiSFMyNTYifQ.
eyJwYXNzd29yZCI6IjEyYWJjZCIsImlzcyI6Ildvb25nIiwiaWF0IjoxNjc1NjAyNzE2LCJleHAiOjE2NzcwNzM5NDV9.
Y-gvsHLXdI8Ug2QoBalRuE2cE6jh08GbzwUnY7GIWGs
위의 token은 어제 내가 만든 JWT이다. 위에서부터 Header, Payload, Signature이다.
내가 뽑아 와야 할 password는 Payload에 암호화 되어 있다. 그래서 Payload에서 Claim 형태로 빼올 것이다.
우리는 이것을 Jws의 Claim 형식으로 빼올 것이다.
JWT는 Jws와 Jwe로 나뉜다.
JWS란?
- Header.Payload.Signature 구조를 가지는 우리가 흔히알고 있는 JWT이다.
JWE란?
- JWS는 Claims에 대한 정보를 암호화 하지 않기 때문에 사용자 정보가 누출될 수 있다. 이를 개선하기 위해 데이터를 암호화하는 것이 JWE다.
이 2개를 구분하는 방법은
- JWS는 3개로 분류되고, JWE는 5개로 분류 되기 때문에 각각 '.'문자가 2개, 4개 존재한다.
- Header의 arg 파라미터 값이 JWS는 RSA, SHA256 등이 존재하지만 JWE는 Key Encryption, Key Wrapping, Direct Key Agreement를 나타냅니다.
내가 만든 것은 JWS이기 때문에 JWS 형식에 맡게 디코딩 해줄 것이다.
JwtService
public String getUserPassword(String jwt) throws BaseException {
String accessToken = jwt;
if(accessToken == null || accessToken.length() == 0){
throw new BaseException(BaseResponseStatus.EMPTY_JWT);
}
Jws<Claims> claims;
try{
claims = Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(accessToken);
}catch(Exception exception){
throw new BaseException(BaseResponseStatus.INVALID_JWT);
}
return claims.getBody().get("password", String.class);
}
JwtService에 이 메소드를 추가한다. 만약 jwt가 null이거나 없으면 Empty_JWT 에러를 발생시킨다.
Jws<Claims> claims를 통해 내용을 빼올 것 이다.
위 코드는 링크 이 곳을 참고했다.
- Jwts.parserBuilder() : JwtParseBuilder인스턴스를 생성한다.
- .setSigningKey(key) : JWS 서명 검증을 위한 SecretKey 혹은 비대칭 PublicKey를 지정한다.
- .build() : 스레드에 안전한 JwtPaser를 리턴하기 위해 JwtPaserBuilder의 build()메서드를 호출한다.
- .parseClimsJws: 원본 JWS를 생성하는 jws를 가지고 parseClaimsJws(String)메서드를 호출한다.
- 파싱이나 서명검증오류 경우에 try/catch구문으로 예외를 잡는다.
그 후 claims의 body에서 "password"와 매칭되는 것을 찾는다. 이전 글에서 말했듯이 claims는 Map형식으로 되어있기 때문에 "password"가 Key고 그에 대한 값이 value이다.
MemberService
- MemberGetRes
@Data
public class MemberGetRes {
private String email;
private String phoneNumber;
private String name;
private String password;
public MemberGetRes(Member member, String password){
this.email = member.getEmail();
this.phoneNumber = member.getPhoneNumber();
this.name = member.getName();
this.password = password;
}
}
/**
* Member 조회
*/
public List<MemberGetRes> selectMember(){
List<Member> members = memberRepository.findAll();
return members.stream()
.map(member -> {
try {
return new MemberGetRes(member, jwtService.getUserPassword(member.getPassword()));
} catch (BaseException e) {
throw new RuntimeException(e);
}
})
.collect(Collectors.toList());
}
member List를 하나 만들어 각각 매칭 해준다. 이때 password는 jwt decode를 해줘야 하므로 위에 만든 메소드로 디코드 해준 뒤 MemberGetRes에 맞춰준다.
MemberController
@GetMapping("/user")
public BaseResponse<List<MemberGetRes>> selectMember(){
return new BaseResponse<>(memberService.selectMember());
}
실행
JWT 토큰이 아닌 내가 이전 글에서 내가 적었던 비밀번호가 그대로 출력된다!!
'혼자하는 프로젝트 > 나만의 프로젝트' 카테고리의 다른 글
Docker로 CI/CD 구축하기 (0) | 2023.06.20 |
---|---|
데이터베이스 정규화 하기 (2) | 2023.04.29 |
OAuth2.0 이란? (2) | 2023.02.07 |
SpringBoot를 이용한 이메일 인증 하기 (0) | 2023.02.06 |
Spring Boot와 JWT를 이용한 회원 가입(1) (0) | 2023.02.05 |