如果文章对你有帮助欢迎【关注❤️❤️❤️点赞👍👍👍收藏⭐⭐⭐】一键三连!一起努力!
一、JWT简介
JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。
一个JWT实际上就是一个字符串,它由三部分组成,头部
、载荷
与签名
。
头部(Header)
头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。
这也可以被表示成一个JSON对象。
{"typ":"JWT","alg":"HS256"}
在头部指明了签名算法是HS256算法。 我们进行BASE64编码http://base64.xpcha.com/,编码后的字符串如下:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
载荷(playload)
载荷就是存放有效信息的地方。
包含两种类型数据:
- 标准中注册的声明的数据;
-
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
-
- 自定义数据。
- 存放我们想放在token中存放的key-value值
定义一个payload:
{"sub":"1234567890","name":"John Doe","admin":true}
然后将其进行base64加密,得到Jwt的第二部分。
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
签证(signature)
jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
header (base64后的)
payload (base64后的)
secret
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
将这三部分用.连接成一个完整的字符串,构成了最终的JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
二、Spring Boot 集成 JWT
1、引入项目依赖 pom.xml
<!--JWT-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
2、编写对应工具类获取token
@Component
public class TokenUtils {
public static String getToken(String userId, String sign) {
return JWT.create().withAudience(userId) // 将 user id 保存到 token 里面,作为载荷
.withExpiresAt(DateUtil.offsetHour(new Date(), 2)) //2小时后token过期
.sign(Algorithm.HMAC256(sign)); // 以 password 作为 token 的密钥
}
}
3、编写JWT拦截器
/**
* @description: JWT拦截器
* @author: Xiong
* @date: 2022/11/17 21:41
*/
public class JwtInterceptor implements HandlerInterceptor {
@Autowired
IUserService userService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 从http请求头中取出token
String token = request.getHeader("token");
// 如果不是映射到方法直接通过
if (!(handler instanceof HandlerMethod)) {
return Boolean.TRUE;
}
// 执行认证
if (StrUtil.isBlank(token)) {
throw new ServiceException(HttpStatus.UNAUTHORIZED, "无token,请登录认证!");
}
// 获取token中的userId
String userId;
try {
userId = JWT.decode(token).getAudience().get(0);
} catch (JWTDecodeException j) {
throw new ServiceException(HttpStatus.UNAUTHORIZED, "token验证失败,请重新登录!");
}
User user = userService.getById(userId);
if (user == null) {
throw new ServiceException(HttpStatus.UNAUTHORIZED, "用户不存在,请重新登录!");
}
// 用户密码加签验证token
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
try {
jwtVerifier.verify(token);
} catch (JWTVerificationException e) {
throw new ServiceException(HttpStatus.UNAUTHORIZED, "token验证失败,请重新登录!");
}
return Boolean.TRUE;
}
}
4、注册拦截器
/**
* @description: 注册拦截器
* @author: Xiong
* @date: 2022/11/17 21:51
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor())
.addPathPatterns("/**") // 拦截所有请求,通过判断token决定是否需要登录
.excludePathPatterns("/**/login", "/**/register", "/**/export", "/**/import", "/sys-file/**") // 放行的请求
;
}
/**
* 注入到spring容器中
* @return
*/
@Bean
public JwtInterceptor jwtInterceptor() {
return new JwtInterceptor();
}
}
5、登录时设置token
@Override
public UserDto login(UserDto userDto) {
User one = getUser(userDto);
if (one != null) {
BeanUtil.copyProperties(one, userDto, true);
// 设置token
String token = TokenUtils.getToken(one.getUserId().toString(), one.getPassword());
userDto.setToken(token);
return userDto;
} else {
throw new ServiceException(HttpStatus.NO_CONTENT, "用户名或密码不正确");
}
}
对应实体类:
@Data
public class UserDto {
private String username;
private String password;
private String nickname;
private String avatar;
private String token;
}
/**
* @description:
* @author: Xiong
* @date: 2022/10/30 20:12
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "sys_user")
public class User extends BaseEntity{
/**
* 用户ID
*/
@TableId(type = IdType.AUTO)
private Long userId;
/**
* 部门ID
*/
@ExcelExport("部门ID")
@ExcelImport("部门ID")
private Long deptId;
/**
* 用户账号
*/
@ExcelExport("用户账号")
@ExcelImport("用户账号")
private String userName;
/**
* 用户昵称
*/
@ExcelExport("用户昵称")
@ExcelImport("用户昵称")
private String nickName;
/**
* 用户邮箱
*/
@ExcelExport("用户邮箱")
@ExcelImport("用户邮箱")
private String email;
/**
* 手机号码
*/
@ExcelExport(value = "手机号码")
@ExcelImport(value = "手机号码", maxLength = 11)
private String phonenumber;
/**
* 用户性别
*/
@ExcelExport(value = "用户性别", kv = "0-男;1-女;2,未知")
@ExcelImport(value = "用户性别", kv = "0-男;1-女;2,未知")
private String sex;
/**
* 用户头像
*/
private String avatar;
/**
* 密码
* 前端不返回
*/
@JsonIgnore
private String password;
/**
* 帐号状态(0正常 1停用)
*/
private String status;
/**
* 删除标志(0代表存在 2代表删除)
*/
private String delFlag;
/**
* 最后登录IP
*/
private String loginIp;
/**
* 最后登录时间
*/
private Date loginDate;
}
/**
* @description: 基础数据
* @author: Xiong
* @date: 2022/10/30 20:14
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BaseEntity implements Serializable {
/**
* 创建者
*/
private String createBy;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 更新者
*/
private String updateBy;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/**
* 备注
*/
private String remark;
}
启动项目后验证:
无法获取数据,并且提醒需要登录!
登录后再请求:
简易版登录认证实现!!!
感谢各位家人的观看喜欢的话点帮忙点赞👍👍👍哦