Giới thiệu JWT

Trong bài viết này, chúng ta sẽ tìm hiểu về cách sử dụng JWT (JSON Web Token) trong một ứng dụng Spring Boot.

1. Cơ bản về JWT

JWT là một tiêu chuẩn mở cho phép trao đổi thông tin an toàn giữa các hệ thống. Mỗi token JWT bao gồm ba phần được phân cách bởi dấu chấm (.):

  • Header: chứa thông tin về loại token và thuật toán ký
  • Payload: chứa thông tin người dùng và các claim
  • Signature: đảm bảo token không bị sửa đổi và xác thực nguồn gốc

2. Cấu trúc và hoạt động

Khi một người dùng đăng nhập, hệ thống tạo ra một token JWT và trả về cho客户端. Client gửi lại token này trong mỗi yêu cầu để xác thực. Server kiểm tra token và thực hiện các hành động tương ứng.

3. Thư viện và cấu hình

Chúng ta sử dụng thư viện java-jwt để xử lý JWT:

<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>2.2.0</version>
</dependency>

4. Code ví dụ

4.1. Tạo token JWT

Code sau đây minh họa cách tạo token JWT:

package com.yxg.security;

import com.auth0.jwt.JWTSigner;
import com.auth0.jwt.JWTVerifier;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.HashMap;
import java.util.Map;

public class JWTUtil {

private static final String SECRET = "abc123";
private static final String EXP = "exp";
private static final String PAYLOAD = "payload";

public static <T> String generateToken(T userInfo, long expiration) {
try {
JWTSigner signer = new JWTSigner(SECRET);
Map claims = new HashMap<>();
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeValueAsString(userInfo);
claims.put(PAYLOAD, jsonString);
claims.put(EXP, System.currentTimeMillis() + expiration);
return signer.sign(claims);
} catch (Exception e) {
return null;
}
}

public static <T> T validateToken(String token, Class type) {
try {
JWTVerifier verifier = new JWTVerifier(SECRET);
Map claims = verifier.verify(token);
if (claims.containsKey(EXP) && claims.containsKey(PAYLOAD)) {
long expTime = (Long) claims.get(EXP);
if (expTime > System.currentTimeMillis()) {
String jsonPayload = (String) claims.get(PAYLOAD);
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(jsonPayload, type);
}
}
} catch (Exception e) {
return null;
}
return null;
}
}

4.2. Sử dụng interceptor để kiểm tra token

Định nghĩa interceptor để kiểm tra token JWT:

@Configuration
public class WebConfig implements WebMvcConfigurer {

@Autowired
private TokenValidator tokenValidator;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenValidator)
.addPathPatterns("/**")
.excludePathPatterns(
"/api/v1/auth/**",
"/api/v1/login",
"/api/v1/register"
);
}
}

Thực hiện kiểm tra token:

@Component
public class TokenValidator implements HandlerInterceptor {

@Autowired
private RedisService redisService;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (token == null || token.isEmpty()) {
return false;
}

// Kiểm tra token
UserInformation userInfo = JWTUtil.validateToken(token, UserInformation.class);
if (userInfo == null) {
response.setStatus(401);
return false;
}

// Kiểm tra Redis
String redisKey = userInfo.getType() + userInfo.getId();
UserInformation storedInfo = redisService.getValueFromRedis(redisKey);
if (storedInfo == null || !storedInfo.getToken().equals(token)) {
response.setStatus(403);
return false;
}

request.setAttribute("user", userInfo);
return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// TODO
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// TODO
}
}

Ví dụ token JWT:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2Mzg1ODA0MDI5OTEsInBheWxvYWQiOiJ7XCJpZFwiOjEsXCJ0b2tlblwiOm51bGwsXCJ0eXBlXCI6XCJtYW5hZ2VyX1wiLFwidXJsXCI6bnVsbH0ifQ.za9jMT8GDflhF0is_qim5FZ7spTIQbwAWCqqjeYW_AU

Token được tạo từ:

  • Header:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
  • Payload:eyJleHAiOjE2Mzg1ODA4MDI5OTMsInBheWxvYWQiOiJ7XCJpZFwiOjIsXCJ0b2tlblwiOm51bGwsXCJ0eXBlXCI6XCJ1c2VyX1wiLFwidXJsXCI6bnVsbH0ifQ
  • Signature:za9jMT8GDflhF0is_qim5FZ7spTIQbwAWCqqjeYW_AU

Thẻ: JWT SpringBoot Token Redis

Đăng vào ngày 10 tháng 6 lúc 01:01