RBAC 权限模型介绍

news2024/9/30 21:23:41

RBAC 权限:

一、关系:

在这里插入图片描述

这基于角色的访问控制的结构就叫RBAC结构。

二、RBAC 重要对象:

  • 用户(Employee):角色施加的主体;用户通过拥有某个或多个角色以得到对应的权限。
  • 角色(Role):表示一组权限的集合。
  • 权限(Permission):一个资源代表一个权限,是否能访问该资源,就是看是否有该权限。

三、权限认证方式:

--其实就是控制用户访问我们的controller层中的方法。

 --- 可以自定义一个注解,在需要权限的方法上面贴一个注解,在不需要权限的方法上就不贴注解。

例子:

 @ RequiredPermission (name="权限名称",expression="权限表达式") //--expression:是我们判断的依据。
public User list(){
     //查询所有学生
 }
     
      

步骤:

1.自定义注解;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiredPermission {
    String name();//----权限名称
    String expression();//----权限表达式
}

2.将自定义注解贴在需要校验权限请求方法上面;

@RequstMapping("/list")
@RequiredPermission (name="员工新增或修改",expression="department:list")
public void saveOrUpdate(Role role) {
        if (role.getId() != null) { // 修改
            roleService.update(role);
        } else { // 新增
            roleService.save(role);
        }
      
    }

注意:

在权限系统开发中,一般权限系统有一个权限加载的功能。
	如果没有权限加载的功能的话,要把系统中所有的权限信息保存到数据库中。假如有 10个Controller接口,每个接口中有5个方法贴了自定义注解 @RequiredPermission 这样的话,得我们自己手动新增50次,而如果通过权限加载这个功能的话,就只需要点击这个功能按钮。这个按钮就可以把我们系统中所有贴了该注解的权限信息保存到数据库中。

权限加载的功能实现:

实现权限加载步骤:

0. 先把数据库中所有的权限表达式查询出来
1. 先拿到所有Controller --> 通过Spring容器去拿
2. 通过contoller去拿到每一个controller方法
3. 通过方法去拿方法上的注解@RequirdPermission
4. 判断如果注解不为空并且注解中权限表达式不在数据库中,拿到注解中name,expression,封装到Permission对象中
5. 把permission对象保存到数据库权限信息表中

方式一:

@Service
@Slf4j
public class PermissionServiceImpl implements IPermissionService {
	
     @Autowired
    private PermissionMapper permissionMapper;
    @Autowired
    private ApplicationContext context; //获取Spring Ioc 容器
	
    @override
	public void reload() {
        //0 先把数据库中所有的权限表达式查询出来
        List<String> expression =  permissionMapper.selectExpression();
		// 1 从spring容器中去拿到所有的controLLer
		Map<String, Obiect> beansithAnnotation = context.getBeanswithAnnotation(Controller.class);
    	// map key 把controller类名小写字符串 ,value才是我们想要的controller
    	Collection<Object> controllers = beanswithAnnotation.values();
    	// 2 根据controller 拿到每一个方法
		for (Object controller : controllers) {
        	//拿到controller对象,通过反射,去获取字节码对象,在获取自身所有的方法
       	 Method[] methods = controller.getClass().getDeclaredMethods();
       	 for (Method method : methods){
			// 3 根据方法拿到方法上注解 @RequiredPermission
       	 RequiredPermission annotation = method.getAnnotation(RequiredPermission.class);
             //判断注解是否为空,将在数据库中查询的权限表达式(第0步),与注解上表达式比对,获取list中不包含的权限表达式
       	 if(annotation!=null && !expression.contatins(annotation.expression())){
			// 4 判断注解是否为空 ,如果不为空数据封装到Permission对象中
			Permission p = new Permission();
			String name = annotation.name():
			String expression = annotation.expression();
       		 p.setExpression(expression);
        	p.setName(name);
        	// 5.把数据保存到数据库中
        	permissionMapper.insert(p);
         }
       }
    }  
}

方式二:

@Service
@Slf4j
public class PermissionServiceImpl implements IPermissionService {
	
     @Autowired
    private PermissionMapper permissionMapper;
    
    @Autowired
    //SpringMVC处理器映射器 ;通过处理器映射器获取Controller注解类中的方法信息,封装到HandlerMethod中
    private RequestMappingHandlerMapping requestMappingHandlerMapping;//SpringMVC处理器映射器 
    @override
	public void reload() {
    	//0 先把数据库中所有的权限表达式查询出来
        List<String> expression =  permissionMapper.selectExpression();
        // 在启动的时候requestMappingHandlerMapping 会把controller中所有的方法封装HandlerMethod 中
        Map<RequestMappingInfo, HandlerMethod> map = requestMappingHandlerMapping.getHandlerMethods();
        Collection<HandlerMethod> handlerMethods = map.values();
        for (HandlerMethod handlerMethod : handlerMethods) {
            // 到方法
            Method method = handlerMethod.getMethod();
            // 方法上的注解
			//3 根据方法拿到方法上注@RequiredPermission
           RequiredPermission annotation = method.getAnnotation(RequiredPermission.class);
            if(annotation!=null 8& !expressions.contains(annotation.expression())){
                // 4 断注解是否为空 ,如果不为空把权限表达式数据封装到Permission对象中
                Permission p = new Permission();
                String name = annotation.name();
				String expression = annotation.expression();
				p.setName(name);
				p.setExpression(expression);
				// 5 把数据保存到数据库中
				permissionMapper.insert(p);
			}  
        }    
    }

权限新增删除:

               角色(Role)                    角色中间表(role_permission)            权限(permission)
即:其通过对角色:新增或删除权限:
       通过中间表来关联,并在中间表上新增和删除。在中间表上对外键 role_id与permission_id 之间的关系来增加或删除。
              
              用户(Employee)                 用户中间表(Employee_role)              角色(role)
       删除的时候,不仅删除用户或角色表中的信息,还要删除中间表中的信息。 

四、登录校验:

@Data
public class JsonResult {
    private boolean success;
    private String msg;

    public JsonResult(boolean success, String msg) {
        this.success = success;
        this.msg = msg;
    }
}

1.controller层:

@Controller
public class LoginController {

    @Autowired
    private IEmployeeService employeeService;

    @Autowired
    private IPermissionService permissionService;

    @RequestMapping("/login")
    @ResponseBody
    // {"success":true,"msg":"登录成功"}
    // {"success":false,"msg":"登录失败"}
    public JsonResult login(String username, String password) {
        
        try{
           Employee employee = employeeService.login(username, password);  
            return new JsonResult(true, "操作成功");
        }catch{
            e.printStackTrace();
             return new JsonResult(false, e.getMessage);
            
        }
        
      
    }

2.service层:

@Service
public class EmployeeServiceImpl implements IEmployeeService {
	
    @Override
    public Employee login(String username, String password) {
        //校验参数非空判断
        if(StringUtils.isEmpty(username) || StringUtil.isEmpty(password)){
            throw new RuntimeException("账号或者密码不能为空");
        }
        //根据账号和密码上数据库中进行查询
        Employee employee = employeeMapper.selectByUsernameAndPassword(username, password);
       //如果为空抛出异常
        if(employee == null){
            throw new RuntimeException("账号或者密码错误");
        }
        return employee;
    }
}

3.前端:

$(".submitBtn").click(function () (
		// 发送ajax请求
$.post("/login",$("#loginForm").serialize(),function (data) {
		// data -> JsonResult
	if(data.success)(
		Location.href="/department/list";
	}else(
	  Swal.fire({
	     text: data.msg
	     })
	   }
	 })
})

4.***注意这样的话还是不行,用户可以直接输入网址进入页面;所以要对url进行拦截,进行登录校验;

在这里插入图片描述

步骤:
第一步: 用户开始时登录的时候,如果用户登录成功,则把用户信息放到ssesion中。
第二步: 定义一个拦截器,对每一次用户请求的资源进行拦截。
第三步: 对拦截器进行配置。
(1)要拦截哪些资源?
(2)排除哪些资源路径?
第一步:用户信息放到ssesion中
@Controller
public class LoginController {

    @Autowired
    private IEmployeeService employeeService;

    @Autowired
    private IPermissionService permissionService;

    @RequestMapping("/login")
    @ResponseBody
    // {"success":true,"msg":"登录成功"}
    // {"success":false,"msg":"登录失败"}
    public JsonResult login(HtttpSession session, String username, String password) {
        
        try{
           Employee employee = employeeService.login(username, password);  
            //验证用户身份成功之后,将用户的信息保存到session中
           session.setAttribute("USER_IN_SESSION", employee);
            return new JsonResult(true, "操作成功");
        }catch{
            e.printStackTrace();
             return new JsonResult(false, e.getMessage);
            
        }
        
      
    }
第二步: 定义一个拦截器
public class CheckLoginInterceptor implements HandlerInterceptor{
	
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 0bject handler) throws{
    // 查询session 中保存用户信息
    Employee employee = (Employee) request.getSession().getAttribute( "USER_IN_SESSION");
   // 判断如果存在就放行,如果不存在跳转到登灵界面中
    if(employee!=nul1){
        return true;
    }
	response.sendRedirect( s:"/static/login.html"); //重定向到登录界面
  return false;
 }
第三步: 对拦截器进行配置:
@Configuration
public class WebConfig implements WebMvcConfigurer (
	@Bean
	public CheckLoginInterceptor checkLoginInterceptor(){
        return new CheckLoginInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry){       
      registry.addInterceptor(checkLoginInterceptor()). 
      addPathPatterns("/**").//将拦截器注入到容器中
      excludePathPatterns("/static/**","/login","/logout");//放行那些资源
    }   

五、退出登录:

就是把session中登录的信息删除掉

@RequestMapping("/logout")
public String logout(HttpSession session){
	session.removeAttribute("USER_IN_SESSION");
    return "redirect:/static/login.html";//重定向
}

六、权限拦截:

分析:

在这里插入图片描述

步骤:

1 上session去获取当前登录用户信息
2 判断用户是否是超级管理员->如果是直接放行
3 拿到当前访问方法
4 获取方法上的注解@RequiredPermission
5 判断注解是否为空 ->直接放行
6 要把当前登录的用户所拥有的权限表达式给查询出来
7 如果注解中权限表示式是在从数据库中查询出来权限表达式集合中,说明用户拥有这个权限的 ->放行
8 没有这个权限跳转到没有权限界面中,进行拦截

1.定义一个拦截器

***拓展:
拦截器中 preHandle 方法的参数列表中有一个参数叫:Object handler 的参数:
通过反射 handler.getClass打印输出为:class org.springframework.web.method.HanderMethod(真实类型)
在 SpringMvc 中: 有一个处理器映射器。处理器映射器会把我们所有贴有Controller注解的类中的方法的信息,封装到HanderMethod中。

所以在拦截器中我们可以通过 HanderMethod 这个去获得贴有Controller注解的类中的方法的信息
/**
*权限校验拦截器
**/
public class CheckPermissionIntercetor implements HandlerInterceptor{
    
    @Autowird   
	private IPermissionService permissionService;
    
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
        // 1.从session中去拿当前用户登录信息
        Employee employee = (Employee) request.getSession().getAtribute("USER_IN_SESSION");
        // 2.判断是否是超级超级管理员
        if(employee.isAdmin()){
            return true;
        }
		// 3.获取到当前访问方法 不明白看拓展;
        HandlerMethod handlerMethod = (Handlerethod)handler
        Method method = handlerMethod.getMethod();
        // 4.通过方法拿方法上的注解
        RequiredPermission annotation = method.getAnnotation(RequiredPermission.class);
        // 5.判断注解如果为空表示访问方法时不需要权限
        if(annotation == null){
            return true;
        } 
		// 6.把用户所拥有的所有权限表达式集合给查询出来
        List<String> list = permissionService.getExpressionByEmpId(employee.getId());
        // 7.判断注解中的权限表达式,是否在集合中
        if(list.contatins(annotation.expression())){
            return true;
        }
        // 8. 如果不包含则表示没有权限 跳转到没有权限的页面中
        response.sendRedirect("/permission/nopermission");//url从定向
        
}

2.编写url从定向接口

@RequestMapping("/nopermission")
public String nopermission(){
    return "自己定义的页面路径";
}

3.将拦截器注入到容器中

@Configuration
public class WebConfig implements WebMvcConfigurer (
	//登录拦截器
    @Bean
	public CheckLoginInterceptor checkLoginInterceptor(){
        return new CheckLoginInterceptor();
    }
    //权限拦截器
    @Bean
	public CheckPermissionIntercetor checkPermissionIntercetor(){
        return new CheckPermissionIntercetor();
    }
	
    //登录拦截器注入
    @Override
    public void addInterceptors(InterceptorRegistry registry){       
      registry.addInterceptor(checkLoginInterceptor()). 
      addPathPatterns("/**").//将拦截器注入到容器中
      excludePathPatterns("/static/**","/login","/logout");//放行那些资源
    } 
    
    
    //登录拦截器注入
    @Override
    public void addInterceptors(InterceptorRegistry registry){       
      registry.addInterceptor(CheckPermissionIntercetor()). 
      addPathPatterns("/**").//将拦截器注入到容器中
      excludePathPatterns("/static/**","/login","/logout");//放行那些资源
    } 
    
    
  }    

七、统一异常处理

如何解决

  • 手动 try

    • 弊端是到处是重复代码,系统的代码耦合度高,工作量大且不好统一,维护的工作量也很大。
  • 利用 Spring MVC 的方式

    • Spring MVC 为 Controller 处理方法执行出现异常提供了全局统一处理,可以使用 @ExceptionHandler 配合 @ControllerAdvice 注解实现异常处理,可减少代码量,提高拓展性和可维护性。
    • 添加处理控制器异常处理类,确保 Spring 配置中要能扫描到这个类。
    • 针对不同异常进行不同处理,针对不同处理方法响应的内容,需要进行不同处理,比如原来方法响应 HTML 依然响应 HTML,若原来方法响应 JSON 依然响应 JSON。
/**
* 对控制器进行增强处理
*/
@ControllerAdvice
public class RuntimeExceptionHandler {
    /**
    * 该方法是用于捕获并处理某种异常
    * e:现在出现的异常对象
    * method:现在出现异常的那个处理方法
    */
    @ExceptionHandler(RuntimeException.class)
    public String exceptionHandler(RuntimeException e, HandlerMethod method, HttpServletResponse response) {
        e.printStackTrace(); // 方便开发的时候找 bug
        // 若原本控制器的方法是返回 JSON,现在出异常也应该返回 JSON
        // 获取当前出现异常的方法,判断是否有 ResponseBody 注解,有就代表需要返回 JSON
        if(method.hasMethodAnnotation(ResponseBody.class)){
            try {
                response.setContentType("application/json;charset=UTF-8");
                response.getWriter()
                    .print(JSON.toJSONString(new JsonResult(false, "系统异常,请联系管理员")));
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            return null;
        }
        // 若原本控制器的方法是返回 HTML,现在也应该返回 HTML
        return "common/error";
    }
}

2、自定义异常

在开发中还可以根据自己业务的异常情况来自定义业务逻辑异常类,一般继承于 java.lang.RuntimeException

public class LogicException extends RuntimeException {
    public LogicException(String errorMsg){
        super(errorMsg);
    }
}

比如虽然登录出错,也响应 JSON 数据,但其需要提示的消息更详细,所以通过自定义异常,再利用 Spring MVC 全局处理异常方式,针对这个异常进行专门处理。可参考另一篇博客SpringBoot统一异常处理

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

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

相关文章

SQL零基础入门学习(十三)

上一篇&#xff08;SQL零基础入门学习&#xff08;十二&#xff09;&#xff09; SQL 视图&#xff08;Views&#xff09; 视图是可视化的表。 SQL CREATE VIEW 语句 在 SQL 中&#xff0c;视图是基于 SQL 语句的结果集的可视化的表。 视图包含行和列&#xff0c;就像一个…

jsp+servlet+javabean新闻发布系统

技术&#xff1a;Java、JSP等摘要&#xff1a;近年来,Internet技术得到迅速的发展,已经成为计算机产业的一个技术热点。促成Internet高速发展的因素之一就是Web技术。Web技术的发展使得那些具有交互动态页面、有条理的数据库查询、丰富信息内容的页面成为最吸引人的网页。浏览W…

MVCC 当前读 快照读 RC read view RR下事务更新不会丢失

MVCC(multi-version-concurrent-control) MVCC是行锁的一个变种&#xff0c;但MVCC在很多情况下它避免了加锁。不是buffer块&#xff0c;而是buffer中的记录行。 MVCC (Multi-Version Concurrency Control) (注&#xff1a;与MVCC相对的&#xff0c;是基于锁的并发控制&#x…

SVIP优先办理服务-课后程序(JAVA基础案例教程-黑马程序员编著-第八章-课后作业)

【案例8-2】 Svip优先办理服务 【案例介绍】 1.任务描述 在日常工作生活中&#xff0c;无论哪个行业都会设置一些Svip用户&#xff0c;Svip用户具有超级优先权&#xff0c;在办理业务时&#xff0c;Svip用户具有最大的优先级。 本案例要求编写一个模拟Svip优先办理业务的程…

350-401-拖图题1

拖图题 QoS&#xff1b;policing&#xff1a;dropped,no delay;shaping:buffers,delay&#xff1b;policing有TCP和no。shaping有buffer缓冲器和delay延迟&#xff1b;警察安全不丢失&#xff0c;定型过多延迟又延迟 traffic policing&#xff1a;流量监管 causes TCP retran…

JUnit介绍与使用

自动化是通过selenium脚本来实现的&#xff0c;而JUnit是Java单元测试工具&#xff0c;只不过我们在实现自动化的时候要借用一些JUnit库里提供的一些方法。优化我们的自动化使用版本&#xff1a;JUnit5&#xff08;支持最低Java版本为8&#xff09;依赖导入到pom.xml&#xff1…

浅谈权限获取方法之文件上传

概述 文件上传漏洞是发生在有上传功能的应用中&#xff0c;如果应用程序对用户的上传文件没有控制或者存在缺陷&#xff0c;攻击者可以利用应用上传功能存在的缺陷&#xff0c;上传木马、病毒等有危害的文件到服务器上面&#xff0c;控制服务器。 漏洞成因及危害 文件上传漏…

如何进行域名购买,获取免费ssl证书,使用springboot绑定ssl证书

前言 小编我将用CSDN记录软件开发求学之路上亲身所得与所学的心得与知识&#xff0c;有兴趣的小伙伴可以关注一下&#xff01;也许一个人独行&#xff0c;可以走的很快&#xff0c;但是一群人结伴而行&#xff0c;才能走的更远&#xff01;让我们在成长的道路上互相学习&#…

Linux修改文件属性和权限

本次我们还是使用CentOS7来进行实验查看文件属性首先我们可以使用ll命令来查看某一文件的属性现在可以拆分一下-rw-r--r--1rootroot3042月27 22:58kaka文件类型文件所有者权限用户组权限其他人权限硬链接次数属主属组文件大小最后修改时间文件名1.文件类型-普通文件&#xff0c…

python同步线程

线程同步可以定义为一种方法&#xff0c;借助这种方法&#xff0c;可以确信两个或更多的并发线程不会同时访问被称为临界区的程序段。 另一方面&#xff0c;正如我们所知道的那样&#xff0c;临界区是共享资源被访问的程序的一部分。 因此&#xff0c;同步是通过同时访问资源来…

MIT 6.S081学习笔记

计划花25天时间学完6.S081课程&#xff0c;从2月20日-3月20日。课程主页Link   xv6 book   GDB User Manual Lecture 1: Introduction and Examples课程主题&#xff1a;设计和实现操作系统   OS的三大功能&#xff1a;多路复用、隔离和交互。 Lab: Xv6 and Unix utiliti…

“ChatGPT之父”Sam Altman:我是如何成功的?

背靠微软&#xff0c;OpenAI能拳打谷歌&#xff0c;脚踢Meta&#xff0c;它背后的男人&#xff0c;必然不简单。 让我们来看一看&#xff0c;Sam Altman是如何一步步成长为今天这个搅动全世界的男人。 山姆奥特曼&#xff08;Sam Altman&#xff09; 成长和创业经历 在YC创始…

数据结构(Java版)绪论

一、数据结构绪论 1、概论 &#x1f34e;数据结构研究计算机的操作对象以及他们之间的关系和操作。 2、算法的定义、特征、设计要求 算法&#xff1a;是对特定问题求解步骤的一种描述&#xff0c;它是指令的有限序列&#xff0c;是一系列输入转化为输出的计算步骤。 算法的特…

篮球杯 双指针专题

总的来说&#xff0c;双指针分为while(1)类型和尺取法类型可以解决各种问题&#xff08;如子序列问题&#xff09;活动 - AcWing思路&#xff1a;while(1)型的双指针基本形式为&#xff1a;while(1){if(l>n||r>n) break;while(条件&&l<n) l;rl;while(条件&…

使用Platform Designer创建Nios II 最小系统

Nios II简介 ​ Nios II 软核处理器十多年前就有了&#xff0c;它和xilinx的MicroBlaze类似&#xff0c;性能相比硬核处理器要差得多&#xff0c;工程应用也不是很多&#xff0c;那还有必须学习一下吗&#xff1f;我个人认为了解一下Nios II开发流程&#xff0c;对intel FPGA开…

9.网站数据统计

1.Redis 高级数据类型&#xff08;1&#xff09;HyperLogLog统计20万个重复数据的独立总数// 统计20万个重复数据的独立总数. Test public void testHyperLogLog() {String redisKey "test:hll:01";for (int i 1; i < 100000; i) {redisTemplate.opsForHyperLog…

8年测开经验面试28K公司后,吐血整理出高频面试题和答案

#01、如何制定测试计划&#xff1f; ❶参考点 1.是否拥有测试计划的制定经验 2.是否具备合理安排测试的能力 3.是否具备文档输出的能力 ❷面试命中率 80% ❸参考答案 测试计划包括测试目标、测试范围、测试环境的说明、测试类型的说明&#xff08;功能&#xff0c;安全&am…

深入解读.NET MAUI音乐播放器项目(三):界面交互

UI设计的本质是对于产品的理解在界面中多种形式的映射&#xff0c;当需求和定位不同时&#xff0c;对相同的功能表达出了不同的界面和交互方式。 作为播放器&#xff0c;界面可以是千差万别的。《番茄播放器》的iOS平台上我开发了传统版本&#xff0c;和基于手势播放的版本。 …

Word处理控件Aspose.Words功能演示:使用 C++ 在 Word (DOC/DOCX) 中添加或删除水印

Aspose.Words 是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。此外&#xff0c; Aspose API支持流行文件格式处…

Nacos未授权访问漏洞

Nacos介绍 Nacos 的官网地址为&#xff1a; https://nacos.io 它是阿里开源的 SpringCloud Alibaba 项目下的一项技术&#xff0c;可以实现服务注册中心、分布式配置中心。 一般来说&#xff0c;nacos被建议部署在内网中&#xff0c;如果在外网出现&#xff0c;会有很大的风险…