美文网首页后端之路
五. Spring Security 权限管理

五. Spring Security 权限管理

作者: 任未然 | 来源:发表于2019-10-19 18:56 被阅读0次

    概述

    权限是大部分的后台管理系统都需要实现的功能,用户控制不同的角色能够进行的不同的操作。Spring Security的可以进行用户的角色权限控制,也可以进行用户的操作权限控制。在之前的代码实现上,我们仅仅只是实现用户的登录,在用户信息验证的时候使用UserDetailsService,但是却一直忽略了用户的权限。

    一. 启动类配置

    /**
     * 开启方法的注解安全校验。
     *  securedEnabled  @Secured("ROLE_abc")  该注解是Spring security提供的
     *  jsr250Enabled  @RolesAllowed("admin")  该注解是 JSR250 支持的注解形式
     *  prePostEnabled @PreAuthorize("hasAuthority('user:add')
     */
    @SpringBootApplication
    @EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true)
    public class SecurityApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SecurityApplication.class, args);
        }
    }
    

    二. 基于角色的权限控制

    2.1 自定义认证信息类, 查询用户的密码和权限

    @Component
    public class UserSecurityService implements UserDetailsService {
    
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    
            /**
             * 调用形式有两种:
             *   1. 此时构建的 SimpleGrantedAuthority 必须是以 ROLE_ 开头, 例如 ROLE_admin, ROLE_manager.
             *      实现全权限控制的时候使用 @RolesAllowed("ROLE_admin")  或者 @RolesAllowed("admin") 都可以
             *   2. 此时构建的 SimpleGrantedAuthority 必须是以 ROLE_ 开头, 例如 ROLE_admin, ROLE_manager.
             *     实现全权限控制的时候使用 @Secured("ROLE_admin") ROLE_是不能省略的。
             */
            return new User(username, sysUser.getPassword(),
                    Arrays.asList(new SimpleGrantedAuthority("ROLE_admin")));
        }
    }
    

    注意: 我们在构建SimpleGrantedAuthority对象的时候,用户的角色必须是以 ROLE_ 开头,例如 ROLE_adminROLE_manager

    2.2 角色权限控制使用

    在控制器上进行用户访问控制的时候,基于角色有两种书写方式:

    方式一:@RolesAllowed

    /**
     *  @RolesAllowed 中的值可以写成 "admin", 例如 @RolesAllowed("admin")
     *  推荐: @RolesAllowed 中的值还可以写成 "ROLE_admin",例如 @RolesAllowed("ROLE_admin")
     */
    @RequestMapping
    @RolesAllowed("admin")
    public Object getAll() {
        return Arrays.asList(new User(10, "张"), new User(20, "李四"));
    }
    

    方式二:@Secured

    /**
     *  @Secured 中的值必须为 "ROLE_admin",例如 @Secured("ROLE_admin"),ROLE_不能省略
     */
    @RequestMapping
    @Secured("ROLE_admin")
    public Object getAll() {
        return Arrays.asList(new User(10, "张"), new User(20, "李四"));
    }
    

    三. 基于操作的权限控制

    当然我们也可以使用基于操作的权限控制,这个功能稍显得有点累赘,因为在实际的项目开发过程中我们都是基于角色的权限控制。

    3.1 自定义认证信息类, 查询用户的密码和权限

    @Component
    public class UserSecurityService implements UserDetailsService {
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
             /**
             *  A. new SimpleGrantedAuthority("user:delete")   @PreAuthorize("hasAnyAuthority('user:add', 'user:list')") 无法访问。
             *  B. new SimpleGrantedAuthority("user:add")     @PreAuthorize("hasAnyAuthority('user:add', 'user:list')") 可以访问。
             *  C. Arrays.asList(new SimpleGrantedAuthority("user:add"), new SimpleGrantedAuthority("user:list"))
             *     @PreAuthorize("hasAuthority('user:add') and hasAuthority('user:list')") 可以访问
             *  D. new SimpleGrantedAuthority("ROLE_admin") 定义角色
             *    @PreAuthorize("hasRole('admin')")  可以访问
             */
            return new User(username, sysUser.getPassword(),
                    Arrays.asList(new SimpleGrantedAuthority("user:list"),
                    new SimpleGrantedAuthority("user:add")
                    ));
        }
    }
    

    3.2 控制器访问控制(针对角色)

    /**
     *  @PreAuthorize 中的值可以为 "ROLE_admin", "admin",
     *  例如 @PreAuthorize("hasRole('admin')") 或者为  
     *      @PreAuthorize("hasRole('ROLE_admin')")
     */
    @RequestMapping
    @PreAuthorize("hasRole('admin')")
    public Object getAll() {
        return Arrays.asList(new User(10, "张"), new User(20, "李四"));
    }
    

    3.3 控制器访问控制(针对操作)

    @RequestMapping
    // @PreAuthorize("hasAuthority('user:add') and hasAuthority('user:list')") 
    // @PreAuthorize("hasAuthority('user:add') or hasAuthority('user:list')")
    @PreAuthorize("hasAnyAuthority('user:add', 'user:list')") 
    public Object getAll() {
        return Arrays.asList(new User(10, "张"), new User(20, "李四"));
    }
    

    3.4 访问无权限处理

    .and()
    .exceptionHandling()
    .accessDeniedHandler(customizeAccessDeniedHandler)  //无权限访问处理, customizeAccessDeniedHandler为无权限处理操作类
    .and()
    

    四 .示例

    4.1 用户权限表设计

    sql脚本

    SET NAMES utf8mb4;
    SET FOREIGN_KEY_CHECKS = 0;
    
    -- ----------------------------
    -- Table structure for sys_permission
    -- ----------------------------
    DROP TABLE IF EXISTS `sys_permission`;
    CREATE TABLE `sys_permission` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(20) DEFAULT NULL,
      `permmit` varchar(50) DEFAULT NULL,
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
    
    -- ----------------------------
    -- Records of sys_permission
    -- ----------------------------
    BEGIN;
    INSERT INTO `sys_permission` VALUES (1, '删除用户', 'user:delete');
    INSERT INTO `sys_permission` VALUES (2, '展示用户', 'user');
    INSERT INTO `sys_permission` VALUES (3, '添加用户', 'user:add');
    INSERT INTO `sys_permission` VALUES (4, '编辑用户', 'user:edit');
    INSERT INTO `sys_permission` VALUES (5, '导出用户', 'user:export');
    INSERT INTO `sys_permission` VALUES (6, '部门展示', 'dept');
    INSERT INTO `sys_permission` VALUES (7, '删除部门', 'dept:delete');
    COMMIT;
    
    -- ----------------------------
    -- Table structure for sys_role
    -- ----------------------------
    DROP TABLE IF EXISTS `sys_role`;
    CREATE TABLE `sys_role` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `role_name` varchar(20) NOT NULL,
      `description` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
    
    -- ----------------------------
    -- Records of sys_role
    -- ----------------------------
    BEGIN;
    INSERT INTO `sys_role` VALUES (1, 'admin', '系统管理员');
    INSERT INTO `sys_role` VALUES (2, 'finance', '财务管理');
    INSERT INTO `sys_role` VALUES (3, 'administration ', '行政管理');
    COMMIT;
    
    -- ----------------------------
    -- Table structure for sys_role_permission
    -- ----------------------------
    DROP TABLE IF EXISTS `sys_role_permission`;
    CREATE TABLE `sys_role_permission` (
      `role_id` int(11) NOT NULL,
      `permission_id` int(11) NOT NULL,
      PRIMARY KEY (`role_id`,`permission_id`) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
    
    -- ----------------------------
    -- Records of sys_role_permission
    -- ----------------------------
    BEGIN;
    INSERT INTO `sys_role_permission` VALUES (1, 1);
    INSERT INTO `sys_role_permission` VALUES (1, 2);
    INSERT INTO `sys_role_permission` VALUES (1, 3);
    INSERT INTO `sys_role_permission` VALUES (1, 4);
    INSERT INTO `sys_role_permission` VALUES (1, 5);
    INSERT INTO `sys_role_permission` VALUES (1, 6);
    INSERT INTO `sys_role_permission` VALUES (1, 7);
    INSERT INTO `sys_role_permission` VALUES (2, 2);
    INSERT INTO `sys_role_permission` VALUES (2, 5);
    INSERT INTO `sys_role_permission` VALUES (2, 6);
    INSERT INTO `sys_role_permission` VALUES (3, 2);
    INSERT INTO `sys_role_permission` VALUES (3, 6);
    COMMIT;
    
    -- ----------------------------
    -- Table structure for sys_user
    -- ----------------------------
    DROP TABLE IF EXISTS `sys_user`;
    CREATE TABLE `sys_user` (
      `id` int(11) NOT NULL,
      `username` char(20) DEFAULT NULL,
      `password` char(100) DEFAULT NULL,
      `mobile` char(15) DEFAULT NULL,
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED;
    
    -- ----------------------------
    -- Records of sys_user
    -- ----------------------------
    BEGIN;
    INSERT INTO `sys_user` VALUES (1, 'jack', '$2a$10$GqJuJhe7zmtwwThIed7smu9zMJBgSQzFMP47eEDL.g9tg8Y82.A7m', '13268050688');
    INSERT INTO `sys_user` VALUES (2, 'rose', '$2a$10$GqJuJhe7zmtwwThIed7smu9zMJBgSQzFMP47eEDL.g9tg8Y82.A7m', '13268050688');
    COMMIT;
    
    -- ----------------------------
    -- Table structure for sys_user_role
    -- ----------------------------
    DROP TABLE IF EXISTS `sys_user_role`;
    CREATE TABLE `sys_user_role` (
      `user_id` int(11) NOT NULL,
      `role_id` int(11) NOT NULL,
      PRIMARY KEY (`user_id`,`role_id`) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
    
    -- ----------------------------
    -- Records of sys_user_role
    -- ----------------------------
    BEGIN;
    INSERT INTO `sys_user_role` VALUES (1, 1);
    INSERT INTO `sys_user_role` VALUES (2, 2);
    INSERT INTO `sys_user_role` VALUES (2, 3);
    COMMIT;
    
    SET FOREIGN_KEY_CHECKS = 1;
    

    4.2 service层

    @Service
    public class SysUserService {
        @Autowired
        private JdbcTemplate jdbcTemplate;
        public SysUser getSysUserByUsenameOrMobile(String usernameOrMobile) throws EmptyResultDataAccessException {
            String sql = "select u.*, r.role_name, p.permmit from sys_user u join sys_user_role ur on u.id = ur.user_id " +
                    "             join sys_role r on r.id = ur.role_id " +
                    " join sys_role_permission rp on r.id = rp.role_id " +
                    " join sys_permission p on rp.permission_id = p.id" +
                    " where u.username = ? or u.mobile = ? ";
            List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, usernameOrMobile, usernameOrMobile);
            SysUser sysUser = null;
            if(null != list && list.size() > 0) {
                sysUser = new SysUser();
                Map<String, Object> map = list.get(0);
                sysUser.setId((Integer)map.get("id"));
                sysUser.setUsername((String)map.get("username"));
                sysUser.setPassword((String)map.get("password"));
                sysUser.setMobile((String)map.get("mobile"));
    
                Set<String> roles = new HashSet<>();
                Set<String> permissions = new HashSet<>();
                // 是设置 角色
                list.forEach(rows -> {
                    roles.add((String)rows.get("role_name"));
                    permissions.add((String) rows.get("permmit"));
                });
                sysUser.setRoles(roles);
                sysUser.setPermissions(permissions);
            }
            return sysUser;
        }
    }
    

    4.3 用户权限的查询

    // 该类的作用是处理用户登录名和密码
    @Component
    public class UserSecurityService implements UserDetailsService {
        private static Logger logger = LoggerFactory.getLogger(UserSecurityService.class);
        @Resource
        private SysUserService sysUserService;
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            logger.info("用户名或者电话:" + username);
            SysUser sysUser = null;
            try{
                sysUser = sysUserService.getSysUserByUsenameOrMobile(username);
            }catch (EmptyResultDataAccessException exception) {  //没有对应的用户名异常
                throw new UsernameNotFoundException("用户或密码错误");
            }
            if(null == sysUser) {
                throw new UsernameNotFoundException("用户名或密码错误");
            }else {
                /**
                 * User第一参数是:用户名
                 *     第二个参数是:pssword, 是从数据库查出来的
                 *     第三个参数是: 权限
                 */
                User user =  null;
                try{
                    user = new User(username,
                            sysUser.getPassword(),
    //                        getAuthorities(sysUser.getRoles()));
                            getMethodsAuthorities(sysUser.getPermissions()));
                }catch (InternalAuthenticationServiceException exception) {
                    throw exception;  // 在此处,将异常接着往外抛,抛给AuthenticationFailureHandler处理
                }
                return user;
            }
        }
        // [new SimpleGrantedAuthority("user:list"), new SimpleGrantedAuthority("user:add")]
        private List<SimpleGrantedAuthority> getMethodsAuthorities(Set<String> permissions) {
            List<SimpleGrantedAuthority> list = new ArrayList<>();
            if(null != permissions && permissions.size() > 0){
                permissions.forEach(permission -> {
                    SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(permission); // user:list dept:list
                    list.add(simpleGrantedAuthority);
                });
            }
            return list;
        }
        // [new SimpleGrantedAuthority("ROLE_admin"), new SimpleGrantedAuthority("ROLE_xxx")]
        // 封装用户角色权限
        private List<SimpleGrantedAuthority> getAuthorities(Set<String> roles) {
            List<SimpleGrantedAuthority> list = new ArrayList<>();
           if(null != roles && roles.size() > 0){
                roles.forEach(role -> {
                    SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("ROLE_" + role);
                    list.add(simpleGrantedAuthority);
                });
            }
            return list;
        }
    }
    

    4.4 Controller层

    // @RolesAllowed("admin")
    @RequestMapping("/user")
    @Controller
    public class UserController {
    
        /**
         * This annotation can be specified on a class or on method(s). Specifying it
         *  at a class level means that it applies to all the methods in the class.
         *  Specifying it on a method means that it is applicable to that method only.
         *
         * @RolesAllowed  可以写的值 ROLE_admin  admin
         *
         * he <code>Secured</code> annotation is used to define a list of security configuration
         *  attributes for business methods.
         * @Secured  只支持ROLE_admin  尽量用在service中。
         */
        @RequestMapping
        // @RolesAllowed("admin")
        // @Secured({"ROLE_admin", "ROLE_finance"})
        // @PreAuthorize("hasAuthority('user:list')")
        public Object get() {
            return "/index.html";
        }
    
    
        @ResponseBody
        @RequestMapping("/list")
        // @PreAuthorize("hasRole('ROLE_admin')")
        //@RolesAllowed({"admin", "finance", "administration "})
        // @PreAuthorize("hasRole('admin') or hasRole('finance')")
        // @PreAuthorize("hasRole('admin') and hasRole('finance')")  //表是同时拥有这两个角色才能访问
        // @PreAuthorize("hasAnyRole('admin', 'finance')")  //与上面的属性形式一样
        // @PreAuthorize("hasAuthority('user:list')") //3个用户都有这个权限
        public Object list() {
            return "list";
        }
    
        @ResponseBody
        @RequestMapping("/delete")
        // @PreAuthorize("hasAuthority('user:delete')") //只有jack
        public Object delete() {
            return "delete";
        }
    
        @ResponseBody
        @RequestMapping("/export")
        // @PreAuthorize("hasAuthority('user:export')") // 只有rose, jack
        public Object export() {
            return "export";
        }
    }
    

    相关文章

      网友评论

        本文标题:五. Spring Security 权限管理

        本文链接:https://www.haomeiwen.com/subject/vajnmctx.html