在 Spring 应用中,使用 JSON Web Token (JWT) 是一种常见的认证和授权机制。JWT 是一种基于 JSON 的开放标准 (RFC 7519),用来在各方之间传递安全、可信的数据。以下是如何在 Spring 框架中集成和使用 JWT 的完整指南。
核心概念
JWT 结构:
- Header:包含类型和签名算法信息。
- Payload:包含声明 (claims),如用户信息、角色等。
- Signature:用来验证 token 的完整性,通常使用 HMAC 或 RSA 等算法。
使用场景:
- 用户登录后,服务端生成 JWT 并返回给客户端。
- 客户端将 JWT 存储在本地(如浏览器的 LocalStorage 或 HttpOnly Cookie 中)。
- 客户端每次请求时,将 JWT 添加到请求头中,服务端验证后授权访问。
JWT 的使用通常分为以下几个步骤:
- 用户通过用户名和密码登录,后端验证用户身份。
- 后端生成 JWT(包含用户的相关信息),并将其返回给前端。
- 前端存储该 JWT(一般存储在 LocalStorage 或 Cookie 中)。
- 后续每次请求,前端都会将该 JWT 放入请求头的
Authorization
字段中,后端验证 JWT 的合法性。 - 后端根据 JWT 中的内容进行相应的认证和授权。
集成步骤
以下是 Spring Boot 项目中使用 JWT 的主要步骤:
1. 添加依赖
在 pom.xml
中添加 JWT 相关依赖:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
1. 创建 JWT 工具类
首先,创建一个 JWT 工具类用于生成和验证 JWT。
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JwtUtil {
private static final String SECRET_KEY = "your_secret_key"; // 密钥
// 生成安全密钥
private final static SecretKey secretKey =
Keys.hmacShaKeyFor(Decoders.BASE64.decode(SECRET_KEY));
// 过期时间(单位:毫秒)
private static final long EXPIRATION = 3600000; // 一小时
// 生成JWT
public static String generateToken(String username, Integer id) {
Map<String, Object> claims = new HashMap<>();
claims.put("username", username); // 可以根据需要加入更多的自定义字段
claims.put("userId", id);
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION)) // 过期时间1小时
.signWith(secretKey) // 签名
.compact();
}
// 解析JWT
public static Claims parseToken(String token) throws JwtException {
try {
JwtParserBuilder jwtParserBuilder = Jwts.parserBuilder().setSigningKey(secretKey);
return jwtParserBuilder
.build()
.parseClaimsJws(token)
.getBody();
} catch (JwtException e) {
// 捕获解析异常,抛出一个自定义的异常或返回null
throw new JwtException("Invalid token: " + e.getMessage(), e);
}
}
// 检查JWT是否有效
public static boolean isTokenExpired(String token) {
try {
Claims claims = parseToken(token);
return claims.getExpiration().before(new Date());
} catch (JwtException e) {
// 解析失败,视为Token无效
return true;
}
}
// 获取用户名
public static String getUsername(String token) {
try {
Claims claims = parseToken(token);
return claims != null ? claims.get("username", String.class) : null;
} catch (JwtException e) {
// Token无效,返回null或可以选择抛出异常
return null;
}
}
// 获取用户id
public static Integer getUserId(String token) {
try {
Claims claims = parseToken(token);
return claims != null ? claims.get("userId", Integer.class) : null;
} catch (JwtException e) {
// Token无效,返回null或可以选择抛出异常
return null;
}
}
}
2. 创建 JWT 拦截器
接下来,创建一个 HandlerInterceptor
实现 JWT 登录验证逻辑。这个拦截器会在每次请求时验证请求头中的 JWT 是否有效。
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class JwtInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (token == null || !token.startsWith("Bearer ")) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Unauthorized: No token provided");
return false;
}
token = token.substring(7); // 去掉 "Bearer " 前缀
if (JwtUtil.isTokenExpired(token)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Unauthorized: Token expired");
return false;
}
String username = JwtUtil.getUsername(token);
if (username == null) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Unauthorized: Invalid token");
return false;
}
// 将用户名或用户信息放入请求属性中,后续处理可以使用
request.setAttribute("username", username);
return true; // 继续执行请求
}
}
3. 配置拦截器
在 WebMvcConfigurer
中注册该拦截器,使其在处理请求之前执行。
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JwtInterceptor())
.addPathPatterns("/**") // 所有路径都需要拦截
.excludePathPatterns("/login", "/register"); // 排除登录和注册等不需要JWT验证的路径
}
}
4. 创建登录接口
最后,创建一个登录接口用于用户认证,验证成功后生成 JWT 并返回给前端。
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AuthController {
@PostMapping("/login")
public String login(@RequestParam String username, @RequestParam String password) {
// 在这里根据实际情况验证用户名和密码
if ("user".equals(username) && "password".equals(password)) {
// 生成 JWT
String token = JwtUtil.generateToken(username);
return "Bearer " + token; // 返回JWT
}
return "Invalid credentials"; // 登录失败
}
}
总结
- 创建了一个
JwtUtil
工具类,用于生成、解析和验证 JWT。 - 创建了一个
JwtInterceptor
拦截器,在每次请求时验证 JWT 的有效性。 - 使用
WebMvcConfigurer
配置了该拦截器,并指定哪些路径需要拦截。 - 创建了一个登录接口,用户登录时通过用户名和密码获取 JWT。
通过这些步骤,你就可以使用 JWT 实现一个基本的登录验证功能,并且在 Spring 应用中通过拦截器进行保护,确保请求中的 JWT 合法且有效。