桂林市网站建设,免费 网站 服务器,江苏城乡住房建设部网站,wordpress注册时候卡住文章目录 回顾添加依赖Jwt依赖Jwt配置定义Jwt拦截器注册Jwt拦截器#xff0c;配置需要验证token的URL 测试Jwt修改登录等逻辑 回顾
在之前的系统中#xff0c;我们利用UUID配合Redis以达到角色登录的功能。
当前整个系统存在一个问题#xff1a;人为修改token值后#xf… 文章目录 回顾添加依赖Jwt依赖Jwt配置定义Jwt拦截器注册Jwt拦截器配置需要验证token的URL 测试Jwt修改登录等逻辑 回顾
在之前的系统中我们利用UUID配合Redis以达到角色登录的功能。
当前整个系统存在一个问题人为修改token值后用户仍然能在前端进行数据库操作后台没有校验当前用户token就允许一些请求导致系统存在安全漏洞。
解决方法Jwt签名验证。整合Jwt后前端发出的请求后端会先进行token验证然后执行操作。
整合Jwt的效果如下找到token值然后进行修改
在token前加上值123保存后进行一些操作
此时点击页面修改按钮会弹出token错误的信息
后端也会记录token错误的信息 现在开始来实现这个功能
添加依赖 Jwt依赖
在pom文件中添加下述依赖
!-- jwt --
dependencygroupIdio.jsonwebtoken/groupIdartifactIdjjwt/artifactIdversion0.9.1/version/dependencyJwt配置
在common文件夹下新建一个文件夹utils然后新建java文件JwtUtil
写上下述代码注释已标出
package com.ums.common.utils;import com.alibaba.fastjson2.JSON;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;Component
public class JwtUtil {// 有效期private static final long JWT_EXPIRE 30*60*1000L; //半小时, 单位为毫秒// 令牌秘钥private static final String JWT_KEY 123456;// Object data 可放入User对象给User中的信息加密后成为tokenpublic String createToken(Object data){// 当前时间long currentTime System.currentTimeMillis();// token过期时间long expTime currentTimeJWT_EXPIRE;// 构建jwtJwtBuilder builder Jwts.builder().setId(UUID.randomUUID()).setSubject(JSON.toJSONString(data)) // User对象序列化.setIssuer(system).setIssuedAt(new Date(currentTime)).signWith(SignatureAlgorithm.HS256, encodeSecret(JWT_KEY)) // 加密.setExpiration(new Date(expTime));return builder.compact();}// 加密算法private SecretKey encodeSecret(String key){byte[] encode Base64.getEncoder().encode(key.getBytes());SecretKeySpec aes new SecretKeySpec(encode, 0, encode.length, AES);return aes;}// token 解密public Claims parseToken(String token){Claims body Jwts.parser().setSigningKey(encodeSecret(JWT_KEY)).parseClaimsJws(token).getBody();return body;}// token 解密并返回一个对象可是User对象public T T parseToken(String token,ClassT clazz){Claims body Jwts.parser().setSigningKey(encodeSecret(JWT_KEY)).parseClaimsJws(token).getBody();return JSON.parseObject(body.getSubject(),clazz);}}定义Jwt拦截器
在XAdminApplication同级目录下新建文件夹interceptor再新建java文件JwtValidateInterceptor
文件中写入以下代码注释已给出
package com.ums.interceptor;import com.alibaba.fastjson2.JSON;
import com.ums.common.utils.JwtUtil;
import com.ums.common.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;// HandlerInterceptor继承该接口然后重写方法
Component
Slf4j
public class JwtValidateInterceptor implements HandlerInterceptor {Autowiredprivate JwtUtil jwtUtil;Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// X-Token 是前端定义的token标头与前端保持一致String token request.getHeader(X-Token);log.debug(request.getRequestURI() 需要验证 token); // 后台日志记录if (token ! null){try {jwtUtil.parseToken(token);// 不要写System.out.println(); 此为垃圾代码// 加上注解Slf4j , 用log.debug()来打印log.debug(request.getRequestURI() 验证通过);return true;}catch (Exception e) {e.printStackTrace();}}log.debug(request.getRequestURI() 验证失败禁止访问); // 后台日志记录// 创建一个返回对象当token错误后反馈给前端ResultObject fail Result.fail(20003, token无效请重新登录);// 验证不成功给前端返回数据response.setContentType(application/json;charsetutf-8); // 定义返回数据格式response.getWriter().write(JSON.toJSONString(fail)); // 将对象序列化后以json格式反馈至前端return false; // 拦截当前用户的操作}
}注册Jwt拦截器配置需要验证token的URL
在config目录下新建java文件MyInterceptorConfig 写入以下代码
package com.ums.config;import com.ums.interceptor.JwtValidateInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;Configuration
public class MyInterceptorConfig implements WebMvcConfigurer {Autowiredprivate JwtValidateInterceptor iwtValidateInterceptor;Overridepublic void addInterceptors(InterceptorRegistry registry) {InterceptorRegistration registration registry.addInterceptor(iwtValidateInterceptor);registration.addPathPatterns(/**) // 拦截所有URL请求.excludePathPatterns( // 开放下述URL请求/user/login,/user/info,/user/logout);}
}自此Jwt就算配置完毕 总共新建下述三个文件
测试Jwt
新建一个测试类JwtUtilsTest
Autowired
private JwtUtil jwtUtil;Test
public void testCreateJwt(){User user new User();user.setUsername(anthony);user.setPhone(14766665555);String token jwtUtil.createToken(user);System.out.println(token);
}运行testCreateJwt()系统会打印出一个加密后的字符串此串会作为token使用。
将这个字符串复制
新建一个解密的测试方法testParseJwt()下述代码中复制你自己的token Testpublic void testParseJwt(){String token eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIwNmRlOGJmOS1kYmM1LTQzNjUtYWRmYi0yYzBjMmVmM2FkOGYiLCJzdWIiOiJ7XCJwaG9uZVwiOlwiMTQ3NjY2NjU1NTVcIixcInVzZXJuYW1lXCI6XCJhbnRob255XCJ9IiwiaXNzIjoic3lzdGVtIiwiaWF0IjoxNjkwMjQ4MjY1LCJleHAiOjE2OTAyNTAwNjV9.iskJNmm6b6rDFs1oxsinrCdFmul0dd9-4_zswD6eGV0;Claims claims jwtUtil.parseToken(token);System.out.println(claims);}运行后可得到加密的信息
因为我们是将一个对象整体进行加密所以希望在解密的时候还原为一个对象
此时代码可这样写 Testpublic void testParseJw2t(){String token eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIwNmRlOGJmOS1kYmM1LTQzNjUtYWRmYi0yYzBjMmVmM2FkOGYiLCJzdWIiOiJ7XCJwaG9uZVwiOlwiMTQ3NjY2NjU1NTVcIixcInVzZXJuYW1lXCI6XCJhbnRob255XCJ9IiwiaXNzIjoic3lzdGVtIiwiaWF0IjoxNjkwMjQ4MjY1LCJleHAiOjE2OTAyNTAwNjV9.iskJNmm6b6rDFs1oxsinrCdFmul0dd9-4_zswD6eGV0;User user jwtUtil.parseToken(token,User.class);System.out.println(user);}运行后得到一个对象
修改登录等逻辑
现在有了Jwt签名验证机制可将之前的UUID redis登录逻辑进行修改
打开UserServiceImpl文件
将之前写的login(User user)、getUserInfo(String token)、logout(String token)这三段函数全部重写
login(User user) Autowiredprivate JwtUtil jwtUtil;Overridepublic MapString, Object login(User user) {// 根据用户名查询LambdaQueryWrapperUser wrapper new LambdaQueryWrapper();wrapper.eq(User::getUsername, user.getUsername());User loginUser this.baseMapper.selectOne(wrapper);// 结果不为空并且密码与数据库解密后的密码匹配生成token将用户信息存入redisif (loginUser ! null passwordEncoder.matches(user.getPassword(), loginUser.getPassword()) // 匹配加密密码) {loginUser.setPassword(null); // 设置密码为空密码没必要放入// 创建jwtString token jwtUtil.createToken(loginUser);// 返回数据MapString, Object data new HashMap();data.put(token,token);return data;}// 结果不为空生成token前后端分离前端无法使用session可以使用tokenreturn null;}getUserInfo(String token)Override
public MapString, Object getUserInfo(String token) {// 之前已将对象进行序列化处理存入redis现在从redis中取出需要反序列化处理// Object obj redisTemplate.opsForValue().get(token); // 此对象是map类型,稍后需要序列化为Json字符串// User loginUser JSON.parseObject(JSON.toJSONString(obj), User.class);User loginUser null;try {// 解析TokenloginUser jwtUtil.parseToken(token, User.class);}catch (Exception e) {e.printStackTrace();}if (loginUser ! null) {MapString,Object data new HashMap();data.put(name,loginUser.getUsername());data.put(avatar,loginUser.getAvatar());// 先在xml里写SQL语句 idgetRoleNameByUserId然后去UserMapper里实现接口ListString roleList this.baseMapper.getRoleNameByUserId(loginUser.getId());data.put(roles,roleList);return data;}return null;
}logout(String token) , 注释以前的代码可啥也不用写Override
public void logout(String token) {// redisTemplate.delete(token); // 从redis中删除token
}自此完毕