Springboot系列之八:集成Spring Security(5)——权限控制

本文详细介绍了如何在用户角色权限模型中实现权限管理,包括创建权限表、定义实体类、Mapper和服务层,以及自定义PermissionEvaluator来处理hasPermission()方法。通过具体示例展示了如何在接口上应用权限检查。

https://blog.youkuaiyun.com/yuanlaijike/article/details/80327880

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

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

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

一、数据准备

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;

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

内容就是两条数据,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;
    }
}

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

这里需要注意的时相比于数据库,多了一个 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);
}

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

(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);
    }
}

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

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权限";
    }
}

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

让我们修改下我们要访问的接口,@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) {
        // 获得loadUserByUsername()方法的结果
        User user = (User)authentication.getPrincipal();
        // 获得loadUserByUsername()中注入的角色
        Collection<GrantedAuthority> authorities = user.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;
    }
}

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

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

思路如下:

  1. 通过 Authentication 取出登录用户的所有 Role

  2. 遍历每一个 Role,获取到每个Role的所有 Permission

  3. 遍历每一个 Permission,只要有一个 Permissionurl 和传入的url相同,且该 Permission 中包含传入的权限,返回 true

  4. 如果遍历都结束,还没有找到,返回false

下面就是在 WebSecurityConfig 中注册 CustomPermissionEvaluator

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
	...
    /**
     * 注入自定义PermissionEvaluator
     */
    @Bean
    public DefaultWebSecurityExpressionHandler webSecurityExpressionHandler(){
        DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
        handler.setPermissionEvaluator(new CustomPermissionEvaluator());
        return handler;
    }
	...
}

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

三、运行程序

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

运行结果

一直想做一款后台管理系统,看了很多优秀的开源项目但是发现没有合适自己的。于是利用空闲休息时间开始自己写一套后台系统。如此有了若依管理系统。她可以用于所有的Web应用程序,如网站管理后台,网站会员中心,CMS,CRM,OA。所有前端后台代码封装过后十分精简易上手,出错效率低。同时支持移动客户端访问。系统会陆续更新一些实用功能。 您是否在找一套合适后台管理系统。 您是否在找一套代码易读易懂后台管理系统。 那么,现在若依来了。诚意奉献之作 若依是给刚出生的女儿取的名字 寓意:你若不离不弃,我必生死相依 内置功能 用户管理:用户是系统操作者。 部门管理:配置系统组织机构。 岗位管理:岗位是用户所属职务。 菜单管理:配置系统菜单(支持控制到按钮)。 角色管理:角色菜单权限分配。 字典管理:对系统中经常使用的一些较为固定的数据进行维护。 操作日志:系统操作日志记录(含异常)。 登录日志:系统登录情况记录(含异常)。 在线用户:当前系统中活跃用户状态监控。 连接池监视:监视当期系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。 为何选择若依 是一个完全响应式,基于Bootstrap3.3.6最新版本开发的主题。 她可以用于所有的Web应用程序,如网站管理后台,网站会员中心,CMS,CRM,OA。 拥有良好的代码结构,层次结构清晰。内置一系列基础功能。 操作权限控制精密细致,对所有管理链接都进行权限验证,可控制到按钮。 提供在线功能代码生成工具,提高开发效率及质量。 提供常用工具类封装,日志、国际化、缓存、验证、字典等数据。 兼容目前最流行浏览器(IE7+、Chrome、Firefox)手机移动端也支持。 技术选型 1、后端 核心框架:Spring Boot 安全框架:Apache Shiro 模板引擎:Thymeleaf 持久层框架:MyBatis 数据库连接池:Druid 缓存框架:Ehcache 日志管理:SLF4J 工具类:Apache Commons Fastjson POJO:Lombok 2、前端 框架:Bootstrap 数据表格:Bootstrap Table 客户端验证:JQuery Validation 树结构控件:zTree 弹出层:layer 3、平台 服务器中间件:SpringBoot内置 数据库支持:目前仅提供MySql数据库的支持,但不限于数据库 开发环境:Java、Eclipse、Maven、Git
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值