JWT 토큰이란?
JWT는 정보를 안전하게 주고받을 수 있는 방식을 정의한 표준이에요.
사용자 정보를 담은 암호화된 토큰으로 Header 와 Payload 그리고 Signature 부분으로 나뉩니다.
해당 토큰을 유저에게 발급해 준다면, 해당 토큰이 유효한 기간 동안 서버는 토큰의 유효성만 검증하면 로그인을 허가 해 줄 수 있고, db에 접근하여 암호를 대조하는 비용을 줄일 수 도 있고, 실제 암호가 통신 환경에 많이 노출되는 것을 막아 주므로 로그인 인증으로 널리 사용되는 기술 입니다.
JWT 토큰 발행 방법.
우선 의존성 추가 부터 진행해 줍니다.
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.6</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>
이제 웹에서 로그인 요청이 post 형식으로 들어왔다고 생각해 보고, 이를 처리해줄 컨트롤러를 먼저 생성해 줍니다.
@PostMapping("/login")
@Operation(summary = "사용자 로그인 ", description = "로그인을 합니다.")
public ResponseEntity<?> logIn(@RequestBody User user) {
try {
TokenInfo tokenInfo = authService.login(user);
//RefreshToken refreshToken = new RefreshToken();
//refreshToken.setRefreshToken(tokenInfo.getRefreshToken());
return new ResponseEntity<>(tokenInfo, HttpStatus.OK);
} catch (IllegalArgumentException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.UNAUTHORIZED);
}
}
여기서 TokenInfo 는 JWT 토큰을 담을 객체입니다. 추후에 refrash 토큰이나 다양한 정보를 추가하기 위해서도 필요합니다. 이제 이 tokenInfo 를 생성해 줄 authService 객체를 만들어 줍니다.
public TokenInfo login(User user) {
System.out.println(user);
User loginUser = null;
if (user.getUserEmail() == null || user.getUserPassword() == null)
throw new IllegalArgumentException("로그인 입력 정보 없음");
// 입력된 이메일 날림 => user 정보 받아오기
User getUser = userService.searchByEmail(user.getUserEmail());
System.out.println(getUser);
// 아이디 또는 비밀번호 없음
String encodePw = getUser.getUserPassword();
if (getUser == null || !passwordEncoder.matches(user.getUserPassword(), encodePw)) { // user가 입력한 pw와 db의 암호화된
throw new IllegalArgumentException("아이디 또는 비밀번호 틀림");
}
// 로그인 성공
loginUser = getUser;
// 회원가입 정보가 맞을 경우, 로그인 요청 시 => JWT 발급
return jwtUtil.createTokenInfo(loginUser);
}
user 를 email로 조회 한 후, 해당하는 유저가 있을 때 암호화된 비밀번호의 유효성을 판단하는 코드에요.
passwordEncoder 라는 객체의 의존성 주입이 필요합니다. 해당 비밀번호의 유효성을 검증 받으면 해당 user 객체를 createTokenInfo 메서드로 보냅니다.
public TokenInfo createTokenInfo(User user) {
// access-token 생성
String accessToken = createAccessToken(user);
// refresh-token 생성
//String refreshToken = createRefreshToken(user);
TokenInfo tokenInfo = new TokenInfo();
//tokenInfo.setGrantType("Bearer");
tokenInfo.setAccessToken(accessToken);
//tokenInfo.setRefreshToken(refreshToken);
return tokenInfo;
}
이제 TokenInfo 라는 token 을 담은 객체를 생성하는데요. refrash token 이나 Grant type 에 대해서는 다음시간에 공부해 보겠습니다. 이제 createAccessToken 으로 가볼게요.
// 2. 토큰을 생성하는 메서드
public String createToken(User user, long expirationTimeMillis) {
String userNickname = user.getUserNickname();
int userId = user.getUserId();
// 토큰 유효기간 설정
Date exp = new Date(System.currentTimeMillis() + expirationTimeMillis);
return Jwts.builder().header().add("type", "JWT").and().claim("userNickname", userNickname)
.claim("userId", userId).expiration(exp).signWith(secretKey).compact();
}
// access-token 생성
public String createAccessToken(User user) {
return createToken(user, 1000 * 60); // 1분
}
이제 여기서 생성되는 access-token 이 앞서 말씀드린 JWT 토큰 입니다. jwt 토큰에 해당 user 정보와 만료 시간에 대한 정보를 넣어주는 것이 header 부분과 payload 부분입니다. 토큰에 저희가 원하는 정보를 임의로 추가 할 수 있습니다. 이제 토큰 발급에 Jwts.builder() 를 이용하는데, claim 메서드를 통해 payload 부분에 key-value 형태를 통해 원하는 정보를 추가합니다. 이제 여기서 중요한 Signature 부분이 있는데요.
서버에서 토큰의 유효성을 검증하기 위한 방법으로 jwt 를 발급하는 서버는 노출되지 않는 암호문을 하나 들고 있어야 합니다.
/* 토큰 SECRET KEY */
@Value("${JWT_SECRET_KEY}")
private String JWT_SECRET_KEY;
private SecretKey secretKey;
// 1. 시크릿키를 생성하는 메서드
// @PostConstruct로 secretKey 초기화
// Spring Bean의 초기화 후, 딱 한 번만 실행되는 메서드를 지정할 때 사용
@PostConstruct
private void initSecretKey() {
byte[] keyBytes = Decoders.BASE64.decode(JWT_SECRET_KEY); // deprecated 메서드 새로 작성 방법
this.secretKey = Keys.hmacShaKeyFor(keyBytes);
}
JWT_SECRET_KEY 는 저희가 설정하는 암호로, git으로 공유되지 않도록 환경변수 처리를하여 @Value 어노테이션으로 주입받아 처리 했습니다. 어쨋든, 저 key 를 바탕으로 secretkey를 암호화 하여 생성합니다.
그리고 그 secretkey 를 jwt 생성 시에 signature 메서드에 파라미터로 넘겨주면, 해당 토큰에서 secretkey에 해당하는 서명을 생성하여 토큰에 넣어줍니다. 따라서 추후에 서버는 signature 부분의 서명 유효성을 검증하여, 서버가 가지고 있는 secretkey 와 동일한지의 여부를 판단하여 유저들의 접근 허가를 해줄 수 있습니다.
이렇게 생성된 TokenInfo 를 ResponseEntitiy 에 담아 반환해 주면, front 에서 해당 accesstoken 을 session 에 담아두어 추후의 모든 요청의 header 에 access-token 을 넣어 요청을 보내면 login 된 유저임을 서버에서 쉽게 확인할 수 있습니다.
또한 javaScript 를 통해 해당 토큰의 payLoad 부분을 디코딩 하여, 서버가 보내준 정보를 페이지 렌더링에 이용할 수 도 있습니다.
'Project' 카테고리의 다른 글
[JPA] Entity 설계하기. (2) | 2025.01.27 |
---|---|
[프로젝트 Day 5] OAuth2 로그인 (Google) (1) | 2024.11.19 |
[프로젝트 Day 3] Youtube API 발급받기 및 데이터 추출하기. (11) | 2024.11.07 |
[Day 4] 프로젝트 기획 및 DB 설계하기 (7) | 2024.11.05 |
[프로젝트 Day 2] EC2 인스턴스에서 스프링 부트 프로젝트 배포하기. + MYSQL 설치 및 연결하기. (0) | 2024.10.24 |