WEB개념
[WEB] JWT vs JSESSIONID
wooyeon06
2022. 3. 16. 09:03
JWT
: JWT(JSON Web Token)란 인증에 필요한 정보들을 암호화시킨 토큰을 의미합니다. JWT 기반 인증은 쿠키/세션 방식과 유사하게 JWT 토큰(Access Token)을 HTTP 헤더에 실어 서버가 클라이언트를 식별합니다.
JWT 구조
JWT는 .을 구분자로 나누어지는 세 가지 문자열의 조합입니다. 실제 디코딩된 JWT는 다음과 같은 구조를 지닙니다.
Header |
Header는 alg과 typ는 각각 정보를 암호화할 해싱 알고리즘 및 토큰의 타입을 지정합니다. |
|
Payload |
Payload는 토큰에 담을 정보를 지니고 있습니다. 주로 클라이언트의 고유 ID 값 및 유효 기간 등이 포함되는 영역입니다. key-value 형식으로 이루어진 한 쌍의 정보를 Claim이라고 칭합니다. |
단점
- 쿠키/세션과 다르게 JWT는 토큰의 길이가 길어, 인증 요청이 많아질수록 네트워크 부하가 심해집니다.
- Payload 자체는 암호화되지 않기 때문에 유저의 중요한 정보는 담을 수 없습니다.
- 토큰을 탈취당하면 대처하기 어렵습니다.
- 토큰은 한 번 발급되면 유효기간이 만료될 때 까지 계속 사용이 가능하기 때문입니다.
- 특정 사용자의 접속을 강제로 만료하기 어렵지만, 쿠키/세션 기반 인증은 서버 쪽에서 쉽게 세션을 삭제할 수 있습니다.
application.properties
jwt.expiration_time=86400000
jwt.secret=VlwEyVBsYt9V7zq57TejMnVUyzblYcfPQye08f7MGVA9XkHa
build.gradle
implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.12.5'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.12.5'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.12.5'
JAVA
/**
* [JWT 관련 메서드를 제공하는 클래스]
*/
@Log4j2
@Component
public class JWTUtil {
private final Key key;
private final long accessTokenExpTime;
public JWTUtil(
@Value("${jwt.secret}") String secretKey,
@Value("${jwt.expiration_time}") long accessTokenExpTime
) {
byte[] keyBytes = Decoders.BASE64.decode(secretKey);
this.key = Keys.hmacShaKeyFor(keyBytes);
this.accessTokenExpTime = accessTokenExpTime;
}
/**
* Access Token 생성
* @param user
* @return Access Token String
*/
public String createAccessToken(UserInfo user) {
return createToken(user, accessTokenExpTime);
}
/**
* JWT 생성
* @param user
* @param expireTime
* @return JWT String
*/
private String createToken(UserInfo user, long expireTime) {
Map<String, Object> claims = new HashMap<>();
claims.put("userId", user.getUserId());
claims.put("userName", user.getUserName());
ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime tokenValidity = now.plusSeconds(expireTime);
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(Date.from(now.toInstant()))
.setExpiration(Date.from(tokenValidity.toInstant()))
.signWith(key, SignatureAlgorithm.HS256)
.compact();
}
/**
* Token에서 User ID 추출
* @param token
* @return User ID
*/
public String getUserId(String token) {
return parseClaims(token).get("userId", String.class);
}
/**
* JWT 검증
* @param token
* @return IsValidate
*/
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(key).build().parseClaimsJws(token);
return true;
} catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {
log.info("Invalid JWT Token", e);
} catch (ExpiredJwtException e) {
log.info("Expired JWT Token", e);
} catch (UnsupportedJwtException e) {
log.info("Unsupported JWT Token", e);
} catch (IllegalArgumentException e) {
log.info("JWT claims string is empty.", e);
}
return false;
}
/**
* JWT Claims 추출
* @param token
* @return JWT Claims
*/
public Claims parseClaims(String token) {
try {
return Jwts.parser().setSigningKey(key).build().parseClaimsJws(token).getBody();
} catch (ExpiredJwtException e) {
return e.getClaims();
}
}
}
@Log4j2
@RestController
@RequiredArgsConstructor
@RequestMapping("/jwt")
public class LoginController {
private final JWTUtil jwtUtil;
@RequestMapping("/login")
public ResponseEntity<String> login(@RequestBody HashMap<String, Object> data) {
String userId = (String) data.get("id");
String userPassword = (String) data.get("pw");
//로그인 과정 패스....
UserInfo userInfo = new UserInfo();
userInfo.setUserName("Wooyeon");
userInfo.setUserId(userId);
userInfo.setUserPassword(userPassword);
String token = jwtUtil.createAccessToken(userInfo);
return ResponseEntity.status(HttpStatus.OK).body(token);
}
@RequestMapping("/validate")
public ResponseEntity<String> validate(HttpServletRequest request) {
String authorizationHeader = request.getHeader("Authorization");
//JWT가 헤더에 있는 경우
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
String token = authorizationHeader.substring(7);
log.info("token..:" + token);
//JWT 유효성 검증
if (jwtUtil.validateToken(token)) {
String userId = jwtUtil.getUserId(token);
log.info("jwt login userId .... : " + userId);
} else {
log.info("jwt validate token fail");
}
}
return ResponseEntity.status(HttpStatus.OK).body("SUCCESS");
}
}
Cookie
: 쿠키란 클라이언트가 어떠한 웹사이트를 방문할 경우, 그 사이트가 사용하고 있는 서버를 통해 클라이언트의 브라우저에 설치되는 작은 기록 정보 파일을 일컫습니다.
MockHttpServletResponse:
Status = 200
Headers = [Set-Cookie:"userName=kevin", "password=abc123"]
- 서버는 클라이언트의 로그인 요청에 대한 응답을 작성할 때, 클라이언트 측에 저장하고 싶은 정보를 응답 헤더의 Set-Cookie에 담습니다.
- 쿠키는 Key-Value 형식의 문자열입니다.
MockHttpServletRequest:
HTTP Method = GET
Request URI = /user/my/edit
Headers = [Cookie:"userName=kevin"; "password=abc123"]
- 이후 해당 클라이언트는 요청을 보낼 때마다, 매번 저장된 쿠키를 요청 헤더의 Cookie에 담아 보냅니다.
- 서버는 쿠키에 담긴 정보를 바탕으로 해당 요청의 클라이언트가 누군지 식별할 수 있습니다.
단점
- 보안에 취약합니다.
- 요청 시 쿠키의 값을 그대로 보냅니다.
- 유출 및 조작 당할 위험이 존재합니다.
- 쿠키에는 용량 제한이 있어 많은 정보를 담을 수 없습니다.
- 웹 브라우저마다 쿠키에 대한 지원 형태가 다르기 때문에 브라우저간 공유가 불가능합니다.
- 쿠키의 사이즈가 커질수록 네트워크에 부하가 심해집니다.
JAVA
@GetMapping("/getCookie")
public String getCookie(HttpServletRequest request,
@CookieValue(value = "hi") Cookie cookie) {
Cookie[] cookies = request.getCookies();
Arrays.asList(cookies).stream()
.forEach(c -> log.debug(c.getName() + ":" + c.getValue()));
log.debug("cookie :{}", cookie.getValue());
return "getCookie";
}
http://www.opennaru.com/opennaru-blog/jwt-json-web-token/
https://tecoble.techcourse.co.kr/post/2021-05-22-cookie-session-jwt/
https://sjh9708.tistory.com/170