1、JWT的构成
- 头部(header):描述该JWT的最基本的信息,如类型以及签名所用的算法。
- 负载(payload):存放有效信息的地方。
- 签证(signature):base64加密后的header、base64加密后的payload和密钥secret加密后组成。
2、整合JWT
2.1 引入JWT依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>JAVA-jwt</artifactId>
<version>3.18.3</version>
</dependency>
2.2 编写JWTUtils工具类
package com.stock.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.Verification;
import java.util.Calendar;
import java.util.Map;
public class JWTUtils {
private static final String SING="@#$%^&*";
// 生成token
public static String getToken(Map<String,String> map){
Calendar instance = Calendar.getInstance();
instance.add(Calendar.MINUTE,30);
//创建jwt builder
JWTCreator.Builder builder = JWT.create();
//payload
builder.withExpiresAt(instance.getTime());
map.forEach((k,v)->{
builder.withClAIm(k,v);
});
//设置签名
String token = builder.sign(Algorithm.Hmac256(SING));
return token;
}
//验证令牌
public static void verifyToken(String token){
JWTVerifier require = JWT.require(Algorithm.HMAC256(SING)).build();
require.verify(token);
}
//获取token信息
public static DecodedJWT getTokenInfo(String token){
DecodedJWT verify = JWT.require(Algorithm.HMAC256(SING)).build().verify(token);
return verify;
}
}
2.3 编写拦截器
public class JWTInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
System.out.println("OPTIONS请求,放行");
return true;
}
HashMap<String, Object> map = new HashMap<>();
String token = request.getHeader("token");
try {
JWTUtils.verifyToken(token);
return true;
}catch (SignatureVerificationException e){
map.put("msg","无效签名!");
}catch (TokenExpiredException e){
map.put("msg","token过期!");
}catch (AlgorithmMismatchException e){
map.put("msg","token加密算法不一致");
}catch (Exception e){
map.put("msg","无效签名!");
}
map.put("state",404);
map.put("path","/login");
//将map转化为字符串返回给前端
String result = new ObjectMApper().writeValueAsString(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(result);
return false;
}
}
注意:
1、token存放在请求的header中;
2、在前后端分离的项目中,发送的GET/POST请求实则为两次请求。第一次请求为OPTIONS请求,第二次请求才是GET/POST请求;在OPTIONS请求中,不会携带请求头的参数,会导致在拦截器上获取请求头为空,自定义的拦截器拦截成功。第一次请求不能通过,就不能获取第二次的请求。所以需要在拦截器中加上如下代码来判断是否为OPTIONS请求,对于OPTIONS请求直接放过。
if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
System.out.println("OPTIONS请求,放行");
return true;
}
2.4 配置拦截器
package com.stock.config;
import com.stock.Interceptors.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 IntercepterConfg implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JWTInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login");
}
}
2.5 编写Controller
package com.stock.controller;
import com.stock.entity.User;
import com.stock.result.Result;
import com.stock.service.UserService;
import com.stock.utils.JWTUtils;
import com.stock.utils.ResultUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
@RestController
public class LoginController {
private UserService userService;
@Autowired
public LoginController(UserService userService) {
this.userService = userService;
}
@PostMapping("/login")
public Result register(User user){
HashMap<String, String> map = new HashMap<>();
map.put("username",user.getUserName());
String token = JWTUtils.getToken(map);
HashMap<String, Object> data = new HashMap<>();
data.put("token",token);
return ResultUtils.getresult(200,"登录成功!",data);
}
@GetMapping("/main")
public Result tomain(){
return ResultUtils.getresult(200,"访问成功",null);
}
}
2.6使用Postman测试
- 未登录前访问127.0.0.1:8888/main
- 先登录再访问127.0.0.1:8888/main