Java开源安全框架之Apache Shiro

 

APACHE SHIRO安全框架

1      背景

 

Shiro项目始于2003年初,当时它叫JSecurity项目,当时对于Java应用开发人员没有太多的安全替代方案,始终被一个叫JAAS(Java认证/受权服务)束缚着,可是JAAS缺点太多了,如它的受权机制太拙劣,用起来让人沮丧,又一方面JAAS跟虚拟机层面的安全问题关系很是紧密,如判断JVM中判断是否容许装入一个类等,还有加密问题,JAVA中的密码架构又是让人难以理解。因而Jsecurity就诞生了,后来改名为Shiro。html

直到2008年Shiro加入到了APACHE软件基金会,直到如今它叫Apache Shiro。java

2      特性

Ø  易于使用 :易用性是这个项目的最终目标。web

Ø  普遍性:没有其余安全框架能够达到Shiro宣传的广度,它能够为你的安全需求提供“一站式”服务。算法

Ø  灵活性:Shiro能够工做在任何应用环境中,而不依赖于任何的应用环境。数据库

Ø  Web能力:Shiro对Web应用支持很神奇,容许你基于URL和WEB协议建立灵活的安全策略,同时还提供了一套控制页面输出的JSP标签库。apache

Ø  可插拔:Shiro干净的API和设计模式能够方便的与许多的其它框架和应用进行无缝集成。编程

Ø  支持:Shiro是APACHE软件基金会的成员。设计模式

3      简介

Apache Shiro是一个强大易用的Java安全框架,能够用其为你的应用护航。经过简化应用安全的四个领域,即认证、受权、会话管理和加密,在真实应用中,应用安全能更容易被理解和实现。api

Shiro的简单架构和兼容JavaBean几乎可以在任何环境下配置和使用。附加的Web支持和辅助功能,好比多线程和测试支持,让这个框架为应用安全提供了“一站式”服务。Apache Shiro开发团队将继续前进,精炼代码库和支持社区。随着持续被开源和商业应用采纳,能够预期Shiro会继续发展壮大。数组

(1)认证:用户身份识别,常被称之为“登录”操做。

(2)受权:访问控制。

(3)加密:密码加密,保护数据以防被偷窥。

(4)会话:会话管理,每用户相关的时间敏感的状态。

4      SHIRO重要组件


 

Shiro组件图形 1 重要组件

一. 主要的特性:

1.Authentication: 认证(也就是登录),这是一个证实用户所说的他们是谁的行为。

2.Authorization: 受权(访问控制的过程),也就是说“谁”去访问了什么。

3.Session Manager: 管理用户特定的会话,即便在非Web或EJB的应用程序中。

4.Cryptography: 经过使用加密算法保护数据安全同时易于使用。

二. 支持的特性:

1.Web Support: Web支持:Shiro Web API可以轻松的帮助保护Web应用程序。

2.Caching: 缓存支持:可让安全操做快速而高效。

3.Concurrency: 多线程支持:Apache Shiro利用它的并发特性来支持多线程应用程序。

4.Testing: 测试支持:帮助你编写单元测试和集成测试,确保你的应用可以按如期同样安全。

5.“Run As”: 一个容许用户假设为另外一个用户身份的功能。

6.“Remember Me”: 在会话中记住用户身份,因此咱们只须要在强制的时候登录。

5      SHIRO核心组件


 

Shiro组件图形 2 核心组件

1.Subject:

即当前操做用户,可是在Shiro中,Subject这一律念并不只仅指人,也考虑到第三方进程、后台帐户或者其余相似事物,它仅仅意味着“当前跟软件交互的东西”,你能够把它认为是Shiro的“用户”概念。Subject表明了当前用户的安全操做,SecurityManager则管理全部用户的安全操做。

2.SecurityManager:

它是Shiro框架的核心,Shiro经过SecurityManager来管理内部组件实例,并经过它来提供安全管理的各类服务。

特性:

Ø  基于POJO实现

Ø  简单的客户端存储

Ø  独立的容易集成

Ø  异构的客户端访问

Ø  事件的监听

Ø  主机地址的保留

Ø  不活动/过时的支持

Ø  网络支持

Ø  支持SSO

3.Realm:

Realm充当了Shiro与应用安全数据间的“桥梁”或者“链接器”。也就是说,当用户执行认证、受权的时候,Shiro会从应用配置的Realm中查找用户及权限信息。在配置Shiro时,你必须至少指定一个Realm用于认证和受权,配置多个Realm是没有问题的,可是至少须要配置一个。Realm内置了大量的安全的数据源,如:JDBC(关系型数据库)的Realm,LDAP(目录)的Realm,INI文本配置资源的Realm等,若是默认的Realm不能知足要求,还能够插入表明自定义数据源的Realm实现。

 

6      SHIRO架构图


 

Shiro组件图形 3 架构图

7      SHIRO认证过程


 

Shiro组件图形 4 认证过程

第一步:

应用程序构建一个终端用户认证信息的AuthenticationToken实例后,调用Subject.login()方法。

第二步:

Subject的实例一般是DelegaingSubject类或它的子类实例对象,在认证时,会委托应用程序设置securityManager实例而后调用securityManager.login(token)方法。

第三步:

 SecurityManager接收到token信息后会委托内置的Authenticator实例(一般

都是ModularRealmAuthenticator类的实例)调用authenticator.authenticate(token).

ModularRealmAuthenticator在认证过程当中会对设置的一个或多个Realm实例进行适配,它实际上为Shiro提供一个可插拔的认证机制。

第四步:

若是在应用程序中配置多个Realm,ModularRealmAuthenticator会根据配置AuthenticationStrategy(认证策略)来进行多Realm的认证过程。在Realm被调用后,AuthenticationStrategy将对每个Realm的结果作出相应。(若是应用程序中仅配置一个Realm,Realm将被直接调用无需再配置认证策略)

第五步:

判断每个Realm是否支持提交的Token,若是支持,Realm将调用getAuthenticationInfo(token),这个方法就是实际认证处理,咱们经过覆盖Realm的doGetAuthenticationInfo方法来编写咱们自定义的认证处理。

7.1    记住我和已认证

1.Shiro的RememberMe 和 经过认证的Authenticated有明确的区分:

(1).Remembered(记住我):

一个已记住的Subject不是匿名的,并且有一个已知的身份ID(也就是调用subject.getPrincipals()方法是非空的),可是这个被记住的身份ID是在以前的Session中被认证的,若是调用subject.isRemembered()返回true,则Subject被认为是被记住的。

(2).Authenticated(已认证):

一个已认证的Subject是指在当前Session中被成功地验证过了(也就是说subject.login()方法被调用并且没有抛出异常),若是调用subject.isAuthenticated()返回true则认为Subject已经过验证。

注意:Remembered和Authenticated是互斥的,其中一个为真则另外一个为假,反之亦然。

7.2    令牌

1. 令牌是一个键,能够用它登录一个系统,经常使用令牌是UsernamePasswordToken,用它能够指定用户名和密码。

2. UsernamePasswordToken类适用于大多数应用程序,能够扩展这个接口来提供您应用程序用来验证身份的一个关键文件内容。

3. 若是用户密码不正确会抛出:IncorrectCredentialsException,在生产代码中显示处理;

若是用户帐户不正确会抛出:UnknownAccountException,在生产代码中显示处理;

若是用户帐户被锁定会抛出:LockedAccountException,在生产代码中显示处理;

登录失败时其它异常会抛出:AuthenticationException,在生产代码中显示处理;

注意:既然能提供详细的用户名和密码验证信息,可是咱们最好不要提供详细的提示,避免用户猜想来登录系统。

7.3    认证策略

若是你的应用程序中使用多个Realm从多个数据源获取帐户资料,AuthenticationStrategy是最终为最后的“合并”可以被应用程序理解的Subject的身份的视图负责人。

认证策略的类

描述

AtLeastOneSuccessfulStrategy

若是一个或更多的Realm验证成功,则总体尝试被认为是成功的,若是没有一个验证成功,则总体尝试失败。

FirstSuccessfulStrategy

只有第一个成功的验证Realm返回的信息将被使用,全部进一步的Realm将被忽略,若是没有一个验证成功,则总体尝试失败。

AllSuccessfulStrategy

为了总体的尝试成功,全部配置的Realm必须验证成功,若是没有一个验证成功,则总体尝试失败。

7.4    注销

注销就是释放全部已知的识别状态,调用subject.logout()方法来释放识别状态信息。

调用logout会发生:

(1). 现有的Session将会失效,任何的身份将失去关联。

(2). RememberMe cookie也将被删除。

注意:因为Web应用程序依靠Cookies来记住用户身份,而后Cookies只能在Response被committed以前被删除,因此建议在调用注销方法以后将终端用户重定向到一个新的视图或页面,这样可以保证任何的安全相关的Cookies都能想预期同样被删除,这个是http cookies的功能,不是Shiro的。

8      SHIRO受权过程



 

Shiro组件图形 5 受权过程

 

第一步:

在应用程序中调用受权验证方法(例如:Subject的isPermitted*或者hasRole*等等)。

第二步:

Subject实例一般是DelegatingSubject的类或者它子类的实例对象,在认证开始时,会委托应用程序设置securityManager实例调用相应的 isPermitted*或者hasRole*方法。

第三步:

接下来SecurityManager会委托内置的Authorizer实例(默认是

ModularRealmAuthorizer类的实例,它支持一个或者多个Realm实例认证)调用相应的受权方法。

第四步:

每个Realm将检查是否实现了相同的Authorizer接口,而后,将调用Realm本身相应的受权验证方法。

8.1    权限粒度

Shiro权限声明能够作到很是细粒度,权限声明一般是使用以冒号分隔的表达式,一个权限表达式能够清晰的指定资源类型、容许的操做、可访问的数据。同时Shiro权限表达式支持简单的通配符,能够更加灵活的进行权限设置。

8.2    角色模式

Shiro的角色模式有传统角色、权限角色两种角色模式。

(1). 隐式角色(旧RBAC):当须要对某一操做进行受权时,只须要判断是否拥有该角色便可,这种角色权限相对简单、模糊。

(2). 显示角色(新RBAC):一个角色拥有一个权限集合,受权时,须要判断当前角色是否拥有该权限,这种角色权限能够对用户详细的权限描述。

8.3    受权实现方式

Shiro支持三种受权方式,即编码方式、注解方式、自定义标签方式。

8.3.1 编码实现

8.3.1.1Subject API【经常使用】

类型

方法和描述

void

checkPermission(Permission permission)

检查是否拥有指定的权限对象

void

checkPermission(String permission) 

检查是否拥有指定的权限字符串

void

checkPermissions(Collection<Permission> permissions) 
检查是否拥有指定的全部权限对象

void

checkPermissions(String... permissions) 

检查是否拥有指定的全部权限字符串

void

checkRole(String roleIdentifier) 

断言Subject拥有指定的角色

void

checkRoles(Collection<String> roleIdentifiers)

断言Subject拥有集合的全部角色

void

checkRoles(String... roleIdentifiers)

断言Subject拥有指定所有字符数组权限

Object

getPrincipal()

返回应用程序范围的Subject惟一当事人标识,若是为NULL说明Subject是匿名的。

Session

getSession()

返回与Subject相关的应用程序会话

boolean

hasAllRoles(Collection<String> roleIdentifiers)

若是Subject拥有指定的集合角色,返回True不然false

boolean

hasRole(String roleIdentifier)

若是Subject拥有指定字符串角色,返回Treu,不然false

Boolean[]

hasRoles(List<String> roleIdentifiers)

检查Subject是否拥有指定的角色,返回一个布尔型数组。

boolean

isAuthenticated()

是否被认证过,返回True,若是Subject/User证实了本身的身份在当前会话中

boolean[]

isPermitted(List<Permission> permissions)

检查是否拥有指定的权限集合,返回一个布尔值数组。

boolean

isPermitted(Permission permission)

若是Subject拥有指定的权限,返回True,不然返回false

boolean

isPermitted(String... permissions)

检查是否拥有指定权限数组,返回一个布尔型数组。

boolean

isPermitted(String permission)

若是Subject拥有指定权限字符串权限,返回True,不然返回false

boolean

isPermittedAll(Collection<Permission> permissions)

若是Subject拥有指定集合的全部权限,返回True,不然返回False

boolean

isPermittedAll(String... permissions)

若是Subject拥有指定字符数组的全部权限,返回True,不然返回False

boolean

isRemembered()

若是Subject不是匿名的,返回True,不然返回False

void

login(AuthenticationToken token)

执行Subject的登录操做

void

logout()

退出登录,移除Subject相关认证和受权数据

 

8.3.2 注解实现

Shiro注解支持AspectJ、Spring、Google-Guice等,可根据应用进行不一样的配置,注解可用于类/属性/方法。

8.3.2.1@ RequiresAuthentication

用于代表访问该资源的用户需是通过认证。

8.3.2.2@ RequiresGuest

代表访问该资源的用户为Guest用户。

8.3.2.3@ RequiresPermissions

当前用户需拥有指定权限。

8.3.2.4@ RequiresRoles

当前用户需拥有指定角色。

8.3.2.5@ RequiresUser

当前用户需为已认证用户或已记住用户。

8.3.3 标签实现

8.3.3.1JspTaglib实现

Shiro提供了一套JSP标签来实现页面受权访问控制:在使用Shiro标签库前,首先须要在JSP引入shiro标签:

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

8.3.3.2guest标签

验证当前用户是否为“访客”,即未认证(包含未记住)的用户

<shiro:guest>

 Hi there!  Please <a href="login.jsp">Login</a> or

 <a href="signup.jsp">Signup</a> today!

</shiro:guest>

8.3.3.3user标签 

认证经过或已记住的用户 

<shiro:user>

    Welcome back John!  Not John? Click <a href="login.jsp">here<a> to login.

</shiro:user>

8.3.3.4authenticated标签 

已认证经过的用户。不包含已记住的用户,这是与user标签的区别所在。

<shiro:authenticated>

    <a href="updateAccount.jsp">Update your contact information</a>.

</shiro:authenticated>

8.3.3.5notAuthenticated标签 

未认证经过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户。

<shiro:notAuthenticated>

    Please <a href="login.jsp">login</a> in order to update your credit card information.

</shiro:notAuthenticated>

8.3.3.6principal标签

输出当前用户信息,一般为登陆账号信息。

Hello, <shiro:principal/>, how are you today?

8.3.3.7hasRole标签 

验证当前用户是否属于该角色

<shiro:hasRole name="administrator">

    <a href="admin.jsp">Administer the system</a>

</shiro:hasRole>

8.3.3.8lacksRole标签 

与hasRole标签逻辑相反,当用户不属于该角色时验证经过

<shiro:lacksRole name="administrator">

    Sorry, you are not allowed to administer the system.

</shiro:lacksRole>

8.3.3.9hasAnyRole标签

验证当前用户是否属于如下任意一个角色。

<shiro:hasAnyRoles name="developer, project manager, administrator">

    You are either a developer, project manager, or administrator.

</shiro:lacksRole>

8.3.3.10        hasPermission标签 

验证当前用户是否拥有指定权限。

<shiro:hasPermission name="user:create">

    <a href="createUser.jsp">Create a new User</a>

</shiro:hasPermission>

8.3.3.11        lacksPermission标签 

与hasPermission标签逻辑相反,当前用户没有指定权限时,验证经过 。

<shiro:hasPermission name="user:create">

    <a href="createUser.jsp">Create a new User</a>

</shiro:hasPermission>

9      术语

9.1    Authentication

身份验证是验证Subject 身份的过程——实质上是证实某些人是否真的是他们所说的他们是谁。当认证尝试成功后,应用程序可以相信该subject 被保证是其所指望的。

9.2    Authorization

受权,又称为访问控制,是决定一个user/Subject 是否被容许作某事的过程。它一般是经过检查和解释Subject的角色和权限(见下文),而后容许或拒绝到一个请求的资源或功能来完成的。

9.3    Cipher['saɪfə]

密码是进行加密或解密的一种算法。该算法通常依赖于一块被称为key 的信息。基于不一样的key 的加密算法也是不同的,因此解密没有它是很是困难的。密码有不一样的表现形式。分组密码致力于符号块,一般是固定大小的,而流密码致力于连续的符号流。对称性密码加密和解密使用相同的密钥(key),而非对称性加密使用不一样的密钥。若是非对称性加密的密钥不能从其余地方获得,那么能够建立公钥/私钥对公开共享。

9.4    Credential[krɪ'denʃ(ə)l]

凭证是一块信息,用来验证user/Subject 的身份。在认证尝试期间,一个(或多个)凭证与Principals(s)被一同提交,来验证user/Subject 所提交的确实是所关联的用户。证书一般是很是秘密的东西,只有特定的user/Subject 才知道,如密码或PGP 密钥或生物属性或相似的机制。这个想法是为principal 设置的,只有一我的会知道正确的证书来“匹配”该principal。若是当前user/Subject

提供了正确的凭证匹配了存储在系统中的,那么系统能够假定并信任当前user/Subject 是真的他们所说的他们是谁。信任度随着更安全的凭证类型加深(如,生物识别签名 > 密码)。

9.5    Cryptography[krɪp'tɒgrəfɪ]

加密是保护信息不受不但愿的访问的习惯作法,经过隐藏信息或将它转化成无心义的东西,这样没人能够理解它。Shiro 致力于加密的两个核心要素:加密数据的密码,如使用公钥或私钥的邮件,以及散列表(也称消息摘要),它对数据进行不可逆的加密,如密码。

9.6    Hash

散列函数是单向的,不可逆转的输入源,有时也被称为消息,在一个编码的哈希值内部,有时也被称为消息摘要。它一般用于密码,数字指纹,或以字节数组为基础的数据。

9.7    Permission

权限,至少按照Shiro 的解释,是在应用程序中描述原始功能的一份声明并无更多的功能。权限是在安全策略中最低级别的概念。它们仅定义了应用程序可以作“什么”。它们没有说明“谁”可以执行这些操做。权限只是行为的声明,仅此而已。

一些权限的例子:

 打开文件

 浏览'/user/list'页面

 打印文档

 删除'jsmith'用户

9.8    Principal ['prɪnsəp(ə)l]

Principal 是一个应用程序用户(Subject)的任何标志属性。“标志属性”能够是任何对你应用程序有意义的东西——用户名,姓,名,社会安全号码,用户ID 等。这就是它——没什么古怪的。Shiro 也引用一些咱们称之为Subject 的primary principal 的东西。一个primary principal 是在整个应用程序中惟一标识Subject 的principal。理想的primary principal 是用户名或RDBMS 用户表主键——用户ID。对于在应用程序中的用户(Subject)来讲,只有一个primary principal

9.9    Realm

Realm 是一个可以访问应用程序特定的安全数据(如用户,角色和权限)的组件。它能够被看做是一个特定安全的DAO(Data Access Object)。Realm 将这些应用程序特定的数据转换成Shiro 可以理解的格式,这样Shiro反过来可以提供一个单一的易于理解的Subject 编程API,不管有多少数据源存在或不管你的数据是什么样的应用程序特定的格式。Realm 一般和数据源是一对一的对应关系,如关系数据库,LDAP 目录,文件系统,或其余相似资源。所以,Realm 接口的实现使用数据源特定的API 来展现受权数据(角色,权限等),如JDBC,文件IO,Hibernate 或JPA,或其余数据访问API。

9.10       Role

基于你对话的对象,一个角色的定义是能够多变的。在许多应用程序中,它充其量是个模糊不清的概念,人们用它来隐式定义安全策略。Shiro 偏向于把角色简单地解释为一组命名的权限的集合。这就是它——一个应用程序的惟一名称,汇集一个或多个权限声明。这是一个比许多应用程序使用的隐式的定义更为具体的定义。若是你选择了你的数据模型反映Shiro 的假设,你会发现将有更多控制安全策略的权力。

9.11       Session

会话是一个在一段时间内有状态的数据,其上下文与一个单一的与软件系统交互的user/Subject 相关联。当Subject 使用应用程序时,可以从会话中添加/读取/删除数据,而且应用程序稍后可以在须要的地方使用该数据。会话会被终止,因为user/Subject 注销或会话不活动而超时。对于那些熟悉HttpSession 的,Shiro Session 服务于同一目标,除了Shiro 会话可以在任何环境下使用,甚至在没有Servlet 容器或EJB 容器的环境。

9.12       Subject

Subject 只是一个精挑细选的安全术语,基本上的意思是一个应用程序用户的安全特定的“视图”。然而Subject不老是须要反映为一我的——它能够表明一个调用你应用程序的外部进程,或许是一个系统账户的守护进程,在一段时间内执行一些间歇性的东西(如一个cron job)。它基本上是任何使用应用程序作某事的实体的一个表明。

10            问题列表

10.1  如何实现无刷新登录验证提示?

http://www.kankanews.com/ICkengine/archives/48199.shtml

上面这个地址里面有详细介绍………

10.2       Realm是什么?

Realm有iniRealm、jdbcRealm、ldapRealm等,在认证、受权内部实现机制中都有提到,最终处理都将交给Realm进行处理,由于在Shiro中,最终是经过Realm来获取应用程序中的用户、角色、权限的,一般状况下,在Realm中会直接从咱们的数据源中获取Shiro须要的认证信息,能够说,Realm是专用于安全框架的Dao.

10.3       两个Session的区别?

Subject currentUser = SecurityUtils.getSubject();

Session session = currentUser.getSession();

Session.setAttribute(“someKey”,someValue);

HttpServletRequest.getSession(Boolean create)和Subject.getSession(Booleancreate)方法有殊途同归之效。

1. 若是Subject已经拥有了一个Session,则create参数被忽略且Session被当即返回。

2. 若是Subject尚未一个Session,且create参数为true,则建立一个新的会话并返回该会话。

3. 若是Subject尚未一个Session,且create参数为false,则不会建立新的会话且返回null。

10.4       URL过滤器如何配置?

Shiro主要是经过URL过滤来进行安全管理。

Shiro能够经过配置文件实现基于URL的受权验证。

每一个URL配置,表示匹配该URL的应用程序请求将由对应的过滤器进行验证。

URL目录是基于HttpServletRequest.getContextPath()此目录设置。

URL可以使用通配符,**表明任意子目录

Shiro验证URL时,URL匹配成功便不在继续匹配查找,因此要注意配置文件中的URL顺序,尤为在使用通配符时。

一个URL能够配置多个Filter,使用逗号分隔。

当设置多个过滤器时,所有验证经过,才视为经过。

部分过滤器可指定参数,如perms, roles。

3. 拦截器类型:

过滤器名

过滤器说明

过滤器类

anon

匿名过滤器

org.apache.shiro.web.filter.authc.AnonymousFilter

anthc

若是继续操做,须要作对应的表单验证,不然不能经过

org.apache.shiro.web.filter.authc.FormAuthenticationFilter

authcBasic

基于http验证过滤,若是不经过,跳转到登录页面

org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter

logout

登陆退出过滤器

org.apache.shiro.web.filter.authc.LogoutFilter

noSessionCreation

没有Session建立过滤器

org.apache.shiro.web.filter.session.NoSessionCreationFilter

perms

权限过滤器

org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter

port

端口过滤器,能够设置是不是指定端口,若是不是跳转到登录页面

org.apache.shiro.web.filter.authz.PortFilter

rest

http方法过滤器,能够指定如post不能进行访问等

org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter

roles

角色过滤器,判断当前用户是否指定角色

org.apache.shiro.web.filter.authz.RolesAuthorizationFilter

ssl

请求须要经过ssl,若是不是跳转到登录页

org.apache.shiro.web.filter.authz.SslFilter

user

若是访问一个已知用户,好比记住个人功能,走这个过滤器

org.apache.shiro.web.filter.authc.UserFilter

10.5       记住我和已认证何时用?

这通常是应用于电子商务网站中,举个例子:好比你正在访问亚马逊,你弄了几本书放到了购物车中,当你正要结帐的时候,你忽然有些事情不得不出去一下,结果你回来时候已经到了下班时间了,结果你就关机下班回家了,当你到家的时候你忽然想起来还有几本书没有完成付款,结果你打开网站要继续完成支付时你发现网站中还记录着你是谁,你看到了欢迎页面,可是当你要确认支付访问你的信用卡帐户信息时,网站会强制让你再次输入密码,已达到已认证的状态,由于亚马逊网站只记得你是谁,可是那并不能证实你就是你,也多是你的同事或者陌生人在使用你的电脑。

记住我和已认证必定是互斥的,一个为真则另外一个确定为假!

相关文章
相关标签/搜索