目录
什么是JWT?
JWT就是一种 基于令牌的身份验证和授权机制,它可以安全地在客户端和服务器之间传输信息。
具体来说,JWT框架提供了一种将用户身份信息(例如用户名、用户ID等)转换为数字签名令牌的方法,这个数字签名令牌可以在网络上安全地传输,然后在服务器端验证并授权。
当然你可以这样理解,JWT就是古代的帝王,他颁发一个了金牌(token),这个令牌的真假一般人看不出来,它有特殊的鉴别方法(加密),只有各个地方的官员(不同的服务器)知道如何鉴别,公主(客户端)带着这个金牌去地方游玩时,想要获得地方官员的帮助(发送请求),这时官员(服务器)就会进行验证,确定其真假后(解密),就会给予帮助(对客户端进行响应)
为什么要用JWT呢?
验证用户的身份,对用户进行授权,例如访问某些受限资源
可能有的人就要说了,我们可以使用拦截器、数据库权限字段和Session这些方式来实现身份验证和授权功能。这些方式是可行的,但是,与JWT相比,它们存在一些局限性,在一些场景下可能不太适用,需要使用JWT来实现身份验证和授权。比如:
跨域访问:在跨域访问的场景中,由于浏览器的同源策略限制,无法使用Session来进行身份验证和授权。而JWT是基于JSON Web Token标准实现的,可以在跨域访问的场景中使用。
分布式系统:在分布式系统中,如果使用Session进行身份验证和授权,需要将Session存储在共享存储中,才能在不同的服务器之间共享Session信息。这样会增加系统的复杂度和开发成本。而JWT是基于令牌的身份验证和授权机制,可以在不同的服务器之间共享令牌信息,从而简化系统的设计和实现。
无状态API:在设计RESTful API时,为了提高API的可伸缩性和性能,通常会采用无状态的设计方式。这样,服务器不需要在自己的内存中保存任何会话信息。而JWT是一种无状态的身份验证和授权机制,可以很好地支持无状态API的设计和实现。
总结优点
无状态 | JWT本身是无状态的,即服务器不需要在自己的内存中保存任何会话信息。这样可以减少服务器的压力,使得服务器可以更容易地水平扩展。 |
跨语言 | JWT是基于JSON标准实现的(用JSON格式来定义令牌的结构和内容),因此可以在多种编程语言中使用。这使得JWT可以在不同的应用程序之间共享身份验证和授权信息。 |
安全性高 | JWT使用数字签名来验证令牌的合法性,因此可以防止令牌被篡改。此外,JWT还可以通过加密来保护令牌的机密信息。 |
什么是Token?
Token(即令牌)是一种用于身份验证和授权的凭证,它是一段字符串,通常是由服务器生成的。Token可以用于验证用户的身份,授权用户访问系统中的资源或执行某些操作。
Token有什么用?
用户身份验证
当用户登录系统时,服务器会验证用户的身份,并生成一个Token,将其返回给客户端。客户端在后续的请求中需要携带这个Token,服务器在验证Token的有效性后,才会对请求进行响应。
API访问授权
当客户端需要访问某个API时,服务器可以要求客户端提供Token,以验证客户端的身份和授权信息。如果客户端的Token有效,则服务器会授权其访问API,否则会拒绝其访问请求。
服务间通信
在分布式系统中,各个服务之间需要进行身份验证和授权,以保证系统的安全性和完整性。Token可以用于在服务之间传递身份验证和授权信息,确保服务之间的通信安全可靠。
不多BB了,直接上 SpringBoot集成JWT 的代码~
依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.19.2</version>
</dependency>
JWT工具类
创建令牌
/**创建令牌 */
public class CreateToken {
private static final long EXPIRE_TIME=1000*60*60;
//秘钥
public static final String TOKEN_SECRET="13cd761e-8805-4294-8641-fc28fc3120a7";
public static String getToken(String json) {
//过期时间
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
//私钥及加密算法
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
//设置头信息
HashMap<String, Object> header = new HashMap<>(2);
header.put("typ", "JWT");
header.put("alg", "HS256");
//附带username和userID生成签名,token变量将保存生成的JWT字符串
String token= JWT.create().withHeader(header)
.withClaim("info",json)
.withExpiresAt(date).sign(algorithm);
return token;
}
}
创建验证器
/**JWT验证器*/
public class Verifier {
public static ResultTokenVo verifierJwt(String token){
/*创建一个HMAC256加密算法的对象,并设置了加密密钥CreateToken.TOKEN_SECRET。
这个密钥是在创建JWT时使用的密钥,用于对JWT进行签名和加密。在验证JWT时,需要
使用相同的密钥进行解密和验证。*/
Algorithm algorithm = Algorithm.HMAC256(CreateToken.TOKEN_SECRET);
String info="";
/*建了一个JWTVerifier对象,用于对JWT进行验证。它使用algorithm参数指定的
加密算法和密钥进行验证,即只有使用相同的密钥和算法才能解密和验证JWT。*/
JWTVerifier verifier = JWT.require(algorithm).build();
ResultTokenVo vo=new ResultTokenVo();
try {
/**认证token */
/*用verifier对象对JWT进行验证,即解密并验证JWT的签名和过期时间等信息。
如果JWT验证通过,那么会返回一个DecodedJWT对象,包含了JWT的所有信息,
如签发者、过期时间、负载等。*/
DecodedJWT jwt = verifier.verify(token);
/**取token中的信息 */
/*从JWT中获取info字段的值,并转换为字符串类型。这个字段通常用于存储一些额外的信息,如用户信息、权限信息等。*/
info=jwt.getClaim("info").asString();
vo.setTag(1);//验证通过,设置tag值
vo.setJson(info);//额外信息
}catch (Exception e){
vo.setTag(0);
}
//返回ResultTokenVo对象,可以根据ResultTokenVo对象的tag属性来判断JWT的验证结果
return vo;
}
}
JWT拦截器
//交给spring容器管理
@Component
public class JWTInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("token");
//对token进行验证
ResultTokenVo resultTokenVo = Verifier.verifierJwt(token);
//验证成功
if (resultTokenVo.getTag() == 1) {
/*将json属性的值(即JWT中包含的额外信息)解析为一个JSON对象,以便
获取其中的具体字段值*/
JSONObject jsonObject = JSONObject.parseObject(resultTokenVo.getJson());
/*将JSON对象中名为userName的属性值保存到HTTP请求的属性中,以便
在后续的请求处理中使用*/
request.setAttribute("userName", jsonObject.get("userName"));
return HandlerInterceptor.super.preHandle(request, response, handler);
//验证失败
} else {
//返回客户端 相应的信息
PrintWriter p = new PrintWriter("{'tag':'0'}");
p.flush();
p.close();
return false;
}
}
}
配置JWT拦截器
@Component
public class JWTConfig implements WebMvcConfigurer {
//注入自定义的拦截器
@Autowired
private JWTInterceptor jwtInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
/*向拦截器链中添加jwtInterceptor对象,并设置其拦截的路径为/order/**,
表示所有以/order开头的请求都需要进行JWT验证。*/
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/order/**");
}
}