Jwt(Json web token)——使用token的权限验证方法 用户+角色+权限表设计 SpringBoot项目应用

news2025/4/9 22:24:14

目录

  • 引出
  • 使用token的权限验证方法
    • 流程
  • 用户、角色、权限表设计
    • 权限表
    • 角色表
    • 角色-权限关联表
    • 用户表
    • 查询用户的权限(四表联查)
    • 数据库的视图
  • 项目中的应用
    • 自定义注解
    • 拦截器
    • controller层
    • DTO返回给前端
      • 枚举类型的json化
      • 日期json问题
    • 实体类-DAO
  • 总结

引出


1.认证鉴权服务,注册中心,认证中心,鉴权中心;
2.用户,角色,权限表设计,数据库视图的使用;
3.项目中的应用,使用自定义注解+拦截器;
4.枚举类型的json化, @JsonFormat(shape = JsonFormat.Shape.OBJECT) @Getter

https://gitee.com/pet365/springboot-privs-token

在这里插入图片描述

使用token的权限验证方法

在这里插入图片描述

流程

在这里插入图片描述

用户、角色、权限表设计

用户和权限之间关系(多对多)

在这里插入图片描述

中间内容: 角色

在这里插入图片描述

(本系统中: user—》角色(one-to-Many)

在这里插入图片描述

权限表

在这里插入图片描述

在这里插入图片描述

角色表

在这里插入图片描述

在这里插入图片描述

角色-权限关联表

在这里插入图片描述

在这里插入图片描述

用户表

在这里插入图片描述

在这里插入图片描述

查询用户的权限(四表联查)

-- 用户角色-权限模型

SELECT 
user_owner.username,privs_role_tab.role_name,privs_tab.privs_name
FROM user_owner
LEFT JOIN privs_role_tab ON user_owner.user_role = privs_role_tab.role_id
LEFT JOIN privs_relationship_tab ON privs_relationship_tab.rp_role = privs_role_tab.role_id
LEFT JOIN privs_tab ON privs_tab.privs_id=privs_relationship_tab.rp_privs

数据库的视图

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

项目中的应用

https://gitee.com/pet365/springboot-privs-token

在这里插入图片描述

自定义注解

PrivsCheck.java文件

package com.tianju.auth.util;

import java.lang.annotation.*;

/**
 * 定义注解
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PrivsCheck {
    String value() default "";
}

拦截器

在这里插入图片描述

在这里插入图片描述

AuthInterceptor.java文件

package com.tianju.auth.interceptor;

import cn.hutool.json.JSONUtil;
import com.tianju.auth.dto.HttpResp;
import com.tianju.auth.dto.ResultCode;
import com.tianju.auth.util.JwtUtil;
import com.tianju.auth.util.PrivsCheck;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Date;

@Component
@Slf4j
public class AuthInterceptor implements HandlerInterceptor {
    /**
     *
     * @param request 请求
     * @param response 响应
     * @param handler =====>handler====>:class org.springframework.web.method.HandlerMethod
     *                com.tianju.auth.controller.UserController#findAllUsernames()
     *                类 的 findAllUsernames() 方法
     *                Method method = handlerMethod.getMethod();// controller里面的方法findAllUsernames 对象
     *                Annotation[] annotations = method.getDeclaredAnnotations();// 可以获得该方法上的所有注解
     * @return 是否拦截
     * @throws Exception token过期异常
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");
        response.setContentType("text/html;charset=utf-8");
        if (token==null){ // 如果没有token,返回false
            response.getWriter().write(
                    JSONUtil.toJsonStr(
                            HttpResp.results(
                                    ResultCode.USER_NOT_LOGIN_ERROR, new Date(),"用户没有登陆异常"
                            )));
            return false;
        }
        // 存在的用户,检验token是否过期

        try {
            // 正常情况
            String username = JwtUtil.getClaim(token, "username");
            String privs = JwtUtil.getClaim(token, "privs");
            // 判断是否能够访问当前的方法
            log.info("用户权限{}",privs);
            // handler:com.tianju.auth.controller.UserController#findAllUsernames()
            log.debug("handler:{}",handler);
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();// controller里面的方法findAllUsernames 对象
            Annotation[] annotations = method.getDeclaredAnnotations();// 可以获得该方法上的所有注解
            /**
                 * @org.springframework.web.bind.annotation.GetMapping(path=[], headers=[], name=, produces=[], params=[], value=[/findAllUsernames], consumes=[])
                 * @io.swagger.annotations.ApiOperation(code=200, notes=, hidden=false, authorizations=[@io.swagger.annotations.Authorization(scopes=[@io.swagger.annotations.AuthorizationScope(scope=, description=)], value=)], httpMethod=, tags=[查询所有用户名], extensions=[@io.swagger.annotations.Extension(name=, properties=[@io.swagger.annotations.ExtensionProperty(parseValue=false, name=, value=)])], responseHeaders=[@io.swagger.annotations.ResponseHeader(name=, responseContainer=, description=, response=class java.lang.Void)], response=class java.lang.Void, responseReference=, responseContainer=, produces=, nickname=, ignoreJsonView=false, position=0, protocols=, consumes=, value=findAllUsernames)
                 * @com.tianju.auth.util.PrivsCheck(value=findAllUsernames)
             */
//            for (Annotation annotation: annotations) {
//                /**
//                 * @org.springframework.web.bind.annotation.GetMapping(path=[], headers=[], name=, produces=[], params=[], value=[/findAllUsernames], consumes=[])
//                 * @io.swagger.annotations.ApiOperation(code=200, notes=, hidden=false, authorizations=[@io.swagger.annotations.Authorization(scopes=[@io.swagger.annotations.AuthorizationScope(scope=, description=)], value=)], httpMethod=, tags=[查询所有用户名], extensions=[@io.swagger.annotations.Extension(name=, properties=[@io.swagger.annotations.ExtensionProperty(parseValue=false, name=, value=)])], responseHeaders=[@io.swagger.annotations.ResponseHeader(name=, responseContainer=, description=, response=class java.lang.Void)], response=class java.lang.Void, responseReference=, responseContainer=, produces=, nickname=, ignoreJsonView=false, position=0, protocols=, consumes=, value=findAllUsernames)
//                 * @com.tianju.auth.util.PrivsCheck(value=findAllUsernames)
//                 */
//            }
            PrivsCheck annotation = method.getDeclaredAnnotation(PrivsCheck.class);
            System.out.println(annotation.value());
            if (privs.contains(annotation.value())){ // 有此权限
                return true;
            }else {
                response.getWriter().write(
                        JSONUtil.toJsonStr(
                                HttpResp.results(
                                        ResultCode.USER_ACCESS_ERROR, new Date(),"对不起权限不足"
                                )));
                return false;
            }


        }catch (ExpiredJwtException e){
            // token过期
            response.getWriter().write(
                    JSONUtil.toJsonStr(
                            HttpResp.results(
                                    ResultCode.USER_LOGIN_TOKEN_EXPIRED_ERROR, new Date(),"token过期"
                            )));
            return false;

        }

    }
}

拦截器的配置AuthConfig.java文件

package com.tianju.auth.config;

import com.tianju.auth.interceptor.AuthInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class AuthConfig implements WebMvcConfigurer {

    @Autowired
    private AuthInterceptor authInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authInterceptor)
                .addPathPatterns("/api/**")
                .excludePathPatterns("/api/user/login");
    }
}

controller层

package com.tianju.auth.controller;

import com.tianju.auth.dto.HttpResp;
import com.tianju.auth.dto.ResultCode;
import com.tianju.auth.entity.UserPrivs;
import com.tianju.auth.service.IUserService;
import com.tianju.auth.util.JwtUtil;
import com.tianju.auth.util.PrivsCheck;
import io.swagger.annotations.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.List;

@RestController
@RequestMapping("/api/user")
@Api(tags = "用户api接口")
public class UserController {

    @Autowired
    private IUserService userService;

    @ApiOperation(value = "login",tags = "用户登录接口")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "username",value = "用户名",required = true),
            @ApiImplicitParam(name = "password",value = "密码",required = true)

    })
    @GetMapping("/login")
    public HttpResp login(String username, String password, HttpServletResponse response){
        List<UserPrivs> users = userService.login(username, password);
        StringBuilder privs = new StringBuilder();
        users.forEach(userPrivs -> privs.append(userPrivs.getPrivsName()+","));
        System.out.println("用户权限:"+privs);
        privs.deleteCharAt(privs.length()-1);
        String token = JwtUtil.createToken(username, privs.toString(), 1000 * 60);
        response.addHeader("token", token);
        return HttpResp.results(ResultCode.USER_LOGIN_SUCCESS,new Date(),username);
    }

    @GetMapping("/findAllUsernames")
    @ApiOperation(value = "findAllUsernames",tags = "查询所有用户名")
//    @PrivsCheck("findAllUsernames")
    @PrivsCheck("findX") // 设置一个没有的权限
    public HttpResp findAllUsernames(){
        List<String> allUsernames = userService.findAllUsernames();
        return HttpResp.results(ResultCode.USER_QUERY_SUCCESS,new Date(),allUsernames);
    }
}

DTO返回给前端

枚举类型的json化

在这里插入图片描述

@JsonFormat(shape = JsonFormat.Shape.OBJECT)

package com.tianju.auth.dto;

import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;


/**
 * 枚举类型,http请求的返回值
 */
// 枚举类型的json化,需要有get方法
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
@Getter
public enum ResultCode {
    BOOK_RUSH_SUCCESS(20010,"图书抢购成功"),
    BOOK_RUSH_ERROR(3001,"图书抢购失败"),
    LUA_SCRIPT_ERROR(3002,"Lua脚本操作失败"),
    USER_FIND_ERROR(40010,"非法请求,布隆过滤器不通过"),
    USER_FIND_SUCCESS(20010,"查询用户名成功"),
    USER_QUERY_SUCCESS(25010,"查询所有用户名成功"),
    USER_LOGIN_ERROR(40030,"用户登陆失败"),
    USER_NOT_LOGIN_ERROR(40040,"用户没有登陆异常"),
    USER_LOGIN_TOKEN_EXPIRED_ERROR(42040,"token已过期异常"),
    USER_ACCESS_ERROR(45040,"用户权限异常"),
    USER_LOGIN_SUCCESS(20020,"用户登陆成功"),
    ;

    @ApiModelProperty("状态码")
    private Integer code;

    @ApiModelProperty("提示信息")
    private String msg;

    private ResultCode(Integer code,String msg){
        this.code =code;
        this.msg = msg;
    }
}

日期json问题

@JsonFormat(timezone = “GMT+8”)

package com.tianju.auth.dto;

import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.Date;

/**
 * 返回给前端的响应
 * @param <T>
 */
@ApiModel("DTO返回数据")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class HttpResp<T> implements Serializable {
    private ResultCode resultCode;
    @ApiModelProperty("time")
    @JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss",timezone = "GMT+8")
    private Date time;
    @ApiModelProperty("results")
    private T result;

    public static <T> HttpResp <T> results(
            ResultCode resultCode,
            Date time,
            T results){

        HttpResp httpResp = new HttpResp();
        httpResp.setResultCode(resultCode);
        httpResp.setTime(time);
        httpResp.setResult(results);
        return httpResp;
    }
}

实体类-DAO

package com.tianju.auth.entity;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("user_privs_view")
public class UserPrivs {
    @TableField("username")
    private String username;
    @TableField("password")
    private String password;
    @TableField("role_name")
    private String roleName;
    @TableField("privs_name")
    private String privsName;
}

dao

package com.tianju.auth.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tianju.auth.entity.UserPrivs;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper<UserPrivs> {
}


总结

1.认证鉴权服务,注册中心,认证中心,鉴权中心;
2.用户,角色,权限表设计,数据库视图的使用;
3.项目中的应用,使用自定义注解+拦截器;
4.枚举类型的json化, @JsonFormat(shape = JsonFormat.Shape.OBJECT) @Getter

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/849861.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

学习pytorch

学习pytorch 1. 环境安装配置镜像源conda命令记录遇到的问题1. torch.cuda.is_available() False 1. 环境安装 B站小土堆视频 配置镜像源 conda config --show channels conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/mainhttp://www.m…

leetcode 图算法小结

文章目录 1 DFS和BFS797. 所有可能的路径200. 岛屿数量 1 DFS和BFS 深度优先遍历一般采用回溯算法进行解决。回溯算法&#xff0c;其实就是dfs的过程。 void dfs(参数) {处理节点dfs(图&#xff0c;选择的节点); // 递归回溯&#xff0c;撤销处理结果 }广度优先搜索理解为层次…

Java-数据类型

数据类型 数据类型基本数据类型整形浮点字符型布尔类字节 引用数据类型类型转换显式转换隐式转换注意事项 整型提升 作为学习Java的入门知识,在刚开始面试的几场,表现不太好的时候,就有几个面试官会问这个问题,估计此时此刻我在他们的心目中也就是这个问题的层次了吧…当然,当时…

Linux网络服务之自动装机(PXE+KICKSTART)详解

自动装机 一、启动操作系统的方式1.1 系统装机的三种引导方式1.2 系统安装过程1.3 三大文件1.4 如何实现自动装机&#xff1f; 二、PXE2.1 PXE的简介和优点2.2 实现PXE的前提条件2.3 PXE实现过程2.5 要安装的服务2.6 实现PXE2.6.1 前置准备2.6.2 安装并配置DHCP2.6.3 安装并配置…

Nginx负载均衡搭建

目录 1、准备一台装有nginx服务的主机 2、所需模块说明&#xff1a; 3、两台Web服务器主机 4、 修改nginx的配置文件 5、查看结果&#xff1a; 1、准备一台装有nginx服务的主机 LVS—DR集群的搭建_.98℃的博客-CSDN博客 2、所需模块说明&#xff1a; Nginx http 功能模…

【音视频】vms布署说明

目录 外场布署场景&#xff08;99%&#xff09; 研发实验场景&#xff08;1%&#xff09; 高级玩法 证书安装方法 外场布署场景&#xff08;99%&#xff09; 下面两种场景&#xff0c;为本产品主要应用场景&#xff0c;2023-08-08日后&#xff08;统一所有证书&#xff09;…

入门平台工程的福音,麦肯锡刚发布了平台工程蓝图

在软件开发和工程效能领域&#xff0c;平台工程 (Platform Engineering) 是继 DevOps 后逐渐兴起的主流概念。平台工程&#xff0c;顾名思义&#xff0c;就是通过组合一系列标准化的软件开发工具&#xff0c;构建起一个标准化的研发平台。目标则是为了提高开发者体验和生产力。…

构建Docker容器监控系统 (1)(Cadvisor +InfluxDB+Grafana)

目录 Cadvisor InfluxDBGrafana 1. Cadvisor 2.InfluxDB 3.Grafana 开始部署&#xff1a; 下载组件镜像 创建自定义网络 创建influxdb容器 创建数据库和数据库用户 创建Cadvisor 容器 准备测试镜像 创建granafa容器 访问granfana 添加数据源 Add data source 新建 …

python接口自动化之自动发送测试报告邮件

前言 ​ SMTP&#xff08;Simple Mail Transfer Protocol&#xff09;也就是简单邮件传输协议&#xff0c;是一种提供可靠且有效电子邮件传输的协议。python的smtplib模块就提供了一种很方便的途径发送电子邮件&#xff0c;它对smtp协议进行了简单的封装。 ​ python发邮件主…

四 、Mysql 开发

四 、Mysql开发 102 可以使用MySQL直接存储文件吗&#xff1f; 可以使用 BLOB (binary large object)&#xff0c;用来存储二进制大对象的字段类型。 TinyBlob 255 值的长度加上用于记录长度的1个字节(8位) Blob 65K值的长度加上用于记录长度的2个字节(16位) MediumBlob 16M值…

AutoJS自定义悬浮菜单(附完整代码)

我们在开发Autojs脚本时&#xff0c;需要使用到悬浮窗功能来控制脚本。那么到底要如何来做呢&#xff1f;今天给大家分享一些&#xff0c;先来看看效果&#xff1a; 调整移动和贴边。 防止滑出屏幕 附上完整代码 var storage storages.create("日赚3万_短视频合集&quo…

以Java的方式将文件上传到阿里云OSS

文章目录 1. 开通对象存储服务2. 创建 AccessKey 密钥3. 通用代码实现 1. 开通对象存储服务 控制台 → 对象存储 OSS → 立即开通 Bucket列表 → 点击创建 Bucket 填写名称、地域&#xff0c;名称创建后不可修改&#xff0c;地域选择最近的&#xff0c;存储类型选择标准存储&…

OpenCV: 对“google::protobuf::internal::Release_CompareAndSwap”的未定义

解决办法&#xff1a; 需要在文件 protobuf/src/google/protobuf/stubs/atomicops_internals_generic_gcc.h 中的以下补丁 inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, Atomic64 old_value, …

Ajax-AJAX请求的不同发送方式

&#x1f954;&#xff1a;你一定能成为想要成为的人 发送AJAX请求不同方式 发送AJAX请求不同方式1、jQuery发送AJAX请求2、axios发送AJAX请求&#xff08;重点&#xff09;3、fetch发送AJAX请求 发送AJAX请求不同方式 1、jQuery发送AJAX请求 首先需要jquery的js文件&#xf…

Linux shell yes命令(不停输出换行的y)(不停输出换行的指定字符串)(脚本自动确认y)

文章目录 yes命令功能doc文档英文中文翻译完整文档 示例应用案例自动为脚本多次确认y yes命令功能 yes命令可以不断地输出换行的指定字符串&#xff0c;不加参数时&#xff0c;不断输出换行的“y”&#xff0c;有时我们需要执行一些需要用户键入“y”确认的脚本&#xff0c;但…

挖掘Java集合:深入探索List接口与HashSet

文章目录 引言LinkedList&#xff1a;双向链表的实现构造方法LinkedList中的常用方法HashSet&#xff1a;无序且唯一的集合HashSet的实现方式LinkedHashSet&#xff1a;有序且唯一可变长度参数结论 引言 在广阔的Java编程领域中&#xff0c;集合就如同宝库&#xff0c;提供了多…

Clickhouse学习系列——一条SQL完成gourp by分组与不分组数值计算

笔者在近一两年接触了Clickhouse数据库&#xff0c;在项目中也进行了一些实践&#xff0c;但一直都没有一些技术文章的沉淀&#xff0c;近期打算做个系列&#xff0c;通过一些具体的场景将Clickhouse的用法进行沉淀和分享&#xff0c;供大家参考。 首先我们假设一个Clickhouse数…

python基础3——流程控制

文章目录 一、操作符1.1 比较操作符1.2 逻辑操作符1.3 成员操作符1.4 身份操作符 二、流程控制2.1 条件判断2.2 循环语句2.2.1 for循环2.2.2 while循环 2.3 continue与break语句2.4 文件操作函数 三、函数3.1 定义函数3.2 作用域3.3 闭包3.4 函数装饰器3.5 内建函数 一、操作符…

一文看懂 D类数字功放原理及应用

他V hezkz17进数字音频系统研究开发交流答疑群(课题组) D类放大器工作在开关状态&#xff0c;作为控制元件的晶体管本身消耗功率较低&#xff0c;功放的效 率就高&#xff0c;可达 90%以上&#xff0c;因此能较大地降低能源损耗&#xff0c;减小放大器体积。D 类音 频功率放大器…

面试热题(最长上升子序列)

给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。 输入&#xff1…