|
JWT 단점
Cookie & Session vs JWT 장단점
Cookie와 Session | Cookie만 사용하는 방식보다 보안 향상 서버 쪽에서 Session 통제 가능 네트워크의 부하가 낮음 | 세션 저장소 사용으로 인한 서버의 부하 |
JWT | 인증을 위한 별도의 저장소가 필요 없다. 빠른 인증 처리 확장성 우수 | 토큰의 길이가 길수록 네트워크의 부하가 증가, 특정 토큰을 강제로 만료시키기가 어렵다. |
JWT와 세션 기반 인증의 비교
^ 무상태 vs 상태 유지
JWT: 서버는 무상태이다. 클라이언트가 모든 요청에 JWT를 포함하므로 서버는 상태를 유지할 필요가 없다.
세션: 서버는 상태를 유지한다. 서버는 세션 저장소에 클라이언트의 세션 정보를 저장한다.
^ 확장성
JWT: 서버 간 확장성이 좋다. 여러 서버에서 JWT를 동일한 비밀키로 검증할 수 있다.
세션: 상태를 공유해야 하므로 서버 간 확장이 복잡할 수 있다.
^ 보안
JWT: 토큰이 클라이언트에 저장되므로 XSS 공격에 취약할 수 있다. 토큰 탈취 시 위험하다.
세션: 세션 ID는 서버에 저장되며, 세션 탈취 공격(CSRF- Cross-Site Request Forgery, 요청 위조 공격 )에 취약할 수 있다.
JWT 생성 서블릿 샘플 ---------------------------------- ----------------------------------
// 사용자 인증 후 JWT 생성
String username = request.getParameter("username");
String SECRET_KEY = "your-256-bit-secret";
// 토큰 생성
String jwt = Jwts.builder()
.setSubject(username) // 토큰 용도(제목)
.setIssuedAt(new Date()) // 생성 시간 설정
.setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 토큰 만료 시간 설정. 1시간 유효
.signWith(SignatureAlgorithm.HS256, SECRET_KEY) // HS256과 Key로 Sign
.compact(); // 토큰 생성
// 응답에 JWT 포함
response.setContentType("application/json");
response.getWriter().write("{\"token\": \"" + jwt + "\"}");
// 토큰 검증
String token = request.getHeader("Authorization").replace("Bearer ", "");
try {
Claims claims = Jwts.parser() // JWT를 파싱하는 JwtParser 객체를 생성
.setSigningKey(SECRET_KEY) // JWT의 서명 검증을 위해 JwtParser에 JWT 생성 때 사용된 비밀 키와 동일한 비밀 키 설정.
.parseClaimsJws(token) // 주어진 토큰을 파싱하고, 서명확인하여 JWT의 클레임들을 추출, 토큰 유효성 검사 진행.
.getBody(); // Jws 객체에서 페이로드를 추출하여 반환. 페이로드에는 JWT에 포함된 클레임들이 포함되어 있다.
// 파싱 parsing : JWT를 파싱하는 과정은 토큰의 구성을 해석하고, 그 내용을 이해하여 필요한 정보를 추출하는 것.
String username = claims.getSubject();
// 응답에 사용자 정보 포함
response.setContentType("application/json");
response.getWriter().write("{\"username\": \"" + username + "\"}");
} catch (SignatureException e) {
// 토큰 검증 실패
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("{\"error\": \"Invalid token\"}");
}
---------------------------------- ---------------------------------- ----------------------------------
*** 샘플 ***
jwt 실행 라이브러리
// 고정된 비밀 키 사용 (예제용) 최소 256비트 길이의 비밀 키
String secretKeyString = "mySuperSecretKey12345678901234567890123456789012";
Key secretKey = Keys.hmacShaKeyFor(secretKeyString.getBytes());
고정된 비밀 키( secretKeyString )를 자동으로 생성하려 한다면 기존 소스를 아래와 같은 형태로 변경 해 준다.
InitServlet.java <== 새로 작성
package pack;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import javax.crypto.SecretKey;
import javax.crypto.KeyGenerator;
import java.util.Base64;
@WebServlet(loadxxxxOnStartup = 1)
public class InitServlet extends HttpServlet {
public void init() throws ServletException {
try {
// 비밀 키 생성
KeyGenerator keyGen = KeyGenerator.getInstance("HmacSHA256");
keyGen.init(256); // 256비트 길이의 키 생성
SecretKey secretKey = keyGen.generateKey();
/*
실제 비밀 키는 KeyGenerator 객체가 생성한 후 SecretKey 객체로 반환된다.
이 SecretKey 객체는 메모리에 존재하며, 파일이나 데이터베이스와 같은 외부 저장소에
저장하려면 명시적으로 저장하는 코드를 작성해야 한다.
*/
// 비밀 키를 Base64로 인코딩하여 문자열로 변환 java.util.Base64
String encodedKey = Base64.getEncoder().encodeToString(secretKey.getEncoded());
System.out.println("encodedKey : " + encodedKey);
// 서블릿 컨텍스트에 비밀 키 저장
getServletContext().setAttribute("secretKey", encodedKey);
} catch (Exception e) {
throw new ServletException("키 생성 오류", e);
}
}
}
web.xml에 아래 내용 추가
<servlet>
<servlet-name>InitServlet</servlet-name>
<servlet-class>pack.InitServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
login.jsp , success.jsp를 수정
// 고정된 비밀 키 사용 (예제용) 최소 256비트 길이의 비밀 키
// String secretKeyString = "mySuperSecretKey12345678901234567890123456789012";
// Key secretKey = Keys.hmacShaKeyFor(secretKeyString.getBytes());
// 위의 작업을 주석 처리하고 아래 내용으로 변경하자.
// 서블릿 컨텍스트에서 Base64로 인코딩된 비밀 키 가져오기 java.util.Base64
String encodedKey = (String) getServletContext().getAttribute("secretKey");
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
Key secretKey = Keys.hmacShaKeyFor(decodedKey);
참고 : SpringBoot 프로젝트에서 Security 없이 JWT 만 사용해 작업하고자 한다면 build.gradle에 아래와 같이 jjwt를 추가해 준다.
implementation 'io.jsonwebtoken:jjwt-api:0.11.2'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.2'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.2'
세션과 DB 연습문제 -----------------------------------------------------------------------
|
첫댓글 내가만든 쿠키~~
너를 위해 구워찌~