Apache Shiro是可用于身份验证和授权的框架。 本文提供了一些如何在Java™应用程序中使用Shiro的示例,并概述了如何在Grails Web应用程序中使用Shiro。 为了充分利用本文,您应该习惯于创建Java应用程序并安装以下组件:
在保护系统安全时,安全性的两个要素很重要:身份验证和授权。 尽管这两个术语含义不同,但是由于它们在应用程序安全性中的各自作用,有时有时可以互换使用。
身份验证涉及验证用户身份。 对用户进行身份验证时,您确认他们确实是他们声称的真实身份。 在大多数应用程序中,身份验证是通过用户名和密码的组合来完成的。 只要用户选择的密码足以让其他人难以猜到,则用户名和密码的组合通常足以确定身份。 但是,也可以使用其他身份验证方式,例如指纹,证书和生成的密钥。
身份验证过程成功建立身份后, 授权将接管以限制或授予访问权限。 通过身份验证,用户可以登录到系统,但是通过授权,则不允许执行任何操作。 对于未经身份验证的用户,也可以具有一定级别的授权。
在为应用程序计划安全模型时,必须解决这两个要素,以确保系统具有足够的安全级别。 身份验证是应用程序之间的常见问题(特别是仅使用用户名和密码完成时),因此让框架来处理工作是个好主意。 适当的框架具有测试和维护的优势,使您可以专注于业务问题,而不必重新解决已解决问题的问题。
Apache Shiro提供了一个可用的安全框架,各种客户端可以将其应用于他们的应用程序。 本文中的示例介绍了Shiro,并着重于验证用户的基本任务。
Shiro是用Java语言实现的框架,它通过易于使用的API提供身份验证和授权。 通过使用Shiro,您可以为应用程序提供安全性,而无需一开始就编写所有代码。
由于Shiro提供了许多不同数据源以及企业会话管理的身份验证,因此非常适合实现单点登录(SSO),这是大型企业中的一项理想功能,因为该企业通常每天需要用户登录并使用许多不同的系统。 这些数据源包括JDBC,LDAP,Kerberos和Microsoft®ActiveDirectory®目录服务(AD DS)。
Shiro的Session
对象使您无需使用HttpSession
可以使用用户的会话。 通过使用通用Session
对象,即使该代码未在Web应用程序中运行,您也可以使用相同的代码。 通过避免对应用程序服务器或Web应用程序服务器会话管理的需求,即使在命令行环境中,也可以使用Shiro。 换句话说,使用Shiro的API编写的代码使您可以构建连接到LDAP服务器的命令行应用程序,并且与访问LDAP服务器的Web应用程序中的代码相同。
Shiro带有预构建的二进制发行版。 您可以下载Shiro JAR文件,也可以将条目放入Apache Maven或Apache Ivy中以自动安装文件。 本示例使用Ivy通过清单1中所示的简单脚本下载Shiro JAR文件和其他所需的库。
请参阅相关主题有关使用常春藤的更多信息。 如果你不使用Maven或常春藤,从下载站点,这也是在提供下载四郎JAR文件相关主题 。
下载库后,只需将它们添加到CLASSPATH中。 编写清单2中所示的简单代码,该代码获得对当前用户的引用,并报告该用户未通过身份验证。 (您使用Subject
类代表用户。)
package com.nathanagood.examples.shirotest;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ShiroTest {
private static Logger logger = LoggerFactory.getLogger(ShiroTest.class);
public static void main(String[] args) {
// Using the IniSecurityManagerFactory, which will use the an INI file
// as the security file.
Factory factory =
new IniSecurityManagerFactory("auth.ini");
// Setting up the SecurityManager...
org.apache.shiro.mgt.SecurityManager securityManager
= factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject user = SecurityUtils.getSubject();
logger.info("User is authenticated: " + user.isAuthenticated());
}
}
添加此代码后,创建一个名为auth.ini的文件。 目前,该文件为空; 它仅存在,因此您可以在此处运行示例以检查代码是否正常运行。
创建文件后,运行示例。 您应该看到包含INFO日志消息的输出,该消息报告用户尚未登录。
SecurityUtils
对象是单例对象,这意味着不同的对象可以使用它来访问当前用户。 成功设置SecurityManager
,可以在应用程序的不同部分调用SecurityUtils.getSubject()
以获取当前用户的信息。
用Shiro术语来说, 令牌是用于登录系统的密钥。 一个基本的通用令牌是UsernamePasswordToken
,它使您可以为用户指定名称和密码。
UsernamePasswordToken
类实现AuthenticationToken
接口,该接口提供了一种获取凭据和用户主体(帐户身份)的方法。 UsernamePasswordToken
适用于大多数应用程序,并且您还可以根据需要扩展AuthenticationToken
接口,以包括您自己的获取凭据的方法。 例如,您可以扩展接口以提供应用程序用来认证用户的密钥文件的内容。
到目前为止,这个简单的示例涵盖了启动Shiro SecurityManager
,获取当前用户以及记录该用户尚未经过身份验证的情况。 此示例将使用UsernamePasswordToken
以及存储在INI文件中的用户记录来显示通过用户名和密码进行的身份验证。
清单3中显示的示例auth.ini文件现在包含用户的一条记录。 该记录包含用户名和密码。 您可以在此记录中定义角色,也可以为您的应用程序提供授权。
[users]
bjangles = dance
现在,创建上一部分介绍的UsernamePasswordToken
对象,如清单4所示。
// snipped... same as before.
public class ShiroTest {
private static Logger logger = LoggerFactory.getLogger(ShiroTest.class);
public static void main(String[] args) {
// Using the IniSecurityManagerFactory, which will use the an INI file
// as the security file.
Factory factory =
new IniSecurityManagerFactory("auth.ini");
// Setting up the SecurityManager...
org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject user = SecurityUtils.getSubject();
logger.info("User is authenticated: " + user.isAuthenticated());
UsernamePasswordToken token = new UsernamePasswordToken("bjangles", "dance");
user.login(token);
logger.info("User is authenticated: " + user.isAuthenticated());
}
}
用用户名和密码组合实例化UsernamePasswordToken
对象。 然后将令牌传递到Subject
类的login()
方法中。
再次运行该示例。 请注意,日志消息现在报告用户已通过身份验证。
为确保代码正常运行,并且没有误报,请更改代码或INI文件中的密码,然后再次运行示例。 现在, login()
方法将引发IncorrectCredentialsException
。 此异常将专门在您的生产代码中捕获,以便当用户提供了错误的密码时,您的应用程序可以轻松响应。
如果用户不正确,则login()
方法将引发UnknownAccountException
。 您可能要考虑处理此异常,但选择不向用户提供太多信息。 一种常见的做法是拒绝给用户任何有关用户名有效但密码不正确的指示。 如果某人试图通过猜测来获取访问权限,则您不想给该人暗示所猜测的用户名正确的提示。
LDAP是用于通过TCP / IP查询目录的协议。 这些目录可以保存有关用户的任何数量的信息,包括用户ID,联系信息和组成员资格等等。 LDAP目录对于公司地址簿很有用,并被广泛使用。
AD DS是用于用户和组管理的通用目录,它支持LDAP。 Shiro不包括通用的LDAP安全领域,但确实包括ActiveDirectoryRealm
对象,该对象使您可以根据LDAP对用户进行身份验证。 本示例使用在INI文件中配置的ActiveDirectoryRealm
对象对用户进行身份验证。 尽管AD DS与LDAP不同,但是本文使用的Shiro版本没有通用的LDAP对象。
与编写和运行示例本身相比,让LDAP服务器对示例进行测试可能要耗费大量精力。 如果您无权访问AD DS服务器,请考虑下载并安装Apache Directory,以提供示例LDAP服务器实现。 Apache Directory是用Java语言编写的。 同样,Apache Active Directory Studio是一个Eclipse插件,可让您浏览LDAP数据。 它还带有一些示例数据,这些示例数据为您提供了一种针对已知值编写代码的快速方法,而无需怀疑遇到的任何问题是代码问题还是数据问题。
清单5显示了用于认证存储在Apache Directory中的用户的代码。
// snipped...
public class ShiroLDAPTest {
private static Logger logger = LoggerFactory.getLogger(ShiroLDAPTest.class);
/**
* @param args
*/
public static void main(String[] args) {
// Using the IniSecurityManagerFactory, which will use the an INI file
// as the security file.
Factory factory =
new IniSecurityManagerFactory("actived.ini");
// Setting up the SecurityManager...
org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject user = SecurityUtils.getSubject();
logger.info("User is authenticated: " + user.isAuthenticated());
UsernamePasswordToken token =
new UsernamePasswordToken(
"cn=Cornelius Buckley,ou=people,o=sevenSeas", "argh");
user.login(token);
logger.info("User is authenticated: " + user.isAuthenticated());
}
}
除了INI文件的名称以及用户名和密码外,该代码与使用INI文件中的记录对用户进行身份验证的代码相同。 因为您可以使用INI文件配置Shiro,所以会产生相似性。 清单6显示了用于设置Shiro以针对Apache Directory进行身份验证的INI记录。
[main]
activeDirectoryRealm = org.apache.shiro.realm.activedirectory.ActiveDirectoryRealm
activeDirectoryRealm.systemUsername = uid=admin,ou=system
activeDirectoryRealm.systemPassword = secret
activeDirectoryRealm.searchBase = o=sevenSeas,ou=people
activeDirectoryRealm.url = ldap://localhost:10389
注意:我使用Apache Directory Studio将用户密码更改为可以放入测试代码中的值,以确保它可以正常工作。
您可以将Shiro应用于Web应用程序中的两种基本技术。 首先,您可以简单地使用API在基本servlet中合并此处提供的代码版本。 其次,您可以使用Shiro随附的HTTP过滤器。 本示例演示了第二种技术,因为使用过滤器可以利用内置的Web应用程序服务器技术和Shiro项目中的预编写代码。
本示例说明如何在Grails中使用过滤器。 Grails是一个项目,旨在使您可以使用基于约定的配置方法来尽快编写Groovy Web应用程序。 请参阅相关主题有关Grails的更多信息。
通常,您将手动为Shiro的过滤器添加必要的过滤器条目到web.xml文件中。 但是,Grails每次启动应用程序时都会生成web.xml文件,因此无需手动修改web.xml文件。
幸运的是,Grails支持可插入到web.xml生成过程中的插件,并让您参与编写web.xml文件中的条目。 Grails已经有许多插件可用,其中Shiro就有一个。 尝试使用Shiro Grails插件,该插件提供了一些新脚本,您可以运行它们来创建不同的领域和控制器。
另外,如果您喜欢添加条目并自己进行配置,则可以编写自己的插件。 使用Grails,编写新插件很容易。 要创建将必要的Shiro过滤器条目添加到web.xml文件的Grails插件,请使用以下命令开始:
> grails create-plugin ShiroWebXml
创建插件项目后,编辑ShiroWebXmlPlugin.groovy文件,并添加清单7中所示的代码。
class ShiroWebXmlPlugin {
// snipped plugin details...
def doWithWebDescriptor = { xml ->
def filterElement = xml.'filter'
def lastFilter = filterElement[filterElement.size() - 1]
lastFilter + {
'filter' {
'filter-name'("ShiroFilter")
'filter-class'("org.apache.shiro.web.servlet.IniShiroFilter")
'init-param' {
'param-name'("config")
'param-value'("\n#config")
}
}
}
def filterMappingElement = xml.'filter-mapping'
def lastFilterMappingElement =
filterMappingElement[filterMappingElement.size() - 1]
lastFilterMappingElement + {
'filter-mapping' {
'filter-name'("ShiroFilter")
'url-pattern'("/*")
}
}
}
}
该代码在Grails执行web.xml文件时运行。
在启动插件应用程序对其进行测试之前,请将您使用Ivy下载的Shiro JAR文件复制到插件的lib文件夹中。 在JAR文件就绪的情况下,使用以下命令测试插件是否正常工作:
grails run-app
如果插件应用程序成功启动,则可以将其打包以用于示例项目。 要打包该插件,请使用以下命令:
grails package-plugin
您可以使用以下命令安装新的ShiroWebXmlPlugin
:
cd myapp
grails install-plugin /path/to/shiro-web-xml-1.0.zip
如果收到UnavailableSecurityManagerException
,则说明尚未正确设置SecurityManager
。 在调用getSubject()
方法之前,请确保已在SecurityUtils
对象上对其进行了设置。
连接到LDAP服务器可能很困难。 如果收到javax.naming.CommunicationException
,请验证LDAP服务器的主机名和端口。 即使您不使用Apache Directory,Apache Directory Studio(可以单独安装)也可以帮助您解决连接问题和名称问题。
如果您没有使用Ant Ivy脚本初始化环境,则可能会遇到有关缺少类的错误。 即使没有Apache Commons Logging库( commons-logging
),INI文件示例也可以运行,但是运行LDAP示例会导致异常。 使用Apace Commons BeanUtils
解析INI文件并在对象上设置值。
Shiro是Apache Incubator中的一个框架,可让您向应用程序添加身份验证和授权。 它支持不同的身份验证存储,例如LDAP,Kerberos和AD DS。 Shiro的最小依赖关系及其相对易于配置的结合使其成为您应用程序中的安全框架的不错选择。
翻译自: https://www.ibm.com/developerworks/web/library/wa-apacheshiro/index.html