阅读量:0
为实现Jwt简单的权限管理,我们需要用Jwt工具来生成token,也需要用Jwt来解码token,同时需要添加Jwt拦截器来决定放行还是拦截。下面来实现:
1、gradle引入Jwt、hutool插件
implementation 'com.auth0:java-jwt:3.10.3' implementation 'cn.hutool:hutool-all:5.3.7'
2、Jwt工具类,提供静态方法生成token,和根据请求携带的token查找user信息
package com.zzz.simple_blog_backend.utils; import ...... @Component public class JwtTokenUtils { @Autowired private UserService userService; private static UserService userServiceStatic; @PostConstruct//在spring容器初始化后执行该方法 public void setUserService() { userServiceStatic = userService; } //生成Token public static String genToken(String userId,String passwordSign) { return JWT.create().withAudience(userId)//放入载荷 .withExpiresAt(DateUtil.offsetHour(new Date(), 2))//2小时后过期 .sign(Algorithm.HMAC256(passwordSign));//密码签名作为密钥 } //通过token获取当前登录用户信息 public static User getCurrentUser() { String token = null; HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(); //1、获取token token = request.getHeader("token"); if (StrUtil.isBlank(token)) { token = request.getParameter("token"); } if (StrUtil.isBlank(token)) { throw new RuntimeException("没有token,请重新登录"); } String userId; User user; try { userId = JWT.decode(token).getAudience().get(0); } catch (Exception e) { throw new RuntimeException("token验证失败,请重新登录!!!"); } user = userServiceStatic.findById(Integer.parseInt(userId)); if(user==null) { throw new RuntimeException("用户id不存在,请重新登录!!!"); } //3、用密码签名,解码判断token try { JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build(); jwtVerifier.verify(token); } catch (JWTVerificationException e) { throw new CustomException(001, "token验证失败,请重新登录!!!"); } return user; } }
3、Jwt拦截器
SpringBoot添加拦截器,excludePathPatterns
可以指定不拦截的页面,RestController
指定了请求前缀,控制器类要用@RestController
代替@Controller
,addInterceptor
添加了jwtInterceptor
拦截器。
package com.zzz.simple_blog_backend.config; import ... @Configuration public class WebConfig implements WebMvcConfigurer{ @Autowired private JwtInterceptor jwtInterceptor; @Override public void configurePathMatch(PathMatchConfigurer configurer) { //指定restcontroller统一接口前缀 configurer.addPathPrefix("/api", clazz -> clazz.isAnnotationPresent(RestController.class)); } @Override public void addInterceptors(InterceptorRegistry registry) { // 加自定义拦截器 给特定请求放行 registry.addInterceptor(jwtInterceptor).addPathPatterns("/api/**") .excludePathPatterns("/api/user/login","/api/user/register"); } }
仔细的童靴会发现JwtTokenUtils.getCurrentUser()
方法和JwtInterceptor
的拦截方法很像,主要区别就在if(user.getRole()!=0)
的判断。所以JwtInterceptor
只会给管理员放行,如果需要给普通用户放行而未登录用户不放行,那请求路径先添加到excludePathPatterns
,并且控制类对应的响应方法第一句就先调用JwtTokenUtils.getCurrentUser()
判断是否用户已登录。
package com.zzz.simple_blog_backend.config; import ...... @Component public class JwtInterceptor implements HandlerInterceptor{ @Autowired private UserService userService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //1、获取token String token = request.getHeader("token"); if (StrUtil.isBlank(token)) { token = request.getParameter("token"); } if (StrUtil.isBlank(token)) { throw new RuntimeException("没有token,请重新登录"); } //2、开始认证 解码token,获得userId String userId; User user; try { userId = JWT.decode(token).getAudience().get(0); } catch (Exception e) { throw new RuntimeException("token验证失败,请重新登录!!!"); } user = userService.findById(Integer.parseInt(userId)); if(user==null) { throw new RuntimeException("用户id不存在,请重新登录!!!"); } if(user.getRole()!=0) { throw new RuntimeException("非管理员账号,无权访问!!!"); } //3、用密码签名,解码判断token try { JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build(); jwtVerifier.verify(token); } catch (JWTVerificationException e) { throw new RuntimeException("token验证失败,请重新登录!!!"); } //token验证成功,放行 return true; // return HandlerInterceptor.super.preHandle(request, response, handler); } }
4、前后端登录操作
登录后 后端返回带token不带密码的user数据
@PostMapping("user/login") @ResponseBody public CommonResult<Object> login(@RequestBody User user){ user = userService.findByUsernameAndPassword(user); if (user == null) { return CommonResult.failed(001, Message.createMessage("用户名或密码错误!!!")); } else { //生成用户对应的Token String token = JwtTokenUtils.genToken(user.getId().toString(), user.getPassword()); user.setToken(token); //不传输密码 user.setPassword(""); return CommonResult.success(user); } }
前端保存带token的user
//axios的post请求成功后操作 localStorage.setItem("user",JSON.stringify(response.data.data));//保存用户信息
5、前端每次请求都携带token信息
假设已安装axios,Axios.interceptors.request
拦截用户所有请求,添加token信息后再发送请求,这样后端就可以判断了。
import Axios from 'axios' Vue.prototype.$http = Axios; //添加向后端发起请求的服务器地址前缀 Axios.defaults.baseURL=AIOS_BASE_URL; // "http://127.0.0.1/api" //设置请求超时时间 Axios.defaults.timeout=5000; //axios拦截器 //对接口request拦截 Axios.interceptors.request.use(function(config){ //发起增删改查请求时,带上token认证 var user = localStorage.getItem("user"); if(user){ config.headers["token"] = JSON.parse(user).token; } return config; }) //携带证书 session id 跨域请求的话需要 Axios.defaults.withCredentials = true
总结
Jwt实现权限管理的原理是登录成功后 后端端生成token密钥,随着用户信息发送给客户端,客户端接受并保存信息到本地localStorage。以后每次需要权限验证时,根据客户端返回的携带token信息,后端进行拦截并解码校验,通过则放行,否则抛异常。如果要抛异常时,返回错误信息给前端,请看链接。