SpringBoot整合jwt(小白入门)

2023-12-25 13:33:05

本文项目所用版本为: https://blog.csdn.net/weixin_39570751/article/details/133386557

代码仓库: https://gitee.com/skyblue0678/springboot-demo

目录

什么是JWT

JWT依赖

写一个jwt工具类

测试一下jwt

优化:将过期时间配置在文件中

答疑:反正都能解析,加密有啥用?


什么是JWT

JWT(JSON Web Token)是一种用于身份验证和授权的开放标准(RFC 7519),它是一种安全的、轻量级的身份验证方式。JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。

头部通常由两部分信息组成:令牌的类型(即JWT)和所使用的签名算法。载荷包含了一些声明(Claim),用于描述用户信息、权限、过期时间等。签名由头部和载荷组成,并使用密钥进行加密生成。

JWT的使用流程如下:用户使用用户名和密码进行登录,服务器验证用户信息是否正确。服务器生成一个JWT,将用户信息、权限等信息写入载荷中,并使用密钥对头部和载荷进行签名。服务器将生成的JWT返回给客户端,客户端将其存储在本地,通常是在浏览器的cookie或本地存储中。客户端在后续的请求中,将JWT作为请求头部或请求参数传递给服务器。服务器收到请求后,验证JWT的签名是否正确,如果正确则解析出用户信息、权限等信息,进行后续操作。

JWT的优点如下:

无状态:JWT是无状态的,服务器不需要保存任何会话信息,可以轻松扩展和分布式环境下使用。

安全:JWT通过密钥对头部和载荷进行签名,保证了数据的完整性和安全性。

跨域支持:JWT可以跨域使用,可以在不同的域名和服务器之间使用。

简单易用:JWT使用简单,易于实现和维护。

然而,JWT也存在一些缺点:

载荷信息不能太多:JWT的载荷信息不能太多,否则会导致JWT的长度过长,增加网络传输的负担。

安全性依赖于密钥:JWT的安全性依赖于密钥的保护,如果密钥泄露,则JWT的安全性将受到威胁。

无法撤销:一旦JWT生成后,无法撤销,除非修改密钥或者设置短期的过期时间。

JWT依赖

<dependency>
????<groupId>com.auth0</groupId>
????<artifactId>java-jwt</artifactId>
????<version>3.19.0</version>
</dependency>

将这一段拷贝到pom.xml即可。

写一个jwt工具类

jwt主要就两个方面,一个生成token,一个校验和解析token。

/**
?*?jwt工具类
?*/
@Component
public?class?JWTUtil?{

????@Resource
????UserService?userService;

????/**
?????*?创建jwt
?????*?@param?user
?????*?@return
?????*/
????public?String?createToken(User?user){
????????return?JWT.create().withAudience(user.getId().toString())?//?设置载荷
????????????????.withExpiresAt(DateUtil.offsetHour(new?Date(),?24))?//?设置签名过期的时间:24小时后
????????????????.sign(Algorithm.HMAC256(user.getPwd()));?//?签名?Signature
????}

????/**
?????*?校验Token,返回用户信息
?????*?@param?token
?????*?@return
?????*/
????public?User?verify(String?token){
????????DecodedJWT?decode?=?JWT.decode(token);
????????String?userId?=?decode.getAudience().get(0);
????????User?user?=?userService.getById(userId);
????????//获取校验器
????????JWTVerifier?jwtVerifier?=?JWT.require(Algorithm.HMAC256(user.getPwd())).build();
????????jwtVerifier.verify(token);
????????return?user;
????}
}

测试一下jwt

首先,我们写一个根据token获取User的接口。

@GetMapping("/jwt/test")
public?User?jwtTest(HttpServletRequest?request){
????String?token?=?request.getHeader("Token");
????User?user?=?jwtUtil.verify(token);
????return?user;
}

然后,改写login方法生成一下Token:

@GetMapping("/jwt/login")
public?String?jwtLogin(String?username,String?password){
????User?user?=?userService.getByUsername(username);
????if(Objects.nonNull(user))?{
????????if?(!user.getPwd().equals(password))?{
????????????return?"密码错误!";
????????}
????}
????String?token?=?jwtUtil.createToken(user);
????return?token;
}

用postman发登录接口

得到Token:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxIiwiZXhwIjoxNzAzNTU5MjIzfQ.QFGXF6ZkQjmUtVEuDb8c1h_tFHAXE7G_WaSOuOkeBAA

再把Token放到Header,发测试接口:

成功了。

优化:将过期时间配置在文件中

我们目前过期时间是写死的,这样并不好,属于硬编码了。更恰当的方法是写在yml配置文件,或者Apollo中。

现在我们项目没有用Apollo,我们就改成配置文件吧。

在JwtUtil中添加配置:

@Value("${jwt.expire.offset:}")
Integer?expireOffset;

yml中:

代码:

jwt:
??expire:
????offset:?30

单位是秒,所以创建Token的地方也改下:

public?String?createToken(User?user){
????return?JWT.create().withAudience(user.getId().toString())?//?设置载荷
????????????.withExpiresAt(DateUtil.offsetSecond(new?Date(),?expireOffset))?//?设置签名过期的时间
????????????.sign(Algorithm.HMAC256(user.getPwd()));?//?签名?Signature
}

如果过期了,后台会报错,比如: com.auth0.jwt.exceptions.TokenExpiredException: The Token has expired on Mon Dec 25 11:10:33 CST 2023.

答疑:反正都能解析,加密有啥用?

已知在JWT的处理过程中,密钥(本项目中使用的密钥是password)在签名和验证阶段都起着关键作用。

对于DecodedJWT decode = JWT.decode(token);这行代码,实际上只是将JWT字符串解析为一个可操作的对象,并没有进行签名的验证。在这个阶段,确实不需要密钥。

但是,当你想要验证这个token的签名是否有效时,就需要使用到密钥了。例如,在代码中的这部分:

JWTVerifier?jwtVerifier?=?JWT.require(Algorithm.HMAC256(user.getPwd())).build();
jwtVerifier.verify(token);

总结一下,虽然JWT.decode(token)本身不需要密钥,但要确保JWT的有效性和真实性(比如过期时间),验证签名的过程是必要的,而这确实需要使用到密钥。

文章来源:https://blog.csdn.net/weixin_39570751/article/details/135195636
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。