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

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

源码地址: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,即增删改查。

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

SpringBoot集成Spring Security(5)——权限控制的更多相关文章

  1. SpringBoot集成Spring Security入门体验

    一.前言 Spring Security 和 Apache Shiro 都是安全框架,为Java应用程序提供身份认证和授权. 二者区别 Spring Security:重量级安全框架 Apache S ...

  2. request.getRemoteUser() Spring Security做权限控制后

    一. request.getRemoteUser();//获取当前缓存的用户,比如Spring Security做权限控制后就会将用户登录名缓存到这里 request.getRemoteAddr(); ...

  3. SpringBoot集成Spring Security(7)——认证流程

    文章目录 一.认证流程 二.多个请求共享认证信息 三.获取用户认证信息 在前面的六章中,介绍了 Spring Security 的基础使用,在继续深入向下的学习前,有必要理解清楚 Spring Sec ...

  4. SpringBoot集成Spring Security(6)——登录管理

    文章目录 一.自定义认证成功.失败处理 1.1 CustomAuthenticationSuccessHandler 1.2 CustomAuthenticationFailureHandler 1. ...

  5. SpringBoot集成Spring Security(4)——自定义表单登录

    通过前面三篇文章,你应该大致了解了 Spring Security 的流程.你应该发现了,真正的 login 请求是由 Spring Security 帮我们处理的,那么我们如何实现自定义表单登录呢, ...

  6. SpringBoot集成Spring Security(2)——自动登录

    在上一章:SpringBoot集成Spring Security(1)——入门程序中,我们实现了入门程序,本篇为该程序加上自动登录的功能. 文章目录 一.修改login.html二.两种实现方式 2. ...

  7. SpringBoot集成Spring Security

    1.Spring Security介绍 Spring security,是一个强大的和高度可定制的身份验证和访问控制框架.它是确保基于Spring的应用程序的标准 --来自官方参考手册 Spring ...

  8. SpringBoot 集成Spring security

    Spring security作为一种安全框架,使用简单,能够很轻松的集成到springboot项目中,下面讲一下如何在SpringBoot中集成Spring Security.使用gradle项目管 ...

  9. springBoot整合spring security实现权限管理(单体应用版)--筑基初期

    写在前面 在前面的学习当中,我们对spring security有了一个小小的认识,接下来我们整合目前的主流框架springBoot,实现权限的管理. 在这之前,假定你已经了解了基于资源的权限管理模型 ...

随机推荐

  1. Oracle 查询练习

    非常经典的一些日常醒脑练习内容!! 如有更高效的写法欢迎赐教! .已知Oracle的Scott用户中提供了三个测试数据库表,名称分别为dept,emp和salgrade.使用SQL语言完成以下操作 ) ...

  2. 网格弹簧质点系统模拟(Spring-Mass System by Fast Method)附源码(转载)

    转载:  https://www.cnblogs.com/shushen/p/5311828.html 弹簧质点模型的求解方法包括显式欧拉积分和隐式欧拉积分等方法,其中显式欧拉积分求解快速,但积分步长 ...

  3. .net post请求webservice

    class Program { static void Main(string[] args) { , name = "jxp" }); var a = HttpHelper.Po ...

  4. NET 已知excel表格前面26个是a到z,27是aa28是ab,以此类推,N是多少

    问题: 已知excel表格前面26个是a到z,27是aa28是ab,以此类推,N是多少 代码: /// <summary> /// 已知excel表格前面26个是a到z,27是aa28是a ...

  5. The connection string name is missing for the MySqlSiteMapProvider

    在ASP.NET-WebForm程序中,添加SiteMapPath控件时出现问题,如下图所示: 解决办法:找到上图源文件指向的machine.config配置文件,将siteMap节点注释即可.

  6. putty连接centos慢

    用的vmware下的centos minimal镜像,开发时,用putty连接很慢,一分多钟, 解决方案: 禁用GSSAPI认证有两个方式:客户端和服务端 直接配置你ssh客户端的文件/etc/ssh ...

  7. Struts2框架简单介绍

    如需,了解Struts2详情,请点击,传送门 工作原理 在Struts2 框架中的处理大概分为以下步骤: 1.客户端初始化一个指向servlet容器(例如Tomcat)的请求. 2.这个请求经过一系列 ...

  8. Linux管道及重定向

    Linux管道及重定向 对shell有一定了解的人都知道,管道和重定向是 Linux 中非常实用的 IPC 机制.在shell中,我们通常使用符合'|'来表示管道,符号'>'和'<'表示重 ...

  9. Redis事务控制

    Redis事务控制 1.Redis事务控制的相关命令汇总 命令名 作用 MULTI 表示开始收集命令,后面所有命令都不是马上执行,而是加入到一个队列中. EXEC 执行MULTI后面命令队列中的所有命 ...

  10. React入门(二)

    组件的生命周期 概念:在组件创建.到加载到页面运行.以及组件被销毁的过程中伴随的事件.组件的生命周期是指在组件的特定时期触发的事件. 组件的生命周期分为三个部分: 组件创建阶段:只执行一次 compo ...