一、JWT 简介
JSON Web Token (JWT) 是一种用于客户端与服务端之间安全传递信息的轻量级协议。它通常用于:
- 用户认证:登录成功后颁发 Token,后续请求中携带该 Token。
- 授权管理:通过 Token 判断用户是否有权限。
JWT 的组成部分:
- Header:描述令牌的类型和算法。
- Payload:包含用户信息及其他声明。
- Signature:由 Header、Payload 和密钥生成,用于验证 Token 的完整性。
二、项目依赖
在项目的 pom.xml
中引入以下依赖:
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 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>
</dependencies>
三、创建 JWT 配置类
1. 定义 JwtProperties
通过 @ConfigurationProperties
注解加载外部配置:
package com.example.jwt.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "jwt")
public class JwtProperties {
private String secret; // 秘钥
private long expiration; // 过期时间(秒)
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public long getExpiration() {
return expiration;
}
public void setExpiration(long expiration) {
this.expiration = expiration;
}
}
2. 激活配置类
package com.example.jwt.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(JwtProperties.class)
public class JwtConfig {
}
3. 在 application.yml
中添加配置
jwt:
secret: my-secret-key
expiration: 3600 # 单位:秒
四、编写 JWT 工具类
创建 JwtUtils
,用于生成和验证 Token:
package com.example.jwt.utils;
import com.example.jwt.config.JwtProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class JwtUtils {
private final JwtProperties jwtProperties;
public JwtUtils(JwtProperties jwtProperties) {
this.jwtProperties = jwtProperties;
}
// 生成 JWT
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + jwtProperties.getExpiration() * 1000))
.signWith(SignatureAlgorithm.HS256, jwtProperties.getSecret())
.compact();
}
// 验证 JWT
public Claims validateToken(String token) {
try {
return Jwts.parser()
.setSigningKey(jwtProperties.getSecret())
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
return null; // Token 无效
}
}
}
五、实现登录接口
1. 控制器 AuthController
package com.example.jwt.controller;
import com.example.jwt.utils.JwtUtils;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/auth")
public class AuthController {
private final JwtUtils jwtUtils;
public AuthController(JwtUtils jwtUtils) {
this.jwtUtils = jwtUtils;
}
// 登录接口
@PostMapping("/login")
public Map<String, String> login(@RequestBody Map<String, String> request) {
String username = request.get("username");
// 简单验证用户(实际中应使用数据库或其他认证机制)
if ("admin".equals(username)) {
String token = jwtUtils.generateToken(username);
Map<String, String> response = new HashMap<>();
response.put("token", token);
return response;
} else {
throw new RuntimeException("Invalid username or password");
}
}
}
2. 测试接口
使用 curl
测试登录接口:
curl -X POST http://localhost:8080/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "admin"}'
返回结果:
{
"token": "eyJhbGciOiJIUzI1NiIsInR..."
}
六、实现简单的认证拦截器
为了验证 Token 是否有效,我们可以使用拦截器来处理请求。
1. 创建拦截器 JwtInterceptor
package com.example.jwt.interceptor;
import com.example.jwt.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class JwtInterceptor implements HandlerInterceptor {
private final JwtUtils jwtUtils;
public JwtInterceptor(JwtUtils jwtUtils) {
this.jwtUtils = jwtUtils;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
token = token.substring(7); // 去掉 "Bearer "
Claims claims = jwtUtils.validateToken(token);
if (claims != null) {
// 将用户名放入请求属性中
request.setAttribute("username", claims.getSubject());
return true;
}
}
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Unauthorized");
return false;
}
}
2. 注册拦截器
package com.example.jwt.config;
import com.example.jwt.interceptor.JwtInterceptor;
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 {
private final JwtInterceptor jwtInterceptor;
public WebConfig(JwtInterceptor jwtInterceptor) {
this.jwtInterceptor = jwtInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/auth/login"); // 登录接口不拦截
}
}
七、验证拦截功能
添加受保护接口
package com.example.jwt.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@GetMapping("/user/info")
public String getUserInfo(@RequestAttribute("username") String username) {
return "Hello, " + username;
}
}
测试接口
- 访问受保护接口(无 Token):
curl http://localhost:8080/user/info
返回结果:
Unauthorized
- 携带 Token 访问:
curl http://localhost:8080/user/info \
-H "Authorization: Bearer <your-jwt-token>"
返回结果:
Hello, admin
评论 (0)