基于Springboot、RBAC思想使用注解方式实现权限管理功能

所涉及到的技能点:

Springboot 、分页后端插件 pagehelper 、分页前端插件 twbs-pagination

首先什么是RBAC

RBAC 是基于角色的访问控制(Role-Based Access Control ),简单的来说就是不同的人有不同的权限,什么样的人干什么样的事

数据库的设计

角色权限数据库设计图

员工表:用来存储员工信息

字段分析: id(员工编号,主键自增长)、		name(员工姓名)、	
		 role_id(角色编号,关联查询角色)、super_admin(是否超级管理员)

角色表:用来存储员工的角色或者说职位

字段分析: id(角色编号)、 name(角色名称)

权限表:用来存储权限

字段分析: id(权限编号)、name(权限的名称)、 permission(权限的表达式,可以理解为访问的路径)

在设计两个关联表
角色与员工关联表:通过员工查询其拥有的角色
角色与权限关联表:通过角色查询它拥有的权力

项目准备

创建好pojo类、mapper接口、service类、controller类

对于权限的录入 、要么一个一个的录入到数据库中,要么使用注解的方式去标识,后面再从注解里取到他的权限

分析如果是一个一个录入到数据库中 (inser into···) ,如果权限很多,那就会显得非常繁琐麻烦,而且数据一多起来有可能会写错。
所以这里我使用注解的方式去实现。

权限注解设计与使用

创建一个注解 RequestPermission

@Target({ElementType.METHOD})	//标记注解只能再方法上使用
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestPermission {
    //设置一个权限名称
    String name();

    //表达式
    String expression();
}

在我们需要设置权限的地方添加 注解 以:员工权限举例

这里是一个员工的列表页面
我们不想任何人都可以访问我们的 添加 和 编辑 页面
所以我们就可以再除了 查询页面的方法以外的方法添加注解

员工页面
在这里插入图片描述

再员工的 EmployeeController 下 :这里主要讲解注解是 RequestPermission 怎么使用的其他的业务逻辑可以忽略 员工查询 list 没有加注解是为了谁都可以查看

@Controller
@RequestMapping("/employee")
public class EmployeeController {

    @Autowired
    private IEmployeeService employeeService;

    @Autowired
    private IRoleService iRoleService;

    // 处理分页查询员工请求
    @RequestMapping("/list")	
    // @ModelAttribute("qo") 这个是给 参数 qo 取个别名方便前端页面回显数据拿到 
    //	但是这里 不写也可以名字一样了
    public String list(Model model, @ModelAttribute("qo") EmployeeQueryObject qo) {
        PageInfo<Employee> pageInfo = employeeService.query(qo);	//这里使用的是 pagehelper 实现分页
        model.addAttribute("pageInfo", pageInfo); // 将我们的分页结果集返回给前端
      
        return "employee/list";
    }

    // 处理删除员工请求 /employee/delete?id=*
    @RequestMapping("/delete")
    //设置权限的名称为员工删除  , 表达式为 employee:delete
    @RequestPermission(name="员工删除" ,expression = "employee:delete")
    public String delete(Long id) {
        if (id != null) {
            employeeService.delete(id);
        }
        return "redirect:/employee/list"; 
    }

    // 处理去新增或者去修改请求 /employee/input
    @RequestMapping("/input")
    //设置权限的名称为员工编辑页面  , 表达式为 employee:input
    @RequestPermission(name="员工编辑页面" ,expression = "employee:input")
    public String input(Long id, Model model) {

        if (id != null) {
            Employee employee = employeeService.get(id);
            model.addAttribute("employee", employee);
            //如果不为空,那我就再去查看他的角色
            List<Role> roleList = iRoleService.getByEmployeeId(id);
            model.addAttribute("roleList",roleList);
        }

        //再去查询分配角色
        List<Role> roles = iRoleService.listAll();
        model.addAttribute("roles",roles);

        return "employee/input"; 
    }

    // 处理新增请求 /employee/saveOrUpdate 参数在请求体
    @RequestMapping("/saveOrUpdate")
    @ResponseBody
    //设置权限的名称为员工保存和修改  , 表达式为 employee:saveOrUpdate 
    @RequestPermission(name="员工保存和修改" ,expression = "employee:saveOrUpdate ")
    public JsonResult saveOrUpdate(Employee employee,Long[] roleIds) {
    // JsonResult  是自己定义的一个类 字段有 (boolean)success 和 (String)msg 用来返回一个js对象给前端接收
        try {
            if (employee.getId() != null) {
                employeeService.update(employee,roleIds);
            } else {
                employeeService.save(employee,roleIds);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return new JsonResult(false,e.getMessage());
        }
        return new JsonResult(true,"成功保存");
    }
}

简化版:

@Controller
@RequestMapping("/employee")
public class EmployeeController {

    @Autowired
    private IEmployeeService employeeService;

    @Autowired
    private IRoleService iRoleService;

    @RequestMapping("/list")
    public String list(Model model, @ModelAttribute("qo") EmployeeQueryObject qo) {

    }

    @RequestMapping("/delete")
    @RequestPermission(name="员工删除" ,expression = "employee:delete")
    public String delete(Long id) {

    }

    @RequestMapping("/input")
    @RequestPermission(name="员工编辑页面" ,expression = "employee:input")
    public String input(Long id, Model model) {

    }

    @RequestMapping("/saveOrUpdate")
    @ResponseBody
    @RequestPermission(name="员工保存和修改" ,expression = "employee:saveOrUpdate ")
    public JsonResult saveOrUpdate(Employee employee,Long[] roleIds) {

    }
}
注解的扫描和存储

那么接下来问题就到了 :
如何将我们注解上的信息保存到我们的数据库?
怎么识别方法上的注解?

再权限页面设置一个权限查询按钮刷新之后将我们的权限全部录入到数据库中
效果展示:
在这里插入图片描述

在我们的 PermissionService 的保存方法中

@Service
public class PermissionServiceImpl implements IPermissionService {

    @Autowired
    private PermissionMapper permissionMapper;

    @Autowired
    private ApplicationContext ac;

    Permission permission = new Permission();

    Set<String> set = new HashSet<>();

    @Override
    public void savePermission() {

        // 先查询数据库 找到我们所有的权限放到Set中
        List<Permission> permissions = permissionMapper.selectAll();
        if (permissions!=null){
            permissions.forEach((permission)->{set.add(permission.getExpression());});
        }
        
        //这里我们编写保存方法
        //首先我们得先拿到我们 Controller的方法 在拿到我们方法上面的注解
        //我们的容器已经有了,就在 RequestMappingHandlerMapping 里面,它扫描了全部的controller 方法
        //这也是为什么拦截器能找到我们对应的方法路径
        RequestMappingHandlerMapping handlerMapping = ac.getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = handlerMapping.getHandlerMethods();
        //我们拿到所有的value也就是 methods
        Collection<HandlerMethod> methods = handlerMethodMap.values();
        //得到方法上的注解  标记我们想要的注解
        for (HandlerMethod method : methods) {

            //判断每一个方法上有没有这个注解,如果没有就跳过
            RequestPermission requestPermission = method.getMethodAnnotation(RequestPermission.class);

            if (requestPermission == null)
                continue;

            String expression = requestPermission.expression();

			//为了防止 重复存入,我们使用Set集合 的无序不可重复 特性存储
            //能到这里说明是有值的,那我就拿出来,存到数据库中
            if (set.contains(expression))
                continue;	// 如果我们存过了 那就直接跳过 
            //把这个 表达式 存到 set里面
            set.add(expression);
            String name = requestPermission.name();
            permission.setName(name);
            permission.setExpression(expression);
            permissionMapper.insert(permission);

        }
    }
}

拦截的实现

接下来问题就到了 如何实现权限的拦截?
使用拦截器 编写一个 PermissionInterceptor 实现 HandlerInterceptor 接口 重写 preHandle 方法 实现拦截
再把我们写的拦截器 添加到spring中,让他生效

 这里的前提是 员工要有角色 才能有权限: 这里的 员工 绑定 角色 省略了 就假定 用户只有 查询权限,没有编辑和删除权限 需要被拦截
 ps : 这里是写了 员工登入功能,如果没有登入就不能走到这里,登入之后将 员工存入session ,再从session取出
 			判断是不是超级管理员 、 和有什么角色 ,再通过什么角色 找到 什么功能

编写自己的权限拦截器

@Component
public class PermissionInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //权限拦截器
//        首先我先判断它有没有权限, 先拿到他 session 的 employee 信息
        Employee employee = (Employee) request.getSession().getAttribute("EMPLOYEE_IN_SESSION");
        if (employee.isAdmin())
            return true; //如果是超级管理员我直接放行
		
		//为了效率和优化:将所有的权限都先查出来 放到session中
        //拿到session 的roles
        List<String> expressions = (List<String>) request.getSession().getAttribute("PERMISSION_IN_SESSION");

//        handler 可以获取所有的方法 包括 静态ResourceHttpRequestHandler 和 非静态的HandlerMethod方法
		//静态资源不需要 拦截 ,只需要拦截 Controller 方法上 有 RequestPermission 注解的方法
        if (handler instanceof HandlerMethod){
            //强转
            HandlerMethod handlerMethod = (HandlerMethod) handler;

            //如果你的方法没有权限 那么就随都可以进来 直接放行
            if (!handlerMethod.hasMethodAnnotation(RequestPermission.class))
                return true;

            //到这里就说明需要权限
            //如果我是Controller 的方法 那我就需要先判断 你是返回路径 还是返回数据
            if (handlerMethod.hasMethodAnnotation(ResponseBody.class)) {
                //如果你是返回的响应体 那我就返回字符串给你
                response.setContentType("application/json;charset=utf-8");
                //我通过原生的写 写出去
                response.getWriter().write(JSON.toJSONString(new JsonResult(false,"没有权限")));
                return false;
            }

            //走到这里的都没有 ResponseBody 那就都是
            RequestPermission permission = handlerMethod.getMethodAnnotation(RequestPermission.class);
            // 拿到对应的权限的 表达式 再去权限集合中判断有没有
            String expression = permission.expression();
            expressions.forEach(System.out::println);
            if (!expressions.contains(expression)) {
                response.sendRedirect("/permission/nopermission");
                return false;
            }
        }
        return true;
    }
}

效果展示:
在这里插入图片描述
主要的思路和逻辑大概就是这样

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RBAC权限管理系统是基于角色的用户权限控制系统。在springboot中,我们可以使用Spring Security框架来实现RBAC权限管理Spring Security是一个功能强大的、灵活的框架,可以帮助我们在应用程序中实现身份验证、授权和其他安全功能。 要在springboot使用RBAC权限管理系统,可以按照以下步骤进行操作: 1. 导入Spring Security依赖:在pom.xml文件中添加Spring Security相关的依赖项。 2. 配置Spring Security:在应用程序的配置文件中,设置Spring Security的基本参数,例如认证方式、登录页面等。 3. 创建用户和角色实体:使用实体类表示用户和角色,并建立它们之间的关系。 4. 实现用户认证和授权:通过自定义的用户认证和授权逻辑,实现用户登录验证和权限控制。可以使用注解或配置文件的方式进行权限配置。 5. 创建页面和接口:根据系统需求,创建相应的页面和接口,并为每个角色分配不同的权限。 6. 完善系统功能:根据实际需求,完善RBAC权限管理系统的其他功能,例如日志记录、异常处理等。 需要注意的是,以上步骤仅为概括性的指导,实际操作可能会因具体需求而有所不同。可以根据具体的项目情况进行适当的调整和扩展。 总结起来,springboot中的RBAC权限管理是通过使用Spring Security框架来实现的,需要进行依赖导入、配置、实体创建、认证和授权逻辑实现、页面和接口创建等步骤。通过这些步骤,可以构建一个完善的RBAC权限管理系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值