网站模板 安装,松江品划网站建设维护,沈阳网官方网站,wordpress 花瓣模板紧接着上一次的博客#xff0c;我们现在来给这个项目添加一个jwt的权限验证功能#xff0c;上一次的博客如下#xff1a;springboot结合vue实现登录和注册功能-CSDN博客 1.后端
1.1.导入依赖 dependencygroupIdcom.auth0/groupIdartifactId我们现在来给这个项目添加一个jwt的权限验证功能上一次的博客如下springboot结合vue实现登录和注册功能-CSDN博客 1.后端
1.1.导入依赖 dependencygroupIdcom.auth0/groupIdartifactIdjava-jwt/artifactIdversion4.3.0/version/dependency
1.2.编写jwt的拦截器 这个类实现一个HandlerInterceptor接口这个类主要完成以下几个任务 从请求头里获取token没有获取到就抛异常(注意请求头里原本是没有token的这个需要我们自己在前端添加一个token)解码token并从token里获取用户ID没有获取到就抛异常表明token里没有数据(注意这个用户ID是自己在前端添加token时存储的)通过用户密码来生成一个验证器解析token(JWT一般含有三个部分头部荷载签名解析过程中jwtVerifier会检验这三部分能不能正常分离以及来用验证器来验证签名以及检查token的过期时间)这一步也是最重要的一步 package com.kuang.common;import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.kuang.exception.ServiceException;
import com.kuang.mapper.UserMapper;
import com.kuang.pojo.User;
import io.micrometer.common.util.StringUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;public class JwtInterceptor implements HandlerInterceptor {Resourceprivate UserMapper userMapper;Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//从请求头Header里接收传来的参数tokenString headerToken request.getHeader(token);//如果传来的token为空则从url参数中来接收传来的tokenif(StringUtils.isBlank(headerToken)){headerToken request.getParameter(token);//如果url里的token为空则抛异常}if (StringUtils.isBlank(headerToken)){throw new ServiceException(401,请登录);}//从token中获取userId//JWT.decode(headerToken) 解码JTW TokenString userId;try {userId JWT.decode(headerToken).getAudience().get(0);} catch (JWTDecodeException e) {throw new ServiceException(401,请登录);}//根据userId查询数据库//userId是String类型这里要转换成int类型User user userMapper.selectUserById(Integer.parseInt(userId));//user为空则抛异常if (user null){throw new ServiceException(401,请登录);}//通过用户密码加密之后生成一个验证器JWTVerifier jwtVerifier JWT.require(Algorithm.HMAC256(user.getPassword())).build();try {//验证tokenjwtVerifier.verify(headerToken);} catch (JWTVerificationException e) {throw new ServiceException(401,请登录);}return true;}
}1.3.编写token的工具类 在这个类中有以下几点任务 生成token并且将用户ID放在token的荷载(Payload)中当作受众声明(Audience)以及设置token的过期时间把用户密码当作密钥然后给token加一个签名只有添加了签名这个token才能被使用而我们设置的这个密钥就是来验证签名的钥匙 package com.kuang.utils;import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.kuang.mapper.UserMapper;
import com.kuang.pojo.User;
import io.micrometer.common.util.StringUtils;
import jakarta.annotation.PostConstruct;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
import java.util.Date;Component
public class TokenUtils {private static UserMapper staticUserMapper;AutowiredUserMapper userMapper;PostConstructpublic void setUserService(){staticUserMapper userMapper;}/*** 创建token* param userId* param sign* return*/public static String createToken(String userId,String sign){// 获取当前时间Date currentDate new Date();// 创建Calendar实例Calendar calendar Calendar.getInstance();// 设置Calendar的时间为currentDatecalendar.setTime(currentDate);// 向前偏移两个小时calendar.add(Calendar.HOUR_OF_DAY, 2);// 获取偏移后的时间Date offsetDate calendar.getTime();return JWT.create().withAudience(userId)//将userId保存到token里.withExpiresAt(offsetDate) //2小时候token过期.sign(Algorithm.HMAC256(sign)); //将password作为token密钥}/*** 获取当前登录的用户信息* return*/public static User getCurrentUser(){//获取当前请求的HttpServletRequest对象这样就能在下面访问请求头、参数等HttpServletRequest request ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();try {//从请求头里获取tokenString token request.getHeader(token);//如果token不为空则从Audience中获取第一个数据(是用户的id)if (StringUtils.isNotBlank(token)){String userId JWT.decode(token).getAudience().get(0);return staticUserMapper.selectUserById(Integer.parseInt(userId));}} catch (Exception e) {return null;}return null;}}1.4.扩展springmvc的拦截器 这个类继承WebMvcConfigurationSupport类主要任务有以下几点 将在第二步编写的jwt的拦截器注入到spring容器中并将其添加到spring的拦截器中设置拦截路径 当用户访问下面设置好的拦截路径时就会触发我们自己编写的jwt的拦截器然后进入校验过程(就是第二步中的那一套流程) package com.kuang.common;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {Overrideprotected void addInterceptors(InterceptorRegistry registry) {//配置jwt的拦截器规则,拦截所以请求除了/user/login,/user/register,/file/uploadregistry.addInterceptor(jwtInterceptor()).addPathPatterns(/**).excludePathPatterns(/user/login,/user/register,/file/upload);super.addInterceptors(registry);}Beanpublic JwtInterceptor jwtInterceptor(){return new JwtInterceptor();}
}1.5.将后端生成的token返回给前端 一般会在用户登录时将token返回给前端下面这个类就是实现登录功能的service层的实现类 service层 Overridepublic User selectUserByUsername(User user) {User user1 userMapper.selectUserByUsername(user);//生成token,userId用来放在token里password用来生成token的验证器来验证tokenString token TokenUtils.createToken(String.valueOf(user1.getId()), user1.getPassword());user1.setToken(token);return user1;}
controller层 //登陆功能PostMapping(/login)public Result login(RequestBody User user, HttpServletRequest request){User user1 userService.selectUserByUsername(user);if (!user.getPassword().equals(user1.getPassword())){return Result.error(用户名或密码不正确);}//将id存入session//request.getSession().setAttribute(userId,user1.getId());//将含有token的user对象返回给前端return Result.success(user1);}
2.前端
2.1.导入request.js文件 这里添加一个通用的request.js文件这个文件用于以下几点 可以在请求发送前对请求做一些处理可以在接口响应后统一处理结果对请求路径前面的httplocalhost做了封装以便不用每次都写上对返回的数据做了封装原本要访问后端返回的数据要这样写res.data.data封装了之后可以简化为res.data import axios from axios
import router from /router;const request axios.create({baseURL: http://localhost:8082, // 注意 这里是全局统一加上了 /api 前缀也就是说所有接口都会加上/api前缀在页面里面写接口的时候就不要加 /api了否则会出现2个/api类似 /api/api/user这样的报错切记timeout: 5000
})// request 拦截器
// 可以自请求发送前对请求做一些处理
// 比如统一加token对请求参数统一加密
request.interceptors.request.use(config {config.headers[Content-Type] application/json;charsetutf-8;//在请求头里添加一个tokenlet user JSON.parse(localStorage.getItem(user) || {})config.headers[token] user.token; // 设置请求头return config
}, error {return Promise.reject(error)
});// response 拦截器
// 可以在接口响应后统一处理结果
request.interceptors.response.use(response {let res response.data;// 如果是返回的文件if (response.config.responseType blob) {return res}// 兼容服务端返回的字符串数据if (typeof res string) {res res ? JSON.parse(res) : res}if (res.code 401){router.push(/login)}return res;},error {console.log(err error) // for debugreturn Promise.reject(error)}
)export default request在上面的文件里我们着重看这几行代码在前端发送请求时在请求头里添加一个token并且从localStorage中获取在登录时存储的用户信息用来存储在token中具体程序如下 在接口响应后统一处理结果中的如下程序如果用户没有token后端就会返回401的错误在这里就会处理401错误进行页面跳转 导入这个文件之后我们可以在main.js文件中注册这个文件的全局对象 2.2.采用request.js文件里提供的请求方式 这里只提供一个例子其他的地方都是一样的 3.流程图分析 下面的流程图涉及了后端以下几个类 JwtInterceptor1.2中的jwt的拦截器TokenUtils1.3中的token的工具类InterceptorConfig1.4中的springmvc的扩展类LoginController处理用户登录的controller方法这个方法用来返回给前端tokenselectUserController在登录之后处理前端发过来的查询用户的请求 解释上面的过程用户在登陆之前还没有token登录之后通过调用TokenUtils来生成token并且返回给前端至此该用户就有了token在之后的请求中首先会被Interceptor-Config类拦截下来然后进入JwtInterceptor类进行token的校验成功后才会进入controller层否则就会抛出401的异常(这里的异常是自己手动设置的)然后就会返回给前端前端就会发生页面跳转跳转到login页面