Jwt令牌生成
- 引入依赖,JDK8之后的版本需要引入JAXB
<!-- JAXB API -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<!-- JAXB Runtime (implementation) -->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
- 写JwtUtils
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.Map;
public class JwtUtils {
private static String secretKey = "secret_key"; // 密钥
private static long expirationTime = 86400000; // 过期时间,1天的毫秒数
// 生成JWT Token
public static String generateToken(Map<String, Object> claims) {
return Jwts.builder()
.addClaims(claims) // 设置载荷 payload
.signWith(SignatureAlgorithm.HS512, secretKey) // 设置签名算法和密钥
.setExpiration(new Date(System.currentTimeMillis() + expirationTime)) // 设置过期时间
.compact();
}
// 解析JWT Token
public static Claims parserToken(String token) {
return Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody();
}
}
- 在登录程序中使用JwtUtils生成token并返回
@RestController
public class LoginController {
private final UserService userService;
@Autowired
public LoginController(UserService userService) {
this.userService = userService;
}
@PostMapping("/login")
public Result login(@RequestBody User user) {
User loginUser = userService.login(user);
if (loginUser != null) {
Map<String, Object> claims = new HashMap<>();
claims.put("id", loginUser.getId());
claims.put("name", loginUser.getName());
claims.put("username", loginUser.getUsername());
String token = JwtUtils.generateToken(claims); // 生成JWT Token
return Result.success(token);
} else {
return Result.error("用户名或密码错误");
}
}
}
- 使用postman测试/login接口返回内容携带token
{
"code": 1,
"msg": "success",
"data": "eyJhbGciOiJIUzUxMiJ9.eyJuYW1lIjoi566h55CG5ZGYIiwiaWQiOiJhZG1pbiIsInVzZXJuYW1lIjoiMSIsImV4cCI6MTcyNDkwMTIxMH0.WjjSzQVD_qBNntHCio_wwDNEmI6ui9aE5TaRluMPGUaA1TrFgGEAD_o8flPR5gwmHJMroRyXLA-GDv-gAGJ2Wg"
}
统一拦截校验
方案一,过滤器Filter(Java Web提供、推荐)
- 自定义filter,使用
@WebFilter(urlPatterns = "/*")
配置过滤器要拦截的请求路径
@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
//前置:强制转换为http协议的请求对象、响应对象 (转换原因:要使用子类中特有方法)
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//1.获取请求url
String url = request.getRequestURL().toString();
//2.判断请求url中是否包含login,如果包含,说明是登录操作,放行
if(url.contains("/login")){
chain.doFilter(request, response);//放行请求
return;//结束当前方法的执行
}
//3.获取请求头中的令牌(token)
String token = request.getHeader("token");
//4.判断令牌是否存在,如果不存在,返回错误结果(未登录)
if(token == null || token.isEmpty()) {
Result responseResult = Result.error("NOT_LOGIN");
//把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
String json = JSONObject.toJSONString(responseResult);
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(json);
return;
}
//5.解析token,如果解析失败,返回错误结果(未登录)
try {
JwtUtils.parserToken(token);
}catch (Exception e){
Result responseResult = Result.error("NOT_LOGIN");
//把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
String json = JSONObject.toJSONString(responseResult);
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(json);
return;
}
//6.放行
chain.doFilter(request, response);
}
}
实现Filter 接口,对于Spring Boot 3.x 及以上:使用jakarta.servlet包;Spring Boot 2.x 及以下:使用javax.servlet包。
2. 开启SpringBoot项目对于Servlet组件的支持使用@ServletComponentScan
注解
@ServletComponentScan
@SpringBootApplication
public class LoginApplication {
public static void main(String[] args) {
SpringApplication.run(LoginApplication.class, args);
}
}
- 过滤器链
多个过滤器会按照过滤器类名顺序调用。
方案二,拦截器Interceptor(Spring提供)
- **自定义拦截器:**实现HandlerInterceptor接口
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("token");
System.out.println("token = " + token);
//1.判断令牌是否存在,如果不存在,返回错误结果(未登录)
if(token == null || token.isEmpty()) {
//创建响应结果对象
Result responseResult = Result.error("NOT_LOGIN");
//把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
String json = JSONObject.toJSONString(responseResult);
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(json);
return false;//不放行
}
//2.解析token,如果解析失败,返回错误结果(未登录)
try {
JwtUtils.parserToken(token);
}catch (Exception e){
//创建响应结果对象
Result responseResult = Result.error("NOT_LOGIN");
//把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
String json = JSONObject.toJSONString(responseResult);
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(json);
return false;
}
//3.放行
return true;
}
}
- 注册配置拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginCheckInterceptor loginCheckInterceptor;
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginCheckInterceptor)
.addPathPatterns("/**") // 拦截所有请求
.excludePathPatterns("/login"); // 放行登录请求
}
}
在拦截器中除了可以设置/**
拦截所有资源外,还有一些常见拦截路径设置:
拦截路径 | 含义 | 举例 |
---|---|---|
/* | 一级路径 | 能匹配/depts,/emps,/login,不能匹配 /depts/1 |
/** | 任意级路径 | 能匹配/depts,/depts/1,/depts/1/2 |
/depts/* | /depts下的一级路径 | 能匹配/depts/1,不能匹配/depts/1/2,/depts |
/depts/** | /depts下的任意级路径 | 能匹配/depts,/depts/1,/depts/1/2,不能匹配/emps/1 |
执行流程
参考:bilibili黑马java web开发教程