zoukankan      html  css  js  c++  java
  • SpringBoot集成Spring Security(5)——权限控制

    在第一篇中,我们说过,用户<–>角色<–>权限三层中,暂时不考虑权限,在这一篇,是时候把它完成了。

    为了方便演示,这里的权限只是对角色赋予权限,也就是说同一个角色的用户,权限是一样的。当然了,你也可以精细化到为每一个用户设置权限,但是这不在本篇的探讨范围,有兴趣可以自己实验,原理都是一样的。

    源码地址:https://github.com/jitwxs/blog_sample

    文章目录

    一、数据准备

    • 1.1 创建 sys_permission 表
    • 1.2 创建 POJO、Mapper、Service
    • 1.3 修改接口

    二、PermissionEvaluator

    三、运行程序

    一、数据准备

    1.1 创建 sys_permission 表

    让我们先创建一张权限表,名为 sys_permission

    CREATE TABLE `sys_permission` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `url` varchar(255) DEFAULT NULL,
      `role_id` int(11) DEFAULT NULL,
      `permission` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `fk_roleId` (`role_id`),
      CONSTRAINT `fk_roleId` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
    ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

    内容就是两条数据,url+role_id+permission 唯一标识了一个角色访问某一 url 时的权限,其中权限暂定为 c、r、u、d,即增删改查。

    sys_permission表

    1.2 创建 POJO、Mapper、Service

    (1)pojo

    package jit.wxs.entity;
    
    import java.io.Serializable;
    import java.util.Arrays;
    import java.util.List;
    
    /**
     * 权限实体类
     * @author jitwxs
     * @since 2018/5/15 18:11
     */
    public class SysPermission implements Serializable {
        static final long serialVersionUID = 1L;
    
        private Integer id;
    
        private String url;
    
        private Integer roleId;
    
        private String permission;
    
        private List permissions;
    
        // 省略除permissions外的getter/setter
    
        public List getPermissions() {
            return Arrays.asList(this.permission.trim().split(","));
        }
    
        public void setPermissions(List permissions) {
            this.permissions = permissions;
        }
    }

    这里需要注意的时相比于数据库,多了一个 permissions 属性,该字段将 permission 按逗号分割为了 list

    (2)mapper

    import jit.wxs.entity.SysPermission;
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Select;
    
    import java.util.List;
    
    @Mapper
    public interface SysPermissionMapper {
        @Select("SELECT * FROM sys_permission WHERE role_id=#{roleId}")
        List<SysPermission> listByRoleId(Integer roleId);
    }

    (3)Service

    package jit.wxs.service;
    
    import jit.wxs.entity.SysPermission;
    import jit.wxs.mapper.SysPermissionMapper;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    
    @Service
    public class SysPermissionService {
        @Autowired
        private SysPermissionMapper permissionMapper;
    
        /**
         * 获取指定角色所有权限
         */
        public List<SysPermission> listByRoleId(Integer roleId) {
            return permissionMapper.listByRoleId(roleId);
        }
    }

    Service中 有一个方法,根据 roleId 获取所有的 SysPermission

    1.3 修改接口

    @Controller
    public class LoginController {
        ...
    
        @RequestMapping("/admin")
        @ResponseBody
        @PreAuthorize("hasPermission('/admin','r')")
        public String printAdminR() {
            return "如果你看见这句话,说明你访问/admin路径具有r权限";
        }
    
        @RequestMapping("/admin/c")
        @ResponseBody
        @PreAuthorize("hasPermission('/admin','c')")
        public String printAdminC() {
            return "如果你看见这句话,说明你访问/admin路径具有c权限";
        }
    }

    让我们修改下我们要访问的接口,@PreAuthorize("hasPermission('/admin','r')")是关键,参数1指明了访问该接口需要的url,参数2指明了访问该接口需要的权限。

    二、PermissionEvaluator

    我们需要自定义对 hasPermission() 方法的处理,就需要自定义 PermissionEvaluator,创建类 CustomPermissionEvaluator,实现 PermissionEvaluator 接口。

    import jit.wxs.entity.SysPermission;
    import jit.wxs.service.SysPermissionService;
    import jit.wxs.service.SysRoleService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.access.PermissionEvaluator;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.stereotype.Component;
    
    import java.io.Serializable;
    import java.util.Collection;
    import java.util.List;
    
    @Component
    public class CustomPermissionEvaluator implements PermissionEvaluator {
        @Autowired
        private SysPermissionService permissionService;
        @Autowired
        private SysRoleService roleService;
    
        @Override
        public boolean hasPermission(Authentication authentication, Object targetUrl, Object targetPermission) {
    
            /**
              * authentication.getPrincipal()返回的是字符串而不是User对象 -》主要是因为构造Token的参数出错了,
          * 在《SpringBoot集成Spring Security(4)——自定义表单登录》中,
          * 实现AuthenticationProvider接口的authenticate()方法的返回值应该改成
          * return new UsernamePasswordAuthenticationToken(userDetails, inputPassword, userDetails.getAuthorities());
          * 就是将inputName改成userDetails(就是用户对象)
    */ // 获得loadUserByUsername()方法的结果 // User user = (User)authentication.getPrincipal(); // 获得loadUserByUsername()中注入的角色 // Collection<GrantedAuthority> authorities = user.getAuthorities();
          
         //也可以使用authentication.getAuthorities()方法来获取对象 Collection<GrantedAuthority> authorities = (Collection<GrantedAuthority>) authentication.getAuthorities(); // 遍历用户所有角色 for(GrantedAuthority authority : authorities) { String roleName = authority.getAuthority(); Integer roleId = roleService.selectByName(roleName).getId(); // 得到角色所有的权限 List<SysPermission> permissionList = permissionService.listByRoleId(roleId); // 遍历permissionList for(SysPermission sysPermission : permissionList) { // 获取权限集 List permissions = sysPermission.getPermissions(); // 如果访问的Url和权限用户符合的话,返回true if(targetUrl.equals(sysPermission.getUrl()) && permissions.contains(targetPermission)) { return true; } } } return false; } @Override public boolean hasPermission(Authentication authentication, Serializable serializable, String s, Object o) { return false; } }

    在 hasPermission() 方法中,参数 1 代表用户的权限身份,参数 2 参数 3 分别和 @PreAuthorize("hasPermission('/admin','r')") 中的参数对应,即访问 url 和权限。

    思路如下:

    1. 通过 Authentication 取出登录用户的所有 Role
    2. 遍历每一个 Role,获取到每个Role的所有 Permission
    3. 遍历每一个 Permission,只要有一个 Permission 的 url 和传入的url相同,且该 Permission 中包含传入的权限,返回 true
    4. 如果遍历都结束,还没有找到,返回false

    下面就是在 WebSecurityConfig 中注册 CustomPermissionEvaluator

    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
        ...
        /**
         * 注入自定义PermissionEvaluator
         */
        @Bean
        public DefaultWebSecurityExpressionHandler webSecurityExpressionHandler(){//方法名称webSecurityExpressionHandler如果和SpringSecurity原本的方法名有冲突,可以更换该名称
            DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
            handler.setPermissionEvaluator(new CustomPermissionEvaluator());
            return handler;
        }
        ...
    }

    三、运行程序

    当我使用角色为 ROLE_USER 的用户仍然能访问,因为该用户访问 /admin 路径具有 r 权限:

    运行结果

    ---------------------
    作者:Jitwxs
    来源:CSDN
    原文:https://blog.csdn.net/yuanlaijike/article/details/80327880

  • 相关阅读:
    一题多解(六)—— 一个数二进制形式 1 的个数
    心算技巧
    心算技巧
    位运算应用及其注意事项
    位运算应用及其注意事项
    辨异 —— 不同的编程语言(编译型语言、解释型语言、动态语言、静态语言)
    C#POP3协议实现SSL验证登陆GMAIL
    PB学习笔记(一)
    24点计算 --- 庞果
    JAVA网站高并发解决方案
  • 原文地址:https://www.cnblogs.com/ryelqy/p/10302613.html
Copyright © 2011-2022 走看看