提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
学习Jwt,那么Jwt是用来做什么的呢,Jwt又是什么呢?
Jwt的作用:从而允许用户访问该令牌允许的路由,服务和资源。单点登录当今广泛使用JWt的一项功能,因为它的开销小,同时又会避免使用session出现作用域的问题
Jwt组成:JSON Web Token,也就是通过JSON形式作为Web应用中的令牌,用于各方之间安全地将信息作为JSON对象传输,在数据传输过程中还可以完成数据加密,签名等相关处理
提示:以下是本篇文章正文内容,下面案例可供参考
一、使用场景
用于前后端一个安全地访问,同时可以在传递的Jwt令牌中获得到对应的用户信息
二、使用步骤
Jwt结构
1.标头(Header)
2.有效载荷(Payload)
3.签名(Signature)
xxxxxxx.yyyyyyyy.zzzzzz
1.引入库
<!-- jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
2.创建Jwt的令牌
代码如下(示例):用于创建Jwt令牌
Calendar instance = Calendar.getInstance();
instance.add(Calendar.DATE,3); //data天
Map<String, Object> map = new HashMap<>();
String token = JWT.create()
//.withHeader(map) //header 不写就用默认HMAC256 也是推荐使用的
.withClaim("userId", "1111") //payload
.withClaim("userName", "张三")
.withExpiresAt(instance.getTime()) //指定令牌过期时间
.sign(Algorithm.HMAC256("@*%^!@BKF&*W&#$GTIKSDY")); //签名
System.out.println(token);
withClaim: 用于放入Jwt的数据,一般放入个用户名称和唯一标识,密码不用
withExpiresAt: 指定用户这个令牌的过期时间,如果过期了要重新登录的
sign: 用于在生成令牌时额外的数据越复杂越安全
看生成的token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
.eyJ1c2VyTmFtZSI6IuW8oOS4iSIsImV4cCI6MTY3OTAwMDYwNywidXNlcklkIjoiMTExMSJ9
.zYxDCLQumzHaDQutzAECWdYnVPfAF2ueTl_-j0WrwXI
3.解析令牌
既然生成,我们肯定可以解析,这样才可以验证令牌是否正确
上代码:
//验证对象
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("@*%^!@BKF&*W&#$GTIKSDY")).build();
DecodedJWT verify = jwtVerifier.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6IuW8oOS4iSIsImV4cCI6MTY3ODYxMjM3MywidXNlcklkIjoiYXNkZmFzZGZzYSJ9.MUl7PzhHqF_Z4H3rUJ-hhW7SYjGxqSysAV_40vrbOGY");
System.out.println(verify);
System.out.println(verify.getClaim("userId").asString());
System.out.println(verify.getClaim("userName").asString());
System.out.println("过期时间:" + verify.getExpiresAt());
注意:
1. Algorithm.HMAC256("@*%^!@BKF&*W&#$GTIKSDY") 这里要和加密时用的算法一致,同时@*%^!@BKF&*W&#$GTIKSDY 这些也要一致,因为只有这样才可以正确解析
2. .verify("eyJ0eX 这里填入的也就是我们生成的Jwt令牌(token)
3.verify.getClaim("userId").asString() 这里asString()转换为指定类型,一定要和传入的类型一样,不然获得不到值的
整合SpringBoot + Jwt
jar包 上面有
1.创建Jwt工具类
package com.heng.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Calendar;
import java.util.Map;
/**
* @author 小马
* @date
* Jwt工具类
*/
public class JwtUtils {
//额外的数据,越复杂越安全
private static final String SING_VALUE = "@*%^!@BKF&*W&#$GTIKSDY";
//token的过期时间
private static final int VALIDITY_DATE = 3; //单位 天
/**
* 生成token 组成 header.payload.sing
* payload: 想要在toekn中携带的值 用户标识 名称等
*/
public static String getToken(Map<String, String> payload) {
if (SING_VALUE.isEmpty() || SING_VALUE == null) {
throw new NullPointerException("JWT的签名不能为空");
}
if (VALIDITY_DATE == 0) {
throw new NullPointerException("JWT的过期时间不能为空");
}
JWTCreator.Builder builder = JWT.create();
payload.forEach((k,v) -> {
builder.withClaim(k, v);
});
Calendar instance = Calendar.getInstance();
instance.add(Calendar.DATE, VALIDITY_DATE); //data天
String token = builder
.withExpiresAt(instance.getTime())
.sign(Algorithm.HMAC256(SING_VALUE));
return token;
}
/**
* 验证token合法性
*/
public static void verify(String token) {
//验证token
JWT.require(Algorithm.HMAC256(SING_VALUE)).build().verify(token);
}
/**
* 获得 token 信息
*/
public static DecodedJWT getTokenInfo(String token) throws Exception {
return JWT.require(Algorithm.HMAC256(SING_VALUE)).build().verify(token);
}
}
2.配置拦截器
package com.heng.config.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.heng.utils.JwtUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
public class JWTInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Map<String, Object> map = new HashMap<>();
//获得请求头中的令牌
String token = request.getHeader("token");
try {
JwtUtils.verify(token); //验证令牌
return true; //放行
} catch (SignatureVerificationException e) {
e.printStackTrace();
map.put("msg", "无效签名");
} catch (TokenExpiredException e) {
e.printStackTrace();
map.put("msg", "token过期!");
} catch (AlgorithmMismatchException e) {
e.printStackTrace();
map.put("msg", "token算法不一致");
} catch (Exception e) {
e.printStackTrace();
map.put("msg", "token无效");
}
map.put("state", "无效");
Object mapJson = JSONObject.toJSON(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().print(mapJson);
return false;
}
}
3.让拦截器生效
package com.heng.config.interceptor;
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 InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JWTInterceptor())
.addPathPatterns("/**")
//只放行登录验证的接口
.excludePathPatterns("/user/login");
}
}
4.编写Controller
@Controller
@RequestMapping(path = "user")
public class UserController {
@PostMapping(path = "login")
@ResponseBody
public Result<String> getToken(@RequestParam("userId") String userId,
@RequestParam("userName") String userName,
HttpServletResponse response) {
if (userId == null || "".equals(userId)) {
return Result.error(500, "id不能为空");
}
if (userName == null || "".equals(userName)) {
return Result.error(500, "姓名不能为空");
}
//验证用户代码。。。。
//将token返回给前端,或者放入Http
Map<String, String> map = new HashMap<>();
map.put("userId", userId);
map.put("userName", userName);
String token = JwtUtils.getToken(map);
response.setHeader("token", token);
return Result.success(token);
}
}
效果图
到这里基本就完成了!!!
还有就是请求是在Headers中记得添加token
可以配置一个统一异常,如果是下面几个错误就是token有问题,重定向到登录页面
常见的异常:
JWTDecodeException: The string '{"typ":(�|b�[Ȏ��̍M��' doesn't have a valid JSON format.
1.Jwt解码异常,根据这个token无法解码,token不对
AlgorithmMismatchException: The provided Algorithm doesn't match the one defined in the JWT's Header.
2.Jwt的算法不匹配
TokenExpiredException: The Token has expired on Thu Mar 09 14:58:12 CST 2023.
3.Jwt令牌过期了
InvalidClaimException
4.失效的payload异常, 没有复现出来
总结
第一次编写博客,后续也会慢慢的更新其他的内容,有问题的可以在下面评论,我会回复的