1.JWT工具包
public class JwtUtil {
/**
* secretKey jwt秘钥
*/
private static String secretKey="123456";
/**
* ttlMillis jwt过期时间(毫秒)
* 设置有效期1天
*/
private static long ttlMillis=1000*3600*24;
/**
* 指定令牌名称,前端需要在请求头中携带tokenName
*/
public static String tokenName="token";
/**
* 生成jwt
* 使用Hs256算法, 私匙使用固定秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
* @param claims 设置的信息
*/
public static String createJWT( Map<String, Object> claims) {
// 指定签名的时候使用的签名算法,也就是header那部分
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
// 生成JWT的时间
//System.currentTimeMillis():这是 Java 中用于获取当前时间的方法
long expMillis = System.currentTimeMillis() + ttlMillis;
Date exp = new Date(expMillis);
// 设置jwt的body
JwtBuilder builder = Jwts.builder()
// 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
.setClaims(claims)
// 设置签名使用的签名算法和签名使用的秘钥
.signWith(signatureAlgorithm,secretKey.getBytes(StandardCharsets.UTF_8))
// 设置过期时间
.setExpiration(exp);
return builder.compact();
}
/**
* Token解密
* @param token 加密后的token
*/
public static Claims parseJWT(String token) {
// 得到DefaultJwtParser
Claims claims = Jwts.parser()
// 设置签名的秘钥
.setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
// 设置需要解析的jwt
.parseClaimsJws(token).getBody();
return claims;
}
}
2.创建拦截器
@Component
public class JwtInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//判断当前拦截到的是Controller的方法还是其他资源
if(!(handler instanceof HandlerMethod)){
//当前拦截的不是动态方法,直接放行
return true;
}
//从请求头获取令牌
String token=request.getHeader(JwtUtil.tokenName);
try {
//判断是否携带令牌
if (StringUtils.isEmpty(token)){
throw new Exception("未携带JWT令牌");
}
//判断令牌是否有效
if (JwtUtil.parseJWT(token)==null){
throw new Exception("JWT令牌有误");
}
}catch (Exception e){
//出现异常,拦截,设置状态码为401
response.setStatus(401);
return false;
}
Map<String,Object> map=JwtUtil.parseJWT(token);
Object id = map.get("id");
LocalStorageUtil.set(id);
return true;
}
}
3.配置JWT拦截器,将拦截器交给WebMVC
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Autowired
private JwtInterceptor jwtInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/**")
//设置不拦截的请求路径,如登录接口不需要拦截等
.excludePathPatterns("/user/login")
.excludePathPatterns("/user/register")
//我使用了Knife4j测试文档,如果你没使用,可以不配置下面这行
.excludePathPatterns("/doc.html","/swagger-resources");
}
}
4.登录注册
@Autowired
private UserMapper userMapper;
@Override
public void register(String username, String password) {
//配置查询条件
//利用Wrapper来创建查找条件,来查找username,并复制给user,不管是null还是有实参
User user=userMapper.selectOne(new LambdaQueryWrapper<User>()
.eq(User::getUsername,username)
);
//然后判断是否为空,是否存在
if(!Objects.isNull(user)){
throw new RuntimeException("用户名已经存在");
}
//如果为空,重新new一个user对象,然后将注册的username和password赋值给user对象
user=new User();
user.setUsername(username);
user.setPassword(password);
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(LocalDateTime.now());
//最后将新的user添加进去
userMapper.insert(user);
}
@Override
public String sigin(String username, String password) {
if(Objects.isNull(username) || Objects.isNull(password)){
throw new RuntimeException("账户或密码不能为空");
}
//配置查询条件
LambdaQueryWrapper<User> lambdaQueryWrapper=new LambdaQueryWrapper<User>()
.eq(User::getUsername,username)
.eq(User::getPassword,password);
User user = userMapper.selectOne(lambdaQueryWrapper);
if(Objects.isNull(user)){
throw new RuntimeException("用户账号或密码错误");
}
//登录成功,生成JWT令牌
Map<String,Object> map=new HashMap<>();
map.put("id",user.getId());
String jwt = JwtUtil.createJWT(map);
return jwt;
}