项目源码:https://github.com/weimingge14/Shiro-project
演示地址:http://liweiblog.duapp.com/Shiro-project/login

关于 Shiro 的权限匹配器和过滤器

上一节,我们实现了自定义的 Realm,方式是继承 AuthorizingRealm这个抽象类,分别实现认证的方法和授权的方法。

这一节实现的代码的执行顺序: 
1、Shiro定义的过滤器和自定义的过滤器,在自定义的过滤器中执行 Subject对象的判断是否具有某项权限的方法 isPermitted(),传入某一个跟当前登录对象相关的特征值(这里是登录对象正在访问的 url 连接) 
2、程序走到自定义的 Realm 中的授权方法中,根据已经认证过的主体查询该主体具有的角色和权限字符串,通常情况下是一个角色的集合和一个权限的集合。

此时我们第 1 步有一个字符串,第 2 步有一个字符串的集合(权限的集合)。 
程序要帮我们做的就是看第 1 步的字符串在不在第 2 步的字符串集合中。那么这件事情是如何实现的呢?

3、此时程序检测到配置文件中有声明一个实现了 PermissionResolver 的类,这个时候程序就会到这个类中去查找所采用的权限匹配策略。 
4、到上一步返回的实现了 Permission 的类的对象中的 implies()方法中去进行判断。如果第 2 步的权限字符串数量多于 1 个,这个 implies()就会执行多次,直到该方法返回 true 为止,第 1 步的 isPermitted() 才会返回 true。

下面我们来关注一下 [urls]这个节点下面的部分。

[urls]
# 配置 url 与使用的过滤器之间的关系
/admin/**=authc,resourceCheckFilter
/login=anon

其中

/admin/**=authc,resourceCheckFilter

表示,当请求 /admin/** 的时候,会依次经过 (1)authc和 (2)resourceCheckFilter 这两个过滤器。

过滤器在有些地方也叫拦截器,他们的意思是一样的。

(1)authc这个过滤器是 Shiro 自定义的认证过滤器,即到自定义 Realm 的认证方法里面去按照指定的规则进行用户名和密码的匹配。 
DefaultFilter这个枚举类里面定义了多个自定义的过滤器,可以直接使用。

(2)resourceCheckFilter 是一个自定义的过滤器,我们来看看它的声明:

[filters]
# 声明一个自定义的过滤器
resourceCheckFilter = com.liwei.shiro.filter.ResourceCheckFilter
# 为上面声明的自定义过滤器注入属性值
resourceCheckFilter.errorUrl=/unAuthorization

实现:

public class ResourceCheckFilter extends AccessControlFilter {

    private String errorUrl;

    public String getErrorUrl() {
return errorUrl;
} public void setErrorUrl(String errorUrl) {
this.errorUrl = errorUrl;
} private static final Logger logger = LoggerFactory.getLogger(ResourceCheckFilter.class); /**
* 表示是否允许访问 ,如果允许访问返回true,否则false;
* @param servletRequest
* @param servletResponse
* @param o 表示写在拦截器中括号里面的字符串 mappedValue 就是 [urls] 配置中拦截器参数部分
* @return
* @throws Exception
*/
@Override
protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
Subject subject = getSubject(servletRequest,servletResponse);
String url = getPathWithinApplication(servletRequest);
logger.debug("当前用户正在访问的 url => " + url);
return subject.isPermitted(url);
} /**
* onAccessDenied:表示当访问拒绝时是否已经处理了;如果返回 true 表示需要继续处理;如果返回 false 表示该拦截器实例已经处理了,将直接返回即可。 * @param servletRequest
* @param servletResponse
* @return
* @throws Exception
*/
@Override
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
logger.debug("当 isAccessAllowed 返回 false 的时候,才会执行 method onAccessDenied "); HttpServletRequest request =(HttpServletRequest) servletRequest;
HttpServletResponse response =(HttpServletResponse) servletResponse;
response.sendRedirect(request.getContextPath() + this.errorUrl); // 返回 false 表示已经处理,例如页面跳转啥的,表示不在走以下的拦截器了(如果还有配置的话)
return false;
}
}

注意:我们首先要关注 isAccessAllowed()方法,在这个方法中,如果返回 true,则表示“通过”,走到下一个过滤器。如果没有下一个过滤器的话,表示具有了访问某个资源的权限。如果返回 false,则会调用 onAccessDenied 方法,去实现相应的当过滤不通过的时候执行的操作,例如跳转到某一个指定的登录页面,去引导用户输入另一个具有更大权限的用户名和密码进行登录。

isAccessAllowed()方法的最后一个参数 o,可以获得我们自定义的过滤器后面中括号中所带的参数。

我们再跳回到 isAccessAllowed()中:subject.isPermitted(url)。说明通过继承 AccessControlFilter我们可以得到认证主体 Subject和当前请求的 url 链接,它们的 API 分别是:

获得认证主体:

Subject subject = getSubject(servletRequest,servletResponse);

与 
获得当前请求的 url

String url = getPathWithinApplication(servletRequest);

然后,我们调用了 subject.isPermitted(url)方法,将 url 这个字符串对象传入。

此时我们的流程应该走到 Realm的授权方法中,通过查询(经过了认证的)用户信息去查询该用户具有的权限信息。此时的代码走到了这里。 
在授权方法中,我们看到 SimpleAuthorizationInfo的角色信息和权限信息都是通过字符串来解析的。 
角色信息和权限信息都是集合。

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
logger.info("--- MyRealm doGetAuthorizationInfo ---"); // 获得经过认证的主体信息
User user = (User)principalCollection.getPrimaryPrincipal(); //
// 此处为节约篇幅,突出重点省略
// SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setRoles(new HashSet<>(roleSnList));
info.setStringPermissions(new HashSet<>(resStrList)); // 以上完成了动态地对用户授权
logger.debug("role => " + roleSnList);
logger.debug("permission => " + resStrList); return info;
}

在这个 Realm 的授权方法中,完成了对该认证后的主体所具有的角色和权限的查询,然后放入 SimpleAuthorizationInfo对象中。

接下来就要进行 subject.isPermitted(url)中的 url 和 自定义 Realm 中的授权方法中的 info.setStringPermissions(new HashSet<>(resStrList));权限字符串集合的匹配操作了。

权限信息: 

它们都是从数据库中查询出来的。

那么如何实现匹配呢?比较简单的一个思路就是比较字符串,但是这件简单的比较的事情被 Shiro 定义为一个 PermissionResolver,通过实现 PermissionResolver,我们可以为完成自定义的权限匹配操作,可以是简单的字符串匹配,也可以稍有灵活性的通配符匹配,这都取决于我们程序员自己。

public class UrlPermissionResolver implements PermissionResolver {

    private static final Logger logger = LoggerFactory.getLogger(UrlPermissionResolver.class);

    /**
* 经过调试发现
* subject.isPermitted(url) 中传入的字符串
* 和自定义 Realm 中传入的权限字符串集合都要经过这个 resolver
* @param s
* @return
*/
@Override
public Permission resolvePermission(String s) {
logger.debug("s => " + s); if(s.startsWith("/")){
return new UrlPermission(s);
}
return new WildcardPermission(s);
}
}

可以看到,权限信息是通过字符串:“/admin/**”等来进行匹配的。这时就不能使用 Shiro 默认的权限匹配器 WildcardPermission了。

而 UrlPermission 是一个实现了 Permission接口的类,它的 implies 方法的实现决定了权限是否匹配,所以 implies 这个方法的实现是很重要的。

public class UrlPermission implements Permission {

    private static final Logger logger = LoggerFactory.getLogger(UrlPermission.class);

    // 在 Realm 的授权方法中,由数据库查询出来的权限字符串
private String url; public UrlPermission(String url){
this.url = url;
} /**
* 一个很重要的方法,用户判断 Realm 中设置的权限和从数据库或者配置文件中传递进来的权限信息是否匹配
* 如果 Realm 的授权方法中,一个认证主体有多个权限,会进行遍历,直到匹配成功为止
* this.url 是在遍历状态中变化的
*
* urlPermission.url 是从 subject.isPermitted(url)
* 传递到 UrlPermissionResolver 中传递过来的,就一个固定值
*
* @param permission
* @return
*/
@Override
public boolean implies(Permission permission) {
if(!(permission instanceof UrlPermission)){
return false;
}
//
UrlPermission urlPermission = (UrlPermission)permission;
PatternMatcher patternMatcher = new AntPathMatcher(); logger.debug("this.url(来自数据库中存放的通配符数据),在 Realm 的授权方法中注入的 => " + this.url);
logger.debug("urlPermission.url(来自浏览器正在访问的链接) => " + urlPermission.url);
boolean matches = patternMatcher.matches(this.url,urlPermission.url);
logger.debug("matches => " + matches);
return matches;
}
}

重点说明:如果在自定义的 Realm 中的授权方法中传入的授权信息中的权限信息是一个集合,那么这里的 implies 就会进行遍历,直到这个方法返回 true 为止,如果遍历的过程全部返回 false,就说明该认证主体不具有访问某个资源的权限。

关于 Shiro 的权限匹配器和过滤器的更多相关文章

  1. 自定义shiro实现权限验证方法isAccessAllowed

    由于Shiro filterChainDefinitions中 roles默认是and, admin= user,roles[system,general] 比如:roles[system,gener ...

  2. 【shiro】(5)---基于Shiro的权限管理

    基于Shiro的权限管理项目搭建 前面写了四篇有关权限的文章,算是这篇文章的铺垫了.这篇文章采用 开发环境           JDK1.8          Eclipse          Mav ...

  3. Shiro权限管理框架(三):Shiro中权限过滤器的初始化流程和实现原理

    本篇是Shiro系列第三篇,Shiro中的过滤器初始化流程和实现原理.Shiro基于URL的权限控制是通过Filter实现的,本篇从我们注入的ShiroFilterFactoryBean开始入手,翻看 ...

  4. spring boot 2 + shiro 实现权限管理

    Shiro是一个功能强大且易于使用的Java安全框架,主要功能有身份验证.授权.加密和会话管理.看了网上一些文章,下面2篇文章写得不错.Springboot2.0 集成shiro权限管理 Spring ...

  5. springboot + shiro 构建权限模块

    权限模块基本流程 权限模块的基本流程:用户申请账号和权限 -->登陆认证 -->安全管控模块认证 -->调用具体权限模块(基于角色的权限控制) --> 登陆成功 -->访 ...

  6. Shiro (包含权限满足其中一个就通过的用法)

    方法/步骤 1 web.xml添加配置 <!-- shiro过滤器 --> <filter> <filter-name>shiroFilter</filter ...

  7. spring boot + mybatis + layui + shiro后台权限管理系统

    后台管理系统 版本更新 后续版本更新内容 链接入口: springboot + shiro之登录人数限制.登录判断重定向.session时间设置:https://blog.51cto.com/wyai ...

  8. SpringBoot整合Shiro实现权限控制,验证码

    本文介绍 SpringBoot 整合 shiro,相对于 Spring Security 而言,shiro 更加简单,没有那么复杂. 目前我的需求是一个博客系统,有用户和管理员两种角色.一个用户可能有 ...

  9. SpringBoot整合Shiro实现权限控制

    目录 1.SpringBoot整合Shiro 1.1.shiro简介 1.2.代码的具体实现 1.2.1.Maven的配置 1.2.2.整合需要实现的类 1.2.3.项目结构 1.2.4.ShiroC ...

随机推荐

  1. Redis 配置登录密码

    1. 通过配置文件进行配置 打开 redis.conf,找到 #requirepass foobared 去掉行前的注释,并修改密码为所需的密码,保存文件 重启redis sudo service r ...

  2. Leetcode 522.最长特殊序列II

    最长特殊序列II 给定字符串列表,你需要从它们中找出最长的特殊序列.最长特殊序列定义如下:该序列为某字符串独有的最长子序列(即不能是其他字符串的子序列). 子序列可以通过删去字符串中的某些字符实现,但 ...

  3. Java学习3之成员方法及函数重载

    方法的定义:方法名称,返回值,参数列表,修饰符(权限修饰符,final,static),实现体. 参考自:<Java 程序设计与工程实践> 方法的签名: 唯一区别其他方法的元素:(1)方法 ...

  4. java之ArrayList.add

    ArrayList添加 public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! ele ...

  5. 函数的apply、call方法和length属性

    函数的apply.call方法和length属性JavaScript为函数对象定义了两个方法:apply和call,它们的作用都是将函数绑定到另外一个对象上去运行,两者仅在定义参数的方式有所区别:Fu ...

  6. 禁用jQuery chosen的选择下拉菜单

    想法是启用被勾掉之后,左侧下拉框禁用.这是chosen()的 disabled之后需要更新一下.就这样,还有别的方法的话请分享,O(∩_∩)O哈哈~

  7. BZOJ 4590 [Shoi2015]自动刷题机 ——二分答案

    二分答案水题. #include <cstdio> #include <cstring> #include <iostream> #include <algo ...

  8. 觉醒力量 (hidpower)

    觉醒力量 (hidpower) 题目描述 [题目背景] 从前有一款非常火的游戏被人们称为pokemon,从最初的红绿蓝黄版直到现在的XY版,都受到世界各地小朋友和大朋友们的喜爱. [题意描述] 作为一 ...

  9. Normalize.css用法

    1 Normalize.css用法 重置样式非常多,Normalize.css和reset是两个常用的重置 CSS 文件 http://necolas.github.io/normalize.css/ ...

  10. SQL只获取字段中的中文字符

    原文发布时间为:2010-10-28 -- 来源于本人的百度文章 [由搬家工具导入] 新建标量函数 set ANSI_NULLS ONset QUOTED_IDENTIFIER ONGOALTER f ...