shiro实战系列(九)之Web-阿里云开发者社区

开发者社区> 安全> 正文

shiro实战系列(九)之Web

简介: 一、Configuration(配置) 将 Shiro 集成到任何 Web 应用程序的最简单的方法是在 web.xml 中配置 ContextListener 和 Filter,理解如何读取 Shiro 的 INI 配置文件。

 

一、Configuration(配置)

将 Shiro 集成到任何 Web 应用程序的最简单的方法是在 web.xml 中配置 ContextListener 和 Filter,理解如何读取 Shiro 的 INI 配置文件。大部分的 INI 配置格式定义在 Configuration 页的 INI Sections 节,但我在这里我们将介绍一些额外 的 Web 的特定部分。

 

Shiro 1.2 and later 在 Shiro 1.2 及以后版本,标准的 Web 应用程序通过添加下面的 XML 块到 web.xml 来初始化 Shiro

这假设一个 Shiro INI 配置文件在以下两个位置任意一个,并使用最先发现的那个:

 1. /WEB-INF/shiro.ini

 2. 在 classpath 根目录下 shiro.ini 文件  

 

下面是上述配置所做的事情: 

(1)    EnvironmentLoaderListener 初始化一个 Shiro WebEnvironment 实例(其中包含 Shiro 需要的一切操作,包括 SecurityManager),使得它在 ServletContext 中能够被访问。如果你需要在任何时候获得 WebEnvironment 实 例,你可以调用 WebUtils.getRequiredWebEnvironment(ServletContext)。 

(2)     ShiroFilter 将使用此 WebEnvironment 对任何过滤的请求执行所有必要的安全操作。 

(3)    最后,filter-mapping 的定义确保了所有的请求被 ShiroFilter 过滤,建议大多数 Web 应用程序使用以确保任何 请求是安全的。 

 

二、ShiroFilter filter-mapping              

它通常是可取的在任何其他 filter-mapping 声明之前定义 ShiroFilter filter-mapping,以确保 Shiro 也能在那些过滤器 下工作的很好。  

 

三、 Custom WebEnvironment Class

 默认情况下,EnvironmentLoaderListener 将创建一个 IniWebEnvironment 实例,呈现 Shiro 基于 INI 文件的配置。如 果你愿意,你可以在 web.xml 中指定一个自定义的 ServletContext context-param:

这允许你自定义一个如何解析和代表 WebEnvironment 实例的配置格式。你可以为自定义的行为对现有的 IniWebEnvironment 创建子类,或完全支持不同的配置格式。例如,如果有人想在 XML 中配置 Shiro 而不是在 INI 中, 他们可以创建一个基于 XML 的实现,如 com.foo.bar.shiro.XmlWebEnviroment

四、Custom Configuration Locations

IniWebEnvironment 将会去读取和加载 INI 配置文件。默认情况下,这个类会自动地在下面两个位置寻找 Shiro.ini 配 置(按顺序)。 1. /WEB-INF/shiro.ini 2. classpath:shiro.ini 它将使用最先发现的那个。   然而,如果你想把你的配置放在另一位置,你可以在 web.xml 中用 contex-param 指定该位置。

默认情况下,在 ServletContext.getResource 方法定义的规则下,param-value 是可以被解析的。例如, /WEB-INF/some/path/shiro.ini。  

但你也可以指定具体的文件系统,如 classpath 或 URL 位置,通过使用 Shiro 支持的合适的资源前缀,例如: 

  (1)file://home/foobar/myapp/shiro.ini 

(2)classpath:com/foo/bar/shiro.ini  (3)url:http://confighost.mycompany.com/myapp/shiro.ini

 

 

 

Shiro 1.1 and earlier

 

在 Web 应用程序中使用 Shiro 1.1 或更早版本的最简单的方法是定义 IniShiroFilter 并指定一个 filter-mapping:

该定义期望你的 INI 配置是一个在 classpath 根目录的 Shiro.ini 文件(如:classpath:shiro.ini)。

 

Custom Path

如果你不想将你的 INI 配置放在/WEB-INF/shiro.ini 或 classpath:shiro.ini,你可以指定一个自定义的资源位置,如果必 要的话。添加一个 configPath 的 init-param,并指定资源位置。

不合格的(不完整的组合或'non-prefixed')configPath 值被假定为 ServletContext 的资源路径,通过 ServletContext.getResource 方法所定义的规则来解析。

通过分别地使用 classpath:,url:,或 file:前缀来指明 classpath,url,或 filesystem 位置,你也可以指定其他非 ServletContext 资源位置。例如:

 

Inline Config

 最后,也可以将你的 INI 配置嵌入到 web.xml 中而不使用一个独立的 INI 文件。你可以通过使用 init-param 做到这点, 而不是 configPath:

内嵌配置对于小型的或简单的应用程序通常是很好用的,但是由于以下原因一般把它具体化到一个专用的 Shiro.ini 文件中:

  (1)你可能编辑了许多安全配置,不希望为 web.xml 添加版本控制。 

(2)你可能想从余下的 web.xml 配置中分离安全配置。 

(3)你的安全配置可能变得很大,你想保持 web.xml 的苗条并易于阅读。 

(4)你有个负责的编译系统,相同的 shiro 配置可能需要在多个地方被引用。 

 这取决于你——使用什么使你的项目更有意义。

 

Subject Inspection

[urls]项允许你做一些在我们已经见过的任何 Web 框架都不存在的东西:在你的应用程序中定义自适应过滤器链来 匹配 URL 路径!

 

  这将更为灵活,功能更为强大,比你通常在 web.xml 中定义的过滤器链更为简洁:即使你从未使用任何 Shiro 提供 的其他功能并仅仅使用了这个,但它即使是单独使用也是值得的。 

 

[urls] 在 urls 项的每一行格式如下:

URL_Ant_Path_Expression = Path_Specific_Filter_Chain  

 

接下来我们将讨论这些行的具体含义。

 

URL Path Expressions  

 

等号左边是一个与 Web 应用程序上下文根目录相关的 Ant 风格的路径表达式。   例如,假设你有如下的[urls]行:

此行表明,“任何对我应用程序的/accout 或任何它的子路径(/account/foo, account/bar/baz,等等)的请求都将触 发'ssl, authc'过滤器链”。我们将在下面讨论过滤器链。   请注意,所有的路径表达式都是相对于你的应用程序的上下文根目录而言的。这意味着如果某一天你在某个位置部 署了你的应用程序,如 www.somehost.com/myapp ,然后又将它部署到了 www.anotherhost.com (没有'myapp'子 目录),这样的匹配模式仍将继续工作。所有的路径都是相对于 HttpServletRequest.getContextPath()的值来的。

 

Filter Chain Definitions

 等号右边是逗号隔开的过滤器列表,用来执行匹配该路径的请求。它必须符合以下格式: filter1[optional_config1], filter2[optional_config2], ..., filterN[optional_configN]   并且: 

(1)    filterN 是一个定义在[main]项中的 filter bean 的名字。 

(2)     [optional_configN]是一个可选的括号内的对特定的路径,特定的过滤器有特定含义的字符串(每个过滤器,每 个路径的具体配置!)。若果该过滤器对该 URL 路径并不需要特定的配置,你可以忽略括号,于是 filteNr[] 就变成了 filterN.

 

因为过滤器标志符定义了链(又名列表),所以请记住顺序问题!请按顺序定义好你的逗号分隔的列表,这样请求 就能够流通这个链。  

 

最后,每个过滤器按照它期望的方式自由的处理请求,即使不具备必要的条件(例如,执行一个重定向,响应一个 HTTP 错误代码,直接渲染等)。否则,它有可能允许该请求继续通过这个过滤器链到达最终的视图。

Available Filters

在过滤器链中能够使用的过滤器“池”被定义在[main]项。在[main]项中指派给它们的名字就是在过滤器链定义中使 用的名字。例如:

 

Default Filters

当运行一个 Web 应用程序时,Shiro 将会创建一些有用的默认 Filter 实例,并自动地在[main]项中将它们置为可用。 你可以在 main 中配置它们,当作在你的链的定义中你是否有任何其他的 bean 和 reference。例如:

自动地可用的默认的 Filter 实例是被 DefaultFilter 枚举定义的,枚举的名称字段是可供配置的名称。它们是:

由于这是与任何过滤器链定义机制(web.xml,Shiro 的 INI 等)相关的例子,你通过在过滤器链中包含它来启用过 滤器,通过在过滤器链中移除它来禁用过滤器。  

 

但在 Shiro 1.2 中新增的一个新功能是不通过从过滤器链中移除过滤器来启用或禁用过滤器。如果启用(默认设置), 那么请求将如预期一样过滤。如果禁用,那么该过滤器将允许请求立即通过到 FilterChain 的下一个元素。你可以基 于一般配置属性触发过滤器的启用状态,或者你甚至可以在每一个请求的基础上触发。   这是一个强大的概念,因为基于特定需求启用或禁用一个过滤器比更改静态过滤器链(这是永久的且固定的)定义 更为方便。 

 Shiro通过它的OncePerRequestFilter抽象父类来完成这点。所有Shiro的不受规范限制的过滤器实现子类实现这一点, 因此不需要从过滤器链移除它们实现启用或禁用。如果你需要实现此功能,你可以为自己的过滤器实现继承这个类 的子类。

 

 

  SHIRO-224 将有望为任何过滤器使用这项功能,不仅仅只是那些 OncePerRequestFilter 的子类。如果这对你很重要, 请为这个 issue 投票。

 

 

General Enabling/Disabling

OncePerRequestFilter(及其所有子类)支持 Enabling/Disabling 所有请求及 per-request 基础。   一般为所有的请求启用或禁用一个过滤器是通过设置其 enabled 属性为 true 或 false。默认的设置是 true 由于大多 数过滤器本质上是需要执行的,如果他们被配置在一个过滤器链中。   例如,在 shiro.ini 中:

该例表明,许多潜在的 URL 路径都需要请求必须通过 SSL 连接保证。在开发中设置 SSL 是令人沮丧且费时的。在开 发时,你可以禁用 ssl 过滤器。当部署产品时,你可以启用它通过一个配置属性——这比手动更改所有 URL 路径或 维护两个 Shiro 配置要容易得多。

 

 

Request-specific Enabling/Disabling

 OncePerRequestFilter 实际上决定过滤器启用或禁用是基于它的 isEnabled(request, response)方法。   该方法默认返回 enabled 属性的值,该属性通常是用来 enabling/disabling 上面提及的所有请求。如果你想启用或禁 用一个基于特定标准的请求的过滤器,你可以通过覆盖 OncePerRequestFilter 的 isEnabled(request, response)方法来 执行更多特定的检查。  

 

Path-specific Enabling/Disabling

Shiro 的 PathMatchingFilter(一个 OncePerRequestFilter 的子类)能够对基于被过滤的特定路径的配置作出反应。这 意味着你可以启用或禁用一个过滤器基于路径和特定路径配置,除了传入的 request 和 response。   如果你需要能够对匹配的路径和特定路径配置作出反应来判断一个过滤器是否是启用的或禁用的,而不是通过覆盖 OncePerRequestFilter 的 isEnabled(request, reponse)方法,你应该是覆盖 PathMatchingFilter 的 isEnabled(request, response)方法。  

 

Remember Me Services

Shiro 将执行'rememberMe'服务如果 AuthenticationToken 实现了 org.apache.shiro.authc.RememberMeAuthenticationToken 接口。该接口指定了一个方法:

该例表明,许多潜在的 URL 路径都需要请求必须通过 SSL 连接保证。在开发中设置 SSL 是令人沮丧且费时的。在开 发时,你可以禁用 ssl 过滤器。当部署产品时,你可以启用它通过一个配置属性——这比手动更改所有 URL 路径或 维护两个 Shiro 配置要容易得多。

 

 

Request-specific Enabling/Disabling

 OncePerRequestFilter 实际上决定过滤器启用或禁用是基于它的 isEnabled(request, response)方法。   该方法默认返回 enabled 属性的值,该属性通常是用来 enabling/disabling 上面提及的所有请求。如果你想启用或禁 用一个基于特定标准的请求的过滤器,你可以通过覆盖 OncePerRequestFilter 的 isEnabled(request, response)方法来 执行更多特定的检查。  

 

Path-specific Enabling/Disabling

Shiro 的 PathMatchingFilter(一个 OncePerRequestFilter 的子类)能够对基于被过滤的特定路径的配置作出反应。这 意味着你可以启用或禁用一个过滤器基于路径和特定路径配置,除了传入的 request 和 response。   如果你需要能够对匹配的路径和特定路径配置作出反应来判断一个过滤器是否是启用的或禁用的,而不是通过覆盖 OncePerRequestFilter 的 isEnabled(request, reponse)方法,你应该是覆盖 PathMatchingFilter 的 isEnabled(request, response)方法。  

 

Remember Me Services

Shiro 将执行'rememberMe'服务如果 AuthenticationToken 实现了 org.apache.shiro.authc.RememberMeAuthenticationToken 接口。该接口指定了一个方法:

如果该方法返回 true,Shiro 将会在整个会话中记住终端用户的身份 ID。

Programmatic Support

要有计划性地使用 rememberMe,你可以在一个支持该配置的类上把它的值设为 true。例如,使用标准的 UsernamePasswordToken:

Form-based Login

对于 Web 应用程序而言,authc 过滤器默认是 FormAuthenticationFilter。它支持将'rememberMe'的布尔值作为一个 form/request 参数读取。默认地,它期望该 request 参数被命名为 rememberMe。下面是一个支持这点的 shiro.ini 配 置的例子: 

同时在你的 web form 中有一个名为'rememberMe'的 checkbox。

 

默认地,FormAuthenticationFilter 将会寻找名为 username,password 及 rememberMe 的 request 参数。如果这些不 同于你使用的 form 中的表单域名,你可能想在 FormAuthenticationFilter 上配置这些参数名。例如,在 shiro.ini 中:

Cookie configuration

你可以通过设定{{RememberMeManager}}的各方面的 cookie 属性来配置 rememberMe cookie 是如何工作的。例如, 在 shiro.ini 中:

Custom RememberMeManager

 应该注意到,默认基于 cookie 的 RememberMeManager 实现不符合你的需求,你可以插入任何你喜欢的插件到 securityManager 当中,就像你配置任何其他对象的引用一样:

JSP/GSP Tag Library

 Apache Shiro 提供了一个 Subject-aware JSP/GSP 标签库,它允许你控制你的 JSP,JSTL 或 GSP 页面基于当前 Subject 的状态进行输出。这对于根据身份个性化视图及当前用户所浏览的页面授权状态是相当有用的。  

 

Tag Library Configuration

 标签库描述文件(TLD )被打包在 META-INF/shiro.tld 文件中的 shiro-web.jar 文件中。要使用任何标签,添加下面一行 到你 JSP 页面(或任何你定义的页面指令)的顶部。  

<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

  我们使用 shiro 前缀用以表明 shiro 标签库命名空间,当然你可以指定任何你喜欢的名字。   现在我们将讨论每一个标签,并展示它是如何用来渲染页面的。

 The guest tag

guest 标签将显示它包含的内容,仅当当前的 Subject 被认为是'guest'时。'guest'是指没有身份 ID 的任何 Subject。也 就是说,我们并不知道用户是谁,因为他们没有登录并且他们没有在上一次的访问中被记住(RememberMe 服务)。 

 例子:  

guest 标签与 user 标签逻辑相反。

 

The user tag

user 标签将显示它包含的内容,仅当当前的 Subject 被认为是'user'时。'user'在上下文中被定义为一个已知身份 ID 的 Subject,或是成功通过身份验证及通过'RememberMe'服务的。请注意这个标签在语义上与 authenticated 标签是 不同的,authenticated 标签更为严格。

usre 标签与 guest 标签逻辑相反

 

 

The authenticated tag

仅仅只当当前用户在当前会话中成功地通过了身份验证 authenticated 标签才会显示包含的内容。它比'user'标签更 为严格。它在逻辑上与'notAuthenticated'标签相反。   authenticated 标签只有当当前 Subject 在其当前的会话中成功地通过了身份验证才会显示包含的内容。它比 user 标 签更为严格,authenticated 标签通常在敏感的工作流中用来确保身份 ID 是可靠的。  

例子:

authenticated 标签与 notAuthenticated 标签逻辑相反。

 

The notAuthenticated tag

notAuthenticated 标签将会显示它所包含的内容,如果当前 Subject 还没有在其当前会话中成功地通过验证。   

notAuthenticated 标签与 Authenticated 标签逻辑相反。

 

 

The principal tag

principal 标签将会输出 Subject 的主体(标识属性)或主要的属性。 若没有任何标签属性,则标签将使用 principal 的 toString()值来呈现页面。例如(假设 principal 是一个字符串的用户 名): 

Typed principal

principal 标签默认情况下,假定该 principal 输出的是 subject.getPrincipal()的值。但若你想输出一个不是主要 principal 的值,而是属于另一个 Subject 的 principal collection,你可以通过类型来获取该 principal 并输出该值。   例如,输出 Subject 的用户 ID(并不是 username),假设该 ID 属于 principal collection:

 

Principal property

但如果该 principal(是默认主要的 principal 或是上面的'typed' principal)是一个复杂的对象而不是一个简单的字符串, 而且你希望引用该 principal 上的一个属性该怎么办呢?你可以使用 property 属性来来表示 property 的名称来理解 (必须通过 JavaBeans 兼容的 getter 方法访问)。例如(假主要的 principal 是一个 User 对象) 

The hasRole tag

 hasRole 标签将会显示它所包含的内容,仅当当前 Subject 被分配了具体的角色。

The lacksRole tag

 lacksRole 标签将会显示它所包含的内容,仅当当前 Subject 未被分配具体的角色。

The hasAnyRole tag

 hasAnyRole 标签将会显示它所包含的内容,如果当前的 Subject 被分配了任意一个来自于逗号分隔的角色名列表中 的具体角色。

The hasPermission tag

 hasPermission 标签将会显示它所包含的内容,仅当当前 Subject“拥有”(蕴含)特定的权限。也就是说,用户具 有特定的能力。     

The lacksPermission tag

lacksPermission 标签将会显示它所包含的内容,仅当当前 Subject 没有拥有(蕴含)特定的权限。也就是说,用户没 有特定的能力。

 

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
+ 订阅

云安全开发者的大本营

其他文章