SpringBoot Vue使用Jwt实现简单的权限管理

19 篇文章 1 订阅
17 篇文章 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代替@ControlleraddInterceptor添加了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信息,后端进行拦截并解码校验,通过则放行,否则抛异常。如果要抛异常时,返回错误信息给前端,请看链接

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值