Apache Shiro™是一个功能强大且易于使用的Java安全框架,可执行身份验证,授权,加密和会话管理。借助Shiro易于理解的API,可以快速轻松地保护任何应用程序 - 从最小的移动应用程序到最大的Web和企业应用程序。
1. Apache Shiro 四基石
shiro通过以下为平台的应用程序的提供安全API。
- Authentication : 身份认证,通常称为 “用户登录”。
- Authorization:授权。
- Crpytography:保护或隐藏会被窥探的数据。
- Session Management:会话session管理
shiro提供一些辅助功能,如:Web应用程序安全、单元测试、多线程支持,但都围绕这四基石为准。
1.1 Authentication
身份验证是验证用户身份的过程。也就是说,当用户使用应用程序进行身份验证时,他们证明他们实际上是他们所说的人。这有时也被称为“登录”。这通常是一个三步过程。
- 收集用户的标识信息,称为主体,并支持身份证明,称为凭证。
- 将主体和凭据提交给系统。
- 如果提交的凭据与系统对该用户标识(主体)的期望值匹配,则认为该用户已通过身份验证。如果它们不匹配,则不会将用户视为已通过身份验证。
每个人都熟悉的这个过程的一个常见例子是用户名/密码组合。当大多数用户登录软件应用程序时,他们通常会提供用户名(主体)和支持密码(凭证)。如果存储在系统中的密码(或其表示)与用户指定的密码匹配,则认为它们已经过身份验证。
Shiro以简单直观的方式支持相同的工作流程。正如我们所说,Shiro有一个以主题为中心的API - 几乎所有你在运行时用Shiro做的事都是通过与当前正在执行的Subject进行交互来实现的。因此,要登录主题,只需调用其登录方法,传递一个AuthenticationToken实例,该实例表示提交的主体和凭据(在本例中为用户名和密码)。
//1. Acquire submitted principals and credentials:
AuthenticationToken token = new UsernamePasswordToken(username, password);
//2. Get the current Subject:
Subject currentUser = SecurityUtils.getSubject();
//3. Login:
currentUser.login(token);
复制代码
Shiro的API很容易反映出常见的工作流程。您将继续将此简单性视为所有主题操作的主题。调用login方法时,SecurityManager将接收AuthenticationToken并将其分派给一个或多个已配置的域,以允许每个域根据需要执行身份验证检查。每个Realm都能够根据需要对提交的AuthenticationTokens做出反应。但是如果登录尝试失败会发生什么?如果用户指定了错误的密码该怎么办?您可以通过对Shiro的运行时AuthenticationException作出反应来处理故障.
//3. Login:
try {
currentUser.login(token);
} catch (IncorrectCredentialsException ice) { …
} catch (LockedAccountException lae) { …
}
…
catch (AuthenticationException ae) {…
}
复制代码
可以选择捕获其中一个AuthenticationException子类并进行具体反应,或者通常处理任何AuthenticationException(例如,向用户显示通用的“不正确的用户名或密码”消息)。根据应用要求,您可以选择。
在成功登录主题后,它们将被视为已通过身份验证,通常您允许它们使用您的应用程序。但仅仅因为用户证明了他们的身份并不意味着他们可以在您的应用程序中做任何他们想做的事情。这引出了下一个问题,“我如何控制允许用户做什么?”决定允许用户做什么称为授权。我们将介绍Shiro如何启用授权。
1.2 Authorization
授权本质上是访问控制 - 控制用户可以在应用程序中访问的内容,例如资源,网页等。大多数用户通过使用角色和权限等概念来执行访问控制。也就是说,通常允许用户基于分配给他们的角色和/或许可来做某事或不做某事。然后,您的应用程序可以根据对这些角色和权限的检查来控制公开的功能。正如您所料,Subject API允许您非常轻松地执行角色和权限检查。例如,清单7中的代码片段显示了如何检查Subject是否已分配了某个角色。
if ( subject.hasRole(“administrator”) ) {
//show the ‘Create User’ button
} else {
//grey-out the button?
}
复制代码
权限检查是执行授权的另一种方式。如上例所示检查角色会遇到一个重大缺陷:您无法在运行时添加或删除角色。代码使用角色名称进行了硬编码,因此如果您更改了角色名称和/或配置,您的代码就会被破坏!如果需要能够在运行时更改角色的含义,或者根据需要添加或删除角色,则必须依赖其他内容。
if ( subject.isPermitted(“user:create”) ) {
//show the ‘Create User’ button
} else {
//grey-out the button?
}
复制代码
通过拥有反映应用程序原始功能的权限,只需要更改权限更改应用程序的功能时检查。反过来,您可以在运行时根据需要为角色或用户分配权限。
这样,任何分配了“user:create”权限的角色或用户都可以单击“创建用户”按钮,这些角色和分配甚至可以在运行时更改,提供非常灵活的安全模型。
“user:create”字符串是遵循某些解析约定的权限字符串的示例。Shiro通过WildcardPermission开箱即用,支持这种约定。WildcardPermission在创建安全策略时非常灵活,甚至支持实例级访问控制等内容。
if ( subject.isPermitted(“user:delete:jsmith”) ) {
//delete the ‘jsmith’ user
} else {
//don’t delete ‘jsmith’
}
复制代码
1.3 Session Management
Apache Shiro在安全框架领域提供了一些独特的东西:
一致的会话API,可用于任何应用程序和任何架构层。也就是说,Shiro为任何应用程序启用了会话编程范例 - 从小型守护程序独立应用程序到最大的集群Web应用程序。这意味着希望使用会话的应用程序开发人员不再需要使用Servlet或EJB容器,否则就不需要它们。
但也许Shiro会话最重要的好处之一就是它们与容器无关。这具有微妙但极其强大的含义。例如,
- 让我们考虑会话群集。有多少特定于容器的方法来集群会话以进行容错和故障转移?Tomcat的做法与Jetty有所不同,它与Websphere等不同。但是通过Shiro会话,您可以获得与容器无关的群集解决方案。Shiro的体系结构允许可插入的Session数据存储,例如企业缓存,关系数据库,NoSQL系统等。这意味着您可以配置一次会话群集,无论您的部署环境如何( Tomcat,Jetty,JEE Server或独立应用程序),它都将以相同的方式工作。 2. Shiro会话的另一个好处是,如果需要,可以跨客户端技术共享会话数据。例如,如果需要,Swing桌面客户端可以参与同一个Web应用程序会话 - 如果最终用户同时使用两者,则会很有用。那么如何在任何环境中访问主题会话?有两种主题方法,如下例所示:
Session session = subject.getSession();
Session session = subject.getSession(boolean create);
复制代码
这些方法在概念上与HttpServletRequest API相同。第一种方法将返回Subject的现有Session,或者如果还没有,它将创建一个新的并返回它。第二种方法接受一个布尔参数,该参数确定是否将创建新的Session(如果它尚不存在)。获得主题会话后,您几乎可以使用它与HttpSession相同。Shiro团队认为HttpSession API对Java开发人员来说最为舒适,因此我们保留了很多感觉。当然,最大的区别在于您可以在任何应用程序中使用Shiro Sessions,而不仅仅是Web应用程序。
Session session = subject.getSession();
session.getAttribute(“key”, someValue);
Date start = session.getStartTimestamp();
Date timestamp = session.getLastAccessTime();
session.setTimeout(millis);
...
复制代码
1.4 Cryptography
加密是为了混淆或隐藏数据,所以,第三方不能窥探到对应数据。Shiro的加密使用了JDK的相关加密。
Cryptography在加密中包含了:
- hashes加密
- 密码加密
1.4.1 hash
JDK中的MessageDigest
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.digest(bytes);
byte[] hashed = md.digest();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
复制代码
shiro的hash
String encodedPassword = new Sha512Hash(password,salt,count).toBase64();
复制代码
与JDK的Cipher API相比,Shiro示例更简单:
- 您可以直接实例化CipherService - 没有奇怪或令人困惑的工厂方法。
- 密码配置选项表示为兼容JavaBeans的getter和setter - 没有奇怪且难以理解的“转换字符串”。
- 加密和解密在单个方法调用中执行。
- 没有强制检查异常。如果你愿意,可以抓住Shiro的CryptoException
Shiro的CipherService API还有其他好处,例如支持基于字节数组的加密/解密(称为“块”操作)以及基于流的加密/解密(例如,加密音频或视频)的能力。
Java密码学不需要痛苦。Shiro的加密支持旨在简化您保护数据安全的工作。
2. Apache Shiro 三大主要概念
Shiro的架构有三个主要概念 - Subject,SecurityManager和Realms。
2. 1 Subject
Subject 这个词是一个安全术语,基本上是指“当前正在执行的用户”。但它不被称为“用户”,因为“用户”这个词通常与人类有关。在安全的世界,术语“主题”可以指一个人,但也有可能是进程,守护进程帐户,或任何类似。它只是意味着“当前与软件交互的东西”。但是,对于大多数意图和目的,我们可以将其视为Shiro的“用户”概念。您可以在代码中的任何位置轻松获取Shiro Subject,如下所示。
import org.apache.shiro.subject.Subject;
import org.apache.shiro.SecurityUtils;
...
Subject currentUser = SecurityUtils.getSubject();
复制代码
获得 Subject 后,可以立即访问当前用户希望使用Shiro执行的90%所有的操作的信息,例如登录,注销,访问其会话,执行授权检查等等 。但稍后会详细介绍。这里的关键点是Shiro的API在很大程度上是直观的,因为它反映了开发人员在 ‘per-user’ 安全控制中思考的自然倾向。在代码中的任何位置访问主题也很容易,允许在需要的任何地方进行安全操作。
2.2 SecurityManager
Subject 的管理者则为 SecurityManager 。Subject表示当前用户的操作模式,而 SecurityManager 管理 所有用户 的安全操作。SecurityManager 是Shiro的核心,充当一种“伞形”对象,它引用了许多形成对象图的内部嵌套安全组件。
每个应用程序几乎总是有一个SecurityManager实例。它本质上是一个应用程序单例(尽管它不需要是静态单例)。与Shiro中的几乎所有内容一样,默认的SecurityManager实现是POJO,可以使用任何兼容POJO的配置机制进行配置 - 普通Java代码,Spring XML,YAML,.properties和.ini文件等。基本上任何能够实例化的东西可以使用类和调用JavaBeans兼容的方法。
2.2.1 使用INI配置Shiro
[main]
cm = org.apache.shiro.authc.credential.HashedCredentialsMatcher
cm.hashAlgorithm = SHA-512
cm.hashIterations = 1024
#Base64 encoding(less text):
cm.storedCredentialsHexEncoded = false
iniRealm.credentialsMatcher = $cm
[users]
jdoe = TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJpcyByZWFzb2
asmith = IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbXNoZWQsIG5vdCB
复制代码
从以上配置中,我们可以看到使用INI配置Shiro时,有两个关键配置项:[main] 和 [users]
[main]部分用于配置SecurityManager对象和/或SecurityManager使用的任何对象(如Realms)。在此示例中,我们看到正在配置两个对象:
- cm对象,是Shiro的HashedCredentialsMatcher类的一个实例。如您所见,cm实例的各种属性正在通过'嵌套点'语法(清单3中所示的IniSecurityManagerFactory使用的约定)来配置,以表示对象图导航和属性设置。
- iniRealm对象,它是SecurityManager用于表示以INI格式定义的用户帐户的组件。
在[users]部分指定用户帐户的静态列表 - 方便简单应用程序或测试时。
2.2.2 加载INI配置
// 1。加载INI配置
Factory factory =
new IniSecurityManagerFactory(“classpath:shiro.ini”);
// 2。创建SecurityManager
SecurityManager securityManager = factory.getInstance();
// 3。使其可访问
SecurityUtils.setSecurityManager(securityManager);
复制代码
3. Realms
Realms充当Shiro与应用程序的“桥梁”或‘连接器“,也就是说,当实际与安全相关的数据(如用户帐户)进行交互以执行身份验证(登录)和授权(访问控制)时,Shiro会从为应用程序配置的一个或多个领域中查找许多这些内容。
Realm本质上是一个特定于安全性的DAO:它封装了数据源的连接细节,并根据需要使相关数据可用于Shiro。配置Shiro时,必须至少指定一个Realm用于身份验证和/或授权。可以配置多个Realm,但至少需要一个。
Shiro提供了开箱即用的Realms,可以连接到许多安全数据源(也称为目录),如LDAP,关系数据库(JDBC),文本配置源(如INI和属性文件等)。如果默认域不符合您的需要,可以插入自己的Realm实现来表示自定义数据源。
[main]
ldapRealm = org.apache.shiro.realm.ldap.JndiLdapRealm
ldapRealm.userDnTemplate = uid={0},ou=users,dc=mycompany,dc=com
ldapRealm.contextFactory.url = ldap://ldapHost:389
ldapRealm.contextFactory.authenticationMechanism = DIGEST-MD5
复制代码
4. Web Support
通过配置网络请求、限制等,以此达到请求拦截,以及过滤请求,并将请求到对应资源。
配置网络过滤:
<filter>
<filter-name>ShiroFilterfilter-name>
<filter-class>
org.apache.shiro.web.servlet.IniShiroFilter
filter-class>
filter>
<filter-mapping>
<filter-name>ShiroFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
复制代码
此过滤器可以读取前面提到的shiro.ini配置,因此无论部署环境如何,您都可以获得一致的配置体验。配置完成后,Shiro过滤器将过滤每个请求,并确保在请求期间可以访问特定于请求的主题。并且因为它会过滤每个请求,您可以执行特定于安全性的逻辑,以确保只允许满足特定条件的请求。
4.1 特定于URL的筛选器链
Shiro通过其创新的URL过滤器链接功能支持特定于安全性的过滤规则。它允许您为任何匹配的URL模式指定ad-hoc过滤器链。这意味着您可以使用Shiro的过滤机制在执行安全规则(或规则组合)方面具有很大的灵活性 - 远远超过您可以单独在web.xml中定义过滤器。
[urls]
/assets/** = anon
/user/signup = anon
/user/** = user
/rpc/rest/** = perms[rpc:invoke], authc
/** = authc
复制代码
上面看到的过滤器名称(anon,user,perms,authc)是Shiro提供的特殊安全相关过滤器。可以混合和匹配这些安全筛选器,以创建非常自定义的安全体验。还可以指定您可能拥有的任何其他现有Servlet过滤器。
4.2 网络会话管理
4.2.1 默认Http会话
对于Web应用程序,Shiro默认其会话基础结构使用我们以前习惯的现有Servlet容器会话。也就是说,当你调用方法 subject.getSession()和 subject.getSession(boolean)时,Shiro将返回由Servlet容器的HttpSession实例支持的Session实例。
这种方法的优点在于调用subject.getSession()的业务层代码与Shiro Session实例交互 - 它没有“知识”它正在使用基于Web的HttpSession对象。在跨建筑层保持清洁分离时,这是一件非常好的事情。
4.2.2 Shiro在Web层中的本地会话
如果已经在Web应用程序中启用了Shiro的本机会话管理,因为需要Shiro的企业会话功能(如与容器无关的群集),当然希望HttpServletRequest.getSession()和HttpSession API与“本机”会话一起使用,不是servlet容器会话。
如果你必须重构任何使用HttpServletRequest和HttpSession API的代码来改为使用Shiro的Session API,那将是非常令人沮丧的。Shiro当然不会指望你这样做。相反,Shiro完全实现了Servlet规范的Session部分,以支持Web应用程序中的本机会话。这意味着无论何时调用相应的HttpServletRequest或HttpSession方法调用,Shiro都会将这些调用委托给其内部本地Session API。
5. Apache Shiro框架 “彩蛋”
Apache Shiro框架中还有其他一些对保护Java应用程序有用的功能,例如:
- 跨线程维护主题的线程和并发支持(Executor和ExecutorService支持)
- Callable和Runnable支持将逻辑作为特定主题执行
- “运行为”支持假设另一个主题的身份(例如在管理应用程序中有用)
- 测试线束支持,使得在单元和集成测试中完全测试Shiro安全代码非常容易