Jwt(Json web token)——使用token的权限验证方法 用户+角色+权限表设计 SpringBoot项目应用
目录
- 引出
- 使用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过期异常*/@Overridepublic 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,返回falseresponse.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 {@Autowiredprivate AuthInterceptor authInterceptor;@Overridepublic 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 {@Autowiredprivate 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
相关文章:
Jwt(Json web token)——使用token的权限验证方法 用户+角色+权限表设计 SpringBoot项目应用
目录 引出使用token的权限验证方法流程 用户、角色、权限表设计权限表角色表角色-权限关联表用户表查询用户的权限(四表联查)数据库的视图 项目中的应用自定义注解拦截器controller层DTO返回给前端枚举类型的json化日期json问题 实体类-DAO 总结 引出 1.…...
SpringWeb项目核心功能总结
SpringWeb项目核心功能总结 文章目录 SpringWeb项目核心功能总结1.浏览器与Java程序的连接(个人偏好使用RequestMapping)2.参数的传入3.结果的返回请求转发和请求重定向的区别 核心功能用到的注解: RestControllerControllerResponseBodyRequ…...
Django------信号
Django 框架包含了一个信号机制,它允许若干个发送者(sender)通知一组接收者(receiver)某些特定操作或事件(events)已经发生了, 接收者收到指令信号(signals)后再去执行特定的操作。本文主要讲解Django信号(…...
HTML5 中新增了哪些表单元素?
聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ HTML5 中新增了的表单元素⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅!这个专栏是为那些对Web开发感兴趣、刚…...
[考研机试] KY20 完数VS盈数 清华大学复试上机题 C++实现
描述 一个数如果恰好等于它的各因子(该数本身除外)子和,如:6321。则称其为“完数”;若因子之和大于该数,则称其为“盈数”。 求出2到60之间所有“完数”和“盈数”。 输入描述: 题目没有任何输入。 输出描述&#…...
re学习(30)攻防世界-hackme(代码复原2)
思路: 1.输出成功,v26不为0,说明关系式:v21((unsigned __int8)v24 ^ v20) →2.在汇编代码第37行,输入v16v20,所以求的值为v20 →3.根据关系式,求的值v20v21^v24 →4.v21在第汇编代码第36行也可以提取出来…...
Go Windows下开发环境配置(图文)
Go Windows下开发环境配置 下载 安装 点击下载的安装包进行安装。安装路径可以选择到自己的目录。 环境变量配置 GOROOT:(指定到安装目录下) GOPATH:(是工作空间) path:在安装时已经添加了…...
【人工智能概述】python妙用 __str__()
【人工智能概述】python妙用 str() 文章目录 【人工智能概述】python妙用 __str__()一.python内置函数__str__() 一.python内置函数__str__() 通过自定义__str__()函数可以打印对象中相关的内容。 class Person(object):def __init__(self, name tom, age 10):self.name n…...
android kernel移植5-RK3568
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言1.添加开发板默认配置文件2.添加开发板默认设备树2.1复制设备树2.2指定设备树前言 前面我们已经学会了移植uboot,其实就是把瑞芯微的关于uboot的一些文件的名字和编译指定的文件改为自己定义…...
C++——string类介绍
我们知道在C语言里,字符串是以\0结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数, 但是这些库函数与字符串是分离开的,而且底层空间需要用户自己管理,可 能还会越界访问。 但是在C…...
教雅川学缠论07-中枢实战众泰汽车000980
本文实战众泰汽车 下面是2023年11月14-2023年8月8众泰汽车日K图 先画日K 接下来处理包含,就变成下面这个样子 下面在套上缠论的理论,未来股价的走势应该是红色椭圆形虚线里面的样子 好了,文章就到这里,如果众泰最终不是这个走势…...
REDIS主从配置
目录 前言 一、概述 二、作用 三、缺点 四、redis主从复制的流程 五、搭建redis主从复制 总结 前言 Redis的主从配置是指在Redis集群中,将一个Redis节点配置为主节点(master),其他节点配置为从节点(slave)…...
【测试】软件测试工具JMeter简单用法
简明扼要,点到为止。 1. JMeter介绍 JMeter的全称是Apache JMeter,是一款用于软件测试的工具软件,其是开源免费的,由Apache基金会运营。 官网:Apache JMeter - Apache JMeter™ 2. 下载安装及运行 2.1 安装 Java8…...
五个授权关键,为智能驾驶量产赋能
站在风口浪尖的智能驾驶行业? 智能汽车是指通过搭载先进传感器等装置,运用人工智能等新技术,具有自动驾驶功能,逐步成为智能移动空间和应用终端的新一代汽车。集中运用了计算机、现代传感、信息融合、通讯、人工智能及自动控制等技…...
【代码随想录-Leetcode第三题:977. 有序数组的平方】
题目 给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。 示例 1: 输入:nums [-4,-1,0,3,10] 输出:[0,1,9,16,100] 解释:平方后,数组…...
[运维|中间件] Apache APISIX Dashboard部署(持续踩坑更新。。。)
参考文献 Apache APISIX v2.6 官方文档 APISIX、APISIX Dashboard搭建、路由配置及插件使用 安装apisix和apisix-dashboard,访问方式,测试路由转发,反向代理等 文档地址 Dashboard Doc 下载地址 Apache APISIX Dashboard下载地址 部署 …...
Vue中watch监听属性新旧值相同问题解决方案
侦听器 _watch: 作用:可以侦听data和computed中数据的变化. 语法 watch: { "被侦听的属性名" (newVal, oldVal){ } } 监听简单数据类型时可以直接使用,而监听复杂数据类型时,例如当我们只需要监听data或者computed中对象的某个属性时,可以使用字符串的形式进行监听…...
awk案例练习
目录 一、awk练习 1.1筛选ip地址 1.2字段去重 1.3次数统计 1.4统计TCP连接状态 1.5处理字段缺失的数据 1.6筛选给定时间范围内的日志 一、awk练习 1.1筛选ip地址 ifconfig命令查看IP 利用awk进行筛选 ifconfig | awk BEGIN{RS""}NR2{print $6} RS指定输入记…...
Debian 12.1 正式发布
导读Debian 12.1 现已发布,这是对稳定发行版 Debian 12(代号 Bookworm )的首次更新。本次发布主要增加了安全问题的修正,并对严重问题进行了一些调整。 一些更新内容包括: 妥善处理系统用户的创建;修复 eq…...
neo4j清空数据库
清空所有Person、 Movie节点及其所有关系 MATCH (a:Person), (m:Movie) OPTIONAL MATCH (a)-[r1]-(), (m)-[r2]-() DELETE a,r1,m,r2 查询任意数据 MATCH (n) RETURN n 如果没有, 就说明已经删除成功了 这段代码是用Cypher查询语言编写的,用于清空…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...
Appium下载安装配置保姆教程(图文详解)
目录 一、Appium软件介绍 1.特点 2.工作原理 3.应用场景 二、环境准备 安装 Node.js 安装 Appium 安装 JDK 安装 Android SDK 安装Python及依赖包 三、安装教程 1.Node.js安装 1.1.下载Node 1.2.安装程序 1.3.配置npm仓储和缓存 1.4. 配置环境 1.5.测试Node.j…...
JavaScript 标签加载
目录 JavaScript 标签加载script 标签的 async 和 defer 属性,分别代表什么,有什么区别1. 普通 script 标签2. async 属性3. defer 属性4. type"module"5. 各种加载方式的对比6. 使用建议 JavaScript 标签加载 script 标签的 async 和 defer …...
