目录
1、前言
2、实现思路
3、编码实战
3.1、准备
3.2、数据库表准备
3.3、自定义注解
3.4、拦截器
3.5、接口使用
3.6、测试
3.7、结论
4、结束语
1、前言
学过Spring Security的小伙伴都知道,SpringBoot项目可以集成Spring Security做权限校验框架,然后在Controller接口上直接使用@PreAuthorize注解来校验权限,但是如果我不想引入像Security、Shiro等第三方框架,也要实现权限校验的效果,该怎么做呢?
接下来就给大家介绍一种方案:拦截器+自定义注解做基于RBAC模型的权限校验
2、实现思路首先数据库需要有基于RBAC模型的表,我们这里用最简单的5张表做示例:用户表、角色表、权限表、用户角色关联表、角色权限关联表
用户每次登录成功后,都需要联表查询该用户拥有的权限集合字符串,加密到token串中(此处可以使用JWT),返回给前端,前端存储到浏览器中,以后每次请求后台接口时,都将此token取出,放到请求头信息中,带到后台
然后新建自定义注解,该注解用于接口上面,表示该接口需要校验访问者的权限
新建拦截器,对所有请求方法的请求进行拦截,然后判断是否有自定义注解,如果有的话,取出权限字符串,和请求头中token的权限字符串集合进行比对,如果包含其中,则说明有权限访问该方法,如果不包含其中,就说明该用户没有权限访问该方法
这里我准备了一个SpringBoot基础环境代码,我会在此基础上进行集成,代码我已上传,地址如下:
SpringBootbase: SpringBoot基础项目框架
基础薄弱的同学,可以下载下来,跟我下面的步骤一步一步走,就可以出来效果了,再集成到你自己的项目中
3.2、数据库表准备5张表准备:
用户表:
CREATE TABLE `sys_user` ( `user_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID', `dept_id` bigint(20) DEFAULT NULL COMMENT '部门ID', `position_id` varchar(5) DEFAULT NULL COMMENT '职位ID', `class_id` bigint(20) DEFAULT NULL COMMENT '班级ID', `user_name` varchar(30) NOT NULL COMMENT '用户账号', `nick_name` varchar(30) NOT NULL COMMENT '用户昵称', `user_type` varchar(2) DEFAULT '00' COMMENT '用户类型(00系统用户)', `email` varchar(50) DEFAULT '' COMMENT '用户邮箱', `phonenumber` varchar(11) DEFAULT '' COMMENT '手机号码', `sex` char(1) DEFAULT '0' COMMENT '用户性别(0男 1女 2未知)', `avatar` varchar(300) DEFAULT '' COMMENT '头像地址', `password` varchar(100) DEFAULT '' COMMENT '密码', `status` char(1) DEFAULT '0' COMMENT '帐号状态(0正常 1停用)', `del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', `login_ip` varchar(128) DEFAULT '' COMMENT '最后登录IP', `login_date` datetime DEFAULT NULL COMMENT '最后登录时间', `create_by` varchar(64) DEFAULT '' COMMENT '创建者', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `update_by` varchar(64) DEFAULT '' COMMENT '更新者', `update_time` datetime DEFAULT NULL COMMENT '更新时间', `remark` varchar(500) DEFAULT NULL COMMENT '备注', PRIMARY KEY (`user_id`), UNIQUE KEY `un_user_name` (`user_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息表';
角色表:
CREATE TABLE `sys_role` ( `role_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '角色ID', `role_name` varchar(30) NOT NULL COMMENT '角色名称', `role_key` varchar(100) DEFAULT NULL COMMENT '角色权限字符串', `role_sort` int(4) DEFAULT NULL COMMENT '显示顺序', `status` char(1) NOT NULL DEFAULT '0' COMMENT '角色状态(0正常 1停用)', `del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', `create_by` varchar(64) DEFAULT '' COMMENT '创建者', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `update_by` varchar(64) DEFAULT '' COMMENT '更新者', `update_time` datetime DEFAULT NULL COMMENT '更新时间', `remark` varchar(500) DEFAULT NULL COMMENT '备注', PRIMARY KEY (`role_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色信息表';
权限表:
CREATE TABLE `sys_menu` ( `menu_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '菜单ID', `menu_name` varchar(50) NOT NULL COMMENT '菜单名称', `parent_id` bigint(20) DEFAULT '0' COMMENT '父菜单ID', `order_num` int(4) DEFAULT '0' COMMENT '显示顺序', `path` varchar(200) DEFAULT '' COMMENT 'menu_id全路径', `component` varchar(255) DEFAULT NULL COMMENT '组件路径', `query` varchar(255) DEFAULT NULL COMMENT '路由参数', `is_frame` int(1) DEFAULT '1' COMMENT '是否为外链(0是 1否)', `is_cache` int(1) DEFAULT '0' COMMENT '是否缓存(0缓存 1不缓存)', `menu_type` char(1) DEFAULT '' COMMENT '菜单类型(M目录 C菜单 F按钮)', `visible` char(1) DEFAULT '0' COMMENT '菜单状态(0显示 1隐藏)', `status` char(1) DEFAULT '0' COMMENT '菜单状态(0正常 1停用)', `perms` varchar(100) DEFAULT NULL COMMENT '权限标识', `icon` varchar(100) DEFAULT '#' COMMENT '菜单图标', `create_by` varchar(64) DEFAULT '' COMMENT '创建者', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `update_by` varchar(64) DEFAULT '' COMMENT '更新者', `update_time` datetime DEFAULT NULL COMMENT '更新时间', `remark` varchar(500) DEFAULT '' COMMENT '备注', PRIMARY KEY (`menu_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='菜单权限表';
用户角色关联表:
CREATE TABLE `sys_user_role` ( `user_id` bigint(20) NOT NULL COMMENT '用户ID', `role_id` bigint(20) NOT NULL COMMENT '角色ID', PRIMARY KEY (`user_id`,`role_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户和角色关联表';
角色权限关联表:
CREATE TABLE `sys_role_menu` ( `role_id` bigint(20) NOT NULL COMMENT '角色ID', `menu_id` bigint(20) NOT NULL COMMENT '菜单ID', PRIMARY KEY (`role_id`,`menu_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色和菜单关联表';
3表联查,传入用户ID就可以查询出该用户拥有哪些权限字符串了,SQL如下:
select * from sys_menu t1 left join sys_role_menu t2 on t1.menu_id = t2.menu_id left join sys_user_role t3 on t2.role_id = t3.role_id where t3.user_id = #{userId}3.3、自定义注解
新建一个自定义注解:
package org.wujiangbo.annotation; import java.lang.annotation.*; @Target({ ElementType.PARAMETER, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface CheckPermission { String per() default ""; }3.4、拦截器
新建拦截器,代码如下:
package org.wujiangbo.interceptor; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.wujiangbo.annotation.CheckPermission; import org.wujiangbo.exception.MyException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; @Component public class CheckPermissionInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){ if (!(handler instanceof HandlerMethod)) { //说明拦截到的请求,不是请求方法的,那就直接放行 return true; } //指定到这里,说明请求的是方法 HandlerMethod handlerMethod = (HandlerMethod) handler; //获取方法对象 Method method = handlerMethod.getMethod(); //获取方法上面的 CheckPermission 注解对象 CheckPermission methodAnnotation = method.getAnnotation(CheckPermission.class); if (methodAnnotation != null) { //获取权限字符串 String per = methodAnnotation.per(); if(StringUtils.isNotBlank(per)){ //这里我们造点测试数据,假设从token中解析出当前登录用户的权限集合如下 ListuserPermissionList = new ArrayList<>(); userPermissionList.add("user:addUser"); userPermissionList.add("user:deleteUser"); userPermissionList.add("user:updateUser"); userPermissionList.add("user:pageList"); //开始判断 if(!userPermissionList.contains(per)){ throw new MyException("您暂无权限进行此 *** 作"); } } } //必须返回true,否则会拦截掉所有请求,不会执行controller方法中的内容了 return true; } }
需要将上面的拦截器类加入到web环境中,新建下面配置类:
package org.wujiangbo.config; 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.WebMvcConfigurationSupport; import org.wujiangbo.interceptor.CheckPermissionInterceptor; @Configuration public class WebConfig extends WebMvcConfigurationSupport { @Autowired private CheckPermissionInterceptor checkPermissionInterceptor; //添加自定义拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(checkPermissionInterceptor).addPathPatterns(" @RestController @Slf4j public class TestController { //用户新增接口 @GetMapping("/user/addUser") @CheckPermission(per = "user:addUser") //表示访问者需要具备 user:addUser 权限才能访问该接口 public JSonResult addUser(){ return JSONResult.success("addUser success"); } //部门新增接口 @GetMapping("/dept/addDept") @CheckPermission(per = "dept:addDept") //表示访问者需要具备 dept:addDept 权限才能访问该接口 public JSonResult addDept(){ return JSONResult.success("addDept success"); } }3.6、测试
用postman工具测试,先访问新增用户接口:http://localhost:8001/user/addUser
再访问新增部门接口:http://localhost:8001/dept/addDept
3.7、结论
从测试结果可以看出,确实可以做到接口的权限校验,以后使用的话,可以直接在controller加个注解就可以了,非常的方便
4、结束语
这样的权限校验在实际工作中也有应用场景,希望大家都可以掌握哦
大家如果还有任何疑问,可以留言,我会第一时间回复的
最后别忘点赞哦,非常感谢大家的支持与厚爱,我会不断分享更多干货的哈
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)