Springboot3+Vue3实现JWT登录鉴权

做鉴权原因:

管理系统的数据是敏感的,隐私的,每个角色的权限是不同的,必须在数据的增删改查操作时候对访问的用户进行权限验证

JWT(Json Web Token)

用于在网络应用间安全的传递消息。它以紧凑且自包含的方式,通过JSON对象在各方之间传递经过验证的信息。JWT通常由三部分组成,用点号(.)分隔:header.payload.signature

集成JWT(在pom中引入依赖)

<!--java-JWT坐标 -->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>4.4.0</version>
        </dependency>

生成token

/**
     * 生成 JWT 令牌
     */
    public static String createToken(String data,String sign) {
        return JWT.create().withAudience(data)//将userid-role保存到token里面作为载荷
                .withExpiresAt(DateUtil.offsetDay(new Date(),1))//1天后token过期
                .sign(Algorithm.HMAC256(sign));//以password作为token的密钥,使用HMAC256算法加密
    }

在***Service中创建token返回前端

String token = TokenUtil.createToken(dbUser.getId()+"-"+"管理员",dbUser.getPassword());
dbUser.setToken(token);

 

Token格式

"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIxLeeuoeeQhuWRmCIsImV4cCI6MTc0MjkyMTk5Mn0.PH2OJMzhqZFuJz-aW5nWfE5wZk9fbM-tgxPql1_NNVI"

JWT拦截器对所有访问的接口进行验证

通过webConfig做一层拦截器拦截所有的接口

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor())
                .addPathPatterns("/**")//校验规则所有接口
                .excludePathPatterns("/login","/register");//排除登录和注册接口
    }
    @Bean
    public JWTInterceptor jwtInterceptor(){
        return new JWTInterceptor();
    }
}

JWT拦截器

/**
 * JWT拦截器
 * 做拦截器的实现
 * 对Token进行拦截并进一步解析Token、验证Token,看看Token是否是合法的
 */
@Component
public class JWTInterceptor implements HandlerInterceptor {
    @Resource
    private UserService userService;
    @Resource
    private ZuKeService zuKeService;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1.从请求头拿到Token
        String token=request.getHeader("token");
        if (StrUtil.isEmpty(token)){
            //如果没拿到,从参数中再拿一次
            token=request.getParameter("token");
        }
        //2.认证Token
        if (StrUtil.isBlank(token)){
            throw new CustomException("401","您无权操作");
        }
        Account account=null;
        try {
            //拿到Token载荷数据
            String audience = JWT.decode(token).getAudience().get(0);
            String[] split=audience.split("-");
            String userId=split[0];
            String role=split[1];
            //柑橘Token解析出来的userId去对应的表查询信息
            if ("管理员".equals(role)){
                account=userService.selectById(userId);
            } else if ("租客".equals(role)) {
                account=zuKeService.selectById(userId);
            }
        } catch (Exception e) {
            throw new CustomException("401","您无权操作");
        }
        if (account==null){
            throw new CustomException("401","您无权操作");
        }
        try {
            //用户加签 验证签名
            JWTVerifier jwtVerifier=JWT.require(Algorithm.HMAC256(account.getPassword())).build();
            jwtVerifier.verify(token);
        } catch (Exception e) {
            throw new CustomException("401","您无权操作");
        }
        return true;
    }
}

出现401错误,无权访问数据怎么办

 在Vue的request.js的拦截器里面添加统一的请求头Token

 request.js的代码

import axios from "axios";
import {ElMessage} from "element-plus";

const request = axios.create({
    baseURL:'http://localhost:8080',//后端统一的请求地址
    timeout:30000 //后台接口时间
})
//request 拦截器
//可以自请求发送前对请求做一些处理
request.interceptors.request.use(config =>{
    //统一的数据传输格式为json,统一的编码utf-8
    config.headers['Content-Type']='application/json;charset=utf-8';
    //let user=JSON.parse(localStorage.getItem('pro1-user') || '{}');
    //config.headers['token']=user.token;
    // ✅ 安全获取 user(兼容 null 和异常情况)
    let user = {};
    try {
        const userStr = localStorage.getItem('pro1-user');
        user = userStr ? JSON.parse(userStr) : {};
    } catch (e) {
        console.error('解析 pro1-user 失败:', e);
    }

    // 仅当 token 存在时才添加到 headers
    if (user.token) {
        config.headers['token'] = user.token;
    } else {
        console.warn('Token 不存在,请求可能被后端拒绝');
        // 可选:跳转到登录页
        // window.location.href = '/login';
    }
    return config;
},error=>{
    return Promise.reject(error)
});
//response拦截器
//可以在接口响应后统一处理结果
request.interceptors.response.use(
    response =>{
        let res=response.data;
        //兼容服务端返回的字符串数据
        if(typeof  res === 'string'){
            //如果是string,转成json
            res = res ? JSON.parse(res) : res
        }
        if (res.code === '401'){
            ElMessage.error(res.msg);
            router.push('/login')
        }else {
            return res;
        }

    },
    error =>{
        //后端返回数据判断
        if (error.response.status === 404){
            ElMessage.error('未找到请求接口')
        }else if (error.response.status === 500){
            ElMessage.error('系统异常,请查看后端控制台报错')
        }else{
            console.error(error.message)
        }
        return Promise.reject(error)
    }
)
export default request

获取当前登录用户信息

@Component
public class TokenUtil {
    @Resource
    UserService userService;
    @Resource
    ZuKeService zuKeService;
    static UserService stasticUserService;
    static ZuKeService stasticZuKeService;

    /**
     * 生成 JWT 令牌
     */
    public static String createToken(String data,String sign) {
        return JWT.create().withAudience(data)//将userid-role保存到token里面作为载荷
                .withExpiresAt(DateUtil.offsetDay(new Date(),1))//1天后token过期
                .sign(Algorithm.HMAC256(sign));//以password作为token的密钥,使用HMAC256算法加密
    }

    /**
     * 获取当前登录用户信息
     * @return
     */
    public static Account getCurrentUser(){
        Account account=null;
        HttpServletRequest request=((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
        String token=request.getHeader("token");
        if (StrUtil.isEmpty(token)){
            //如果没拿到,从参数中再拿一次
            token=request.getParameter("token");
        }
        //拿到Token载荷数据
        String audience = JWT.decode(token).getAudience().get(0);
        String[] split=audience.split("-");
        String userId=split[0];
        String role=split[1];
        if ("管理员".equals(role)){
            return stasticUserService.selectById(userId);
        }else if ("租客".equals(role)){
            return stasticZuKeService.selectById(userId);
        }
        return null;
    }
}

在service方法里面获取当前登录用户的信息

 Account currentUser=TokenUtil.getCurrentUser();
posted @ 2025-03-27 01:07  师大无语  阅读(615)  评论(0)    收藏  举报