Apache Shiro简介
什么是 Apache Shiro
Apache Shiro 是一个强大并且灵活的开源的安全框架,它能很好的处理 authentication(认证), authorization(授权), enterprise session management(企业会话管理)和cryptography(密码加密)。
Apache Shiro最重要的目标就是易于使用和理解。安全有时候会非常的复杂,甚至是痛苦的,但是它并不一定如此的。一个框架屏蔽复杂性, 如有可能暴露一个干净,直观的API,简化了开发人员的努力,保障他们的应用程序的安全。
你可以使用Apache Shiro做以下的事情:
- 验证用户的身份信息
- 对用户执行访问控制,例如:
- 确定用户是否被分配一定的安全角色。
- 确定用户是否被允许做某事。
- 在任何环境下,使用Session API Use a Session API in any environment, 即使没有web或者EJB容器。
- 身份认证、访问控制或者会话的生命周期内时,对事件作出反应。
- 聚合一个或多个数据源的用户安全数据,使得这一切作为一个复合用户"视图"。
- 启用单点登录(SSO)功能。
- 使”记住我“服务关联,但没有登录
…
等,都集成到一个有凝聚力的易于使用的API。
Shiro视图所有应用环境中实现这些目标-从最简单额命令行应用程序到最大的企业应用程序,而无需强制其他第三方框架,容器或者应用程序的依赖。当然,带项目旨在尽可能的集成到这些环境,但它可以在任何环境中即装即用。
Apache Shiro 特性
Apache Shiro是一个具有很多特性的全面的应用程序安全框架。下图展示了Shiro的重点,而本参考手册也是这样组织的:
Shiro的目标是Shiro开发团队所说的“四大基石的应用程序安全性”-Authentication, Authorization, Session Management,和Cryptography:
-
Authentication:有时被称为“登录”,这是证明一个用户的行为,也就是常说的他是谁?
-
Authorization:访问控制的过程,即确定谁有权访问“什么”。
-
Session Management:特定于用户的会话管理,甚至在非web或者EJB应用程序。
-
Cryptography:保持数据安全使用加密算法,同时仍然容易使用。
还有一些额外的功能,以支持和加强这些问题在不同的应用环境,特别是:
- Web Support: Shiro web支持API帮助轻松安全的web应用程序。
- Caching: 在Apache Shiro API中缓存是一线公民,以确保安全操作保持快速和高效。
- Concurrency: Apache Shiro 支持多线程应用程序。
- Testing: 测试支持的存在是为了帮助你编写单元测试和集成测试,并确保你的代码将被抵押的预期。
- “Run As”: 使用户能够承担另一个用户(如果他们被允许),在管理方案有时很有用的身份。
- “Remember Me”: 在整个会话中记住用户的身份,他们只需要在强制时进行登录。
Apache Shiro术语
请你话两分钟的时间阅读和理解这一点-它是非常重要的。这些术语贯穿整个文档,它会大大简化你对Shiro和安全的认识。
因为所使用的术语安全可能令人困惑。我们会让生活更加轻松在澄清这些核心概念后,你会看到Shiro API如何很好的反应它们:
-
Authentication
身份验证是一个主体的身份的过程-基本上证明有人真的是他们说他们是谁。当验证尝试成功的应用程序可以相信,受保证是谁应用程序期望。 -
Authorization
授权,也被称为访问控制,就是如果一个用户/主题是否被允许做某件事它通常是通过检查和解释主体的角色和权限(见下文),然后允许或拒绝访问请求的资源或功能来实现的。 -
Cipher
密码是一种算法来执行加密或解密。该算法一般依赖于一条信息称为密钥。以及根据所述密钥加密而变化,以便decyrption是没有它非常困难。
密码有不同的变化。分组密码工作通常是一个固定大小的符号块而流密码工作符号的连续流。对称密码使用相同的密钥进行加密和解密,同时非对称加密算法使用不同的密钥。和如果在非对称密码的密钥不能从其他派生,则一个可以共享公开创建的公钥/私钥对。
- Credential
凭证是一个资料片,用于验证的用户/主体的身份。一个(或多个)凭证在验证尝试来验证用户/主体提交他们实际上是关联的用户以及主要负责人递交。凭证通常是非常隐秘的事,只有特定用户/科目都知道,如密码或PGP密钥或生物特征属性或类似的机制。
我们的想法是,对于一个校长,只有一个人会知道正确的凭据’对’与校长。如果当前用户/主体提供正确的凭据匹配一个储存在系统中,然后系统可以假设,并相信当前用户/主体是真的,他们说他们是谁。信任程度增加了更多的安全凭证类型(例如生物统计签名>密码)。
-
Cryptography
加密技术是保护信息免受不需要的访问通过隐藏它,或将其转换成废话的做法,让知道的人能够读取它。Shiro专注于密码学的两大核心要素:即加密使用公钥或私钥,如电子邮件数据加密和散列(也称为消息摘要)不可逆加密密码等数据。 -
Hash
散列函数是一个输入源的单向的,不可逆的转换,有时也被称为消息时,成编码的哈希值,有时也被称为消息摘要。它经常被用于密码,数字指纹,或与底层字节数组数据。 -
Permission
一个权限,至少Shiro解释它,是描述一个应用程序并没有什么更多的原始功能表。权限是在安全政策中的最低级别的结构。他们只定义了“什么”的应用程序可以做的。他们并没有描述“谁”能够执行的操作。权限是行为只是一个声明,仅此而已。
许可的例子:
-
- 打开文件
- 查看’/user/list’网页
- 打印文档
- 删除用户’jsmith’
-
Principal
Principal是一个应用程序的用户(主题)的任一标识属性。一个“标识属性”可以是任何有意义的应用程序 - 用户名,姓,一个给定的名字,社会安全号码,用户ID等,这就是它 - 没有什么疯狂。
Shiro 还引用我们称之为主题的主要的 principal 的东西。 A主要 principal 是任何能够唯一标识整个应用程序中的主要主体。理想的主要 principals 是比如用户名或用户ID是一个关系型数据库用户表的主键。有一个在应用程序的用户(主题)只能有一个主要的 principal 。
- Realm
领域是可以访问的应用程序特定的安全数据,如用户,角色和权限的组件。它可以被看作是一个安全的DAO(数据访问对象)。该领域将这种应用程序特定的数据转换成Shiro明白这样 Shiro 可以反过来提供一个单一的,易于理解的主题编程API,无论来源如何许多数据存在或如何应用程序特定的数据可能是一个格式。
领域通常具有1对1的关系与数据源如关系数据库,LDAP目录,文件系统,或其他类似的资源。因此,该领域的接口使用的数据源特定的API的实现来发现授权数据(角色,权限等),如JDBC,文件IO,Hibernate或者JPA,或任何其他数据访问API。
- Role
角色的定义可以根据你要向谁倾诉而有所不同。在许多应用中它是最好的,人们用它来隐含地定义安全策略模糊的概念。 Shiro 愿意将之解释角色的权限的一个简单的命名集合。这就是它 - 一个应用程序唯一的名称,聚集一个或多个许可声明。
这是比隐式1所使用的许多应用中的更具体的定义。如果您选择让您的数据模型反映 Shiro 的假设,你会发现你将有更多的权力控制的安全策略。
- Session
一个Session是一个单用户/科目谁与软件系统在一段时间交互相关的状态数据上下文。数据可以添加/读/从会话中删除,而受使用应用程序和应用程序稍后可以使用此数据在必要时。会话终止用户/主题注销申请的,或当它超时由于不活动的时候。
对于那些熟悉的HttpSession,一个 Shiro 会话有异曲同工之妙,除了 Shiro 会话可以在即使没有任何Servlet容器或EJB容器提供的任何环境中使用。
- Subject
一个主题就是看中安全性的术语,基本上意味着一个应用程序用户的特定安全“视图”。一个Subject并不总是需要反映一个人虽然 - 它可以代表一个外部进程调用您的应用程序,或者说在一段时间(如一个cron作业)执行间歇性的东西一个守护进程系统帐户。它基本上是做一些与应用程序的任何实体的表示。
帮助改进文档
虽然我们希望这个文档可以帮助你和你正在做的与Apache Shiro 工作,社区改善和扩大文档所有的时间。如果你想帮助Shiro项目,请考虑纠正,扩大,或者增加在那里你看到需要的文件。帮助一点一滴您提供扩展的社区,从而提高Shiro。
贡献你的文档最简单的方法是将其发送到用户论坛或用户邮件列表。
Apache Shiro的十分钟教程
介绍
欢迎来到Apache Shiro十分钟教程!
经过这个快速和简单的教程,你应该充分了解开发人员如何在他们的应用程序中使用Shiro。你应该能够在10分钟内做到这一点的。
综述
什么是 Apache Shiro
Apache Shiro是一个功能强大且易于使用的Java安全框架,为开发人员提供了一个直观而全面的解决方案,认证,授权,加密和会话管理。
在实际应用中,它实现了以管理你的应用程序的各个方面的安全性,同时保持尽可能多的方式。它是建立在完善的接口驱动的设计和面向对象的原则,你可以想像它所到之处都会启用自定义行为。但对一切合理的默认值,“放手”让应用程序安全去做。至少,这是我们追求的。
Apache Shiro能做什么?
很多[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HTra104p-1651232933181)(https://cwiki.apache.org/confluence/images/icons/emoticons/smile.gif)].。但是我们不想膨胀快速入门。请查看我们的功能页面,如果你想看看它能为你做什么。另外,如果你好奇我们如何开始,我们为什么存在,请参阅Shiro历史使命页。
好啊。现在让我们做一些实际的!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WL88dxvk-1651232933182)(https://cwiki.apache.org/confluence/images/icons/emoticons/information.gif)]
Shiro 可以在任何环境下运行,从最简单的命令行应用程序,以最大的企业网络和集群应用,但我们将使用一个简单的main方法,最简单的例子这个快速入门,让你可以感受的API。
下载
-
确保你已经安装了JDK1.5+和Maven2.2+。
-
下载最新的“源代码”从下载页面。这个例子中,我们使用1.1.0版本分布。
-
解压缩源包:
> unzip shiro-root-1.2.0-source-release.zip
-
进入快速入门目录:
> cd shiro-root-1.2.0/samples/quickstart
-
运行快速入门:
> mvn compile exec:java
这一目标将刚刚打印出一些日志信息,让你知道是怎么回事,然后退出。在阅读本快速入门,可以自由地先看下samples/quickstart/src/main/java/Quickstart.java.找到源代码。更改该文件并运行上述命令
mvn compile exec:java
像你往常一样控制
Quickstart.java
上面提到的Quickstart.java
文件中包含的所有,将让你熟悉API的代码。现在让我们把它分解成块在这里,所以你可以很容易地理解正在发生的事情。
在几乎所有的环境,你可以获得当前正在执行的用户通过以下调用:
Subject currentUser = SecurityUtils.getSubject();
使用[SecurityUtils](http://shiro.apache.org/static/current/apidocs/org/apache/shiro/SecurityUtils.html).[getSubject()](http://shiro.apache.org/static/current/apidocs/org/apache/shiro/SecurityUtils.html#getSubject%28%29)
, 我们可以获得当前执行的[Subject](http://shiro.apache.org/static/current/apidocs/org/apache/shiro/subject/Subject.html)
. 一个Subject仅仅是一个程序用户的特定的安全“视图”。我们其实是想叫它’用户’自认为“很有道理”,但我们还是决定反对:太多的应用程序具有现有的API已经拥有自己的用户类/框架,我们不想与那些冲突。此外,在安全领域,这个词主旨实际上是公认的术语。好吧,在移动…
在一个独立的应用程序中getSubject()
调用可能会返回基于应用程序中的特定位置的用户数据的Sbject
,并在服务器环境中(例如web应用程序),它获取基于当前线程或传入请求关联的用户数据的Sbject
。
现在,你有一个Subject,你能做些什么呢?
如果你想提供给用户当前会话与应用程序中的事情,你可以得到他们的会话:
Session session = currentUser.getSession();
session.setAttribute( “someKey”, “aValue” );
Session
是Shiro特定的实例提供了大部分你已经习惯了与常规的HttpSessions,但也有一些额外的好东西,一个很大的不同:它并不需要一个HTTP环境!
如果部署一个web应用程序中,默认的HttpSession
基于Session
。但是,在非网络环境中,像这样简单的快速入门,Shiro会自动使用其企业会话管理的默认。这意味着您可以使用相同的API在应用程序中,在任何级别,无论部署环境。这将打开一个全新的世界的应用程序,因为任何需要的会话应用程序并不需要被迫使用HttpSession中或EJB有状态会话Bean 。而且,任何客户端技术现在可以共享会话数据。
所以,现在你可以获取一个Subject
和他们的Session。那么像检查,如果他们被允许做的事情,例如检查对角色和权限的真正有用的东西?
好了,我们只能做那些检查已知用户。我们上面的Subject
实例表示当前用户,但谁是当前用户?嗯,他们是匿名的 - 也就是说,直到他们至少登录一次。所以,让我们这样做:
if ( !currentUser.isAuthenticated() ) {
//collect user principals and credentials in a gui specific manner
//such as username/password html form, X509 certificate, OpenID, etc.
//We’ll use the username/password example here since it is the most common.
//(do you know what movie this is from 😉
UsernamePasswordToken token = new UsernamePasswordToken(“lonestarr”, “vespa”);
//this is all you have to do to support ‘remember me’ (no config - built in!):
token.setRememberMe(true);
currentUser.login(token);
}
就是这样!这再简单不过了。
但是,如果他们的登录尝试失败?你可以捕捉各种各样的具体的异常,告诉你到底发生了什么事并允许您处理:
try {
currentUser.login( token );
//if no exception, that’s it, we’re done!
} catch ( UnknownAccountException uae ) {
//username wasn’t in the system, show them an error message
} catch ( IncorrectCredentialsException ice ) {
//password didn’t match, try again
} catch ( LockedAccountException lae ) {
//account for that username is locked - can’t login. Show them a message
}
… more types exceptions to check if you want …
} catch ( AuthenticationException ae ) {
//unexpected condition - error
}
有许多不同类型的异常,您可以检查,或抛出自己的自定义条件Shiro可能不占。见AuthenticationException的JavaDoc更多。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2B8brAv9-1651232933183)(https://cwiki.apache.org/confluence/images/icons/emoticons/check.gif)]
方便的提示
最安全的做法是给通用登录失败信息给用户,因为你不想帮助攻击者试图闯入你的系统。
好了,现在,我们有一个登录的用户。我们可以怎么做?
比方说,他们是谁:
//print their identifying principal (in this case, a username):
log.info( “User [” + currentUser.getPrincipal() + “] logged in successfully.” );
我们也可以测试一下,看看他们是否有特定的角色或不:
if ( currentUser.hasRole( “schwartz” ) ) {
log.info(“May the Schwartz be with you!” );
} else {
log.info( “Hello, mere mortal.” );
}
我们也可以看到,如果他们有一个权限作用于一个特定类型的实体:
if ( currentUser.isPermitted( “lightsaber:weild” ) ) {
log.info(“You may use a lightsaber ring. Use it wisely.”);
} else {
log.info(“Sorry, lightsaber rings are for schwartz masters only.”);
}
另外,我们可以进行一个非常强大的实例级别的权限检查 - 看看用户是否有访问类型的特定实例的能力的能力:
if ( currentUser.isPermitted( “winnebago:drive:eagle5” ) ) {
log.info("You are permitted to ‘drive’ the ‘winnebago’ with license plate (id) ‘eagle5’. " +
“Here are the keys - have fun!”);
} else {
log.info(“Sorry, you aren’t allowed to drive the ‘eagle5’ winnebago!”);
}
小意思,对不对?
最后,使用应用程序完成用户的时候,他们可以登出:
currentUser.logout(); //removes all identifying information and invalidates their session too.
嗯,这是核心,使用Apache Shiro在应用程序开发者的水平。虽然有一些相当复杂的东西引擎盖下怎么回事,使这项工作如此优雅,这是真正的所有有给它。
但你可能会问自己, “但是,谁负责的登录信息(用户名和密码,角色和权限等)过程中获得的用户数据,以及究竟是谁在运行时执行的安全检查? ”好了,你这样做,通过实施什么Shiro称之为Realm和Realm
的境界到Shiro的配置。
但是,你如何配置一个境界在很大程度上取决于你的运行环境。例如,如果你运行一个独立的应用程序,或者如果你有一个基于Web的应用程序,或者Spring或JEE容器中的应用程序,或它们的组合。这类型的配置是这样的快速入门的范围之内,因为它的目的是让你熟悉的API和Shiro的概念。
当你准备好跳多一点细节,你一定要阅读的验证指南和授权指南。然后可以移动到其他文件,特别是在参考手册中,回答任何其他问题。您也可能会想要加入的用户邮件列表 - 你会发现,我们有一个伟大的社会与人愿意帮助尽可能。
·感谢以下相处。我们希望您享受使用Apache Shiro!
你的第一个Apache Shiro应用程序
如果你是新来的Apache Shiro,这个简短的教程将向你展示如何建立一个初始的和非常简单的应用Apache Shiro担保。我们将讨论Shiro的核心概念来帮助您熟悉 Shiro 的设计和API。
如果你不想真正为你跟着这个教程编辑文件,你可以获得一个几乎相同的示例应用程序和参考它作为你去。选择一个位置:
- 在Apache中Shiro的Subversion版本库:https://svn.apache.org/repos/asf/shiro/trunk/samples/quickstart/
- 在Apache中Shiro的源代码分发的
samples/quickstart
目录. 源分布可以从下载页面Download.
Setup
在这个简单的例子中,我们将创建一个非常简单的命令行应用程序将运行并迅速退出,只是让你可以感受一下Shiro的API。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H4IyAw3Y-1651232933184)(https://cwiki.apache.org/confluence/images/icons/emoticons/information.gif)]
任何应用
Apache Shiro是从一开始就设计为支持任何应用程序 - 从最小的命令行应用程序,以最大的集群Web应用程序。尽管我们正在创建本教程简单的应用程序,要知道,同样的使用模式应用无论您的应用程序是如何创建的,或在那里部署。
本教程需要Java1.5或更高版本。我们也将使用Apache Maven作为我们构建工具,但当然使用Apache Shiro这不是必须的。您可能会获得Shiro jars,并将它们在任何你喜欢的方式进入您的应用程序,例如可能使用Apache的Ant和Ivy。
对于本教程,请确保您正在使用Maven2.2.1或更高版本。在命令提示符输入mvn --version,并看到类似下面的内容:
测试Maven安装
hazlewood:~/shiro-tutorial$ mvn --version
Apache Maven 2.2.1 (r801777; 2009-08-06 12:16:01-0700)
Java version: 1.6.0_24
Java home: /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
Default locale: en_US, platform encoding: MacRoman
OS name: “mac os x” version: “10.6.7” arch: “x86_64” Family: “mac”
现在,创建你的文件系统一个新的目录,例如,shiro-tutorial
和保存以下Mavenpom.xml
文件在该目录中:
pom.xml
<xml version=“1.0” encoding=“UTF-8”>
<project xmlns=“http://maven.apache.org/POM/4.0.0”
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd”>
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.shiro.tutorials</groupId>
<artifactId>shiro-tutorial</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>First Apache Shiro Application</name>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<!-- This plugin is only to test run our little application. It is not
needed in most Shiro-enabled applications: -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<classpathScope>test</classpathScope>
<mainClass>Tutorial</mainClass>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.1.0</version>
</dependency>
<!-- Shiro uses SLF4J for logging. We'll use the 'simple' binding
in this example app. See http://www.slf4j.org for more info. -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.6.1</version>
<scope>test</scope>
</dependency>
</dependencies>
本教程类
我们将运行一个简单的命令行应用程序,所以我们需要有一个public static void main(String[] args)
方法创建一个Java类。
在包含您的pom.xml
文件相同的目录下,创建一个*src/main/java
的子目录。在src/main/java
中创建具有以下内容的Tutorial.java
文件:
src/main/java/Tutorial.java
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Tutorial {
private static final transient Logger log = LoggerFactory.getLogger(Tutorial.class);
public static void main(String\[\] args) {
log.info("My First Apache Shiro Application");
System.exit(0);
}
}
不要担心import语句现在 - 我们将得到他们不久。但现在,我们已经有了一个典型的命令行程序“外壳”。所有这一计划将做的是打印出的文字“My First Apache Shiro Application”,然后退出。
试运行
尝试我们的教程应用程序,运行在您的教程项目的根路径(如shiro-tutorial
)下面的一个命令提示符,然后键入以下内容:
mvn compile exec:java
你会看到我们的小教程“应用程序“的运行和退出。你应该会看到类似下面的内容(注意粗体文字,表示我们的输出):
运行应用程序
lhazlewood:~/projects/shiro-tutorial$ mvn compile exec:java
... a bunch of Maven output ...
1 [Tutorial.main()] INFO Tutorial - My First Apache Shiro Application
lhazlewood:~/projects/shiro-tutorial$
我们已经验证的应用程序运行成功 - 现在让我们使用Apache Shiro。随着我们继续学习本教程,你可以运行mvn compile exec:java
:每次之后的java我们添加一些更多的代码,看看我们的变化的结果。
使用Shiro
在使Shiro在应用程序了解的第一件事情是,几乎一切都在Shiro是关系到所谓的安全管理器的核心/核心组成部分。对于那些熟悉Java的安全性,这是一个SecurityManager
的Shiro的概念 - 它不是一回事java.lang.SecurityManager
。
虽然我们将介绍Shiro的设计,详细的Architecture一章,它是现在不够好,知道在ShiroSecurityManager
是一个应用程序的Shiro环境的核心和SecurityManager必须存在于每个应用程序。因此,在我们的教程应用程序中我们必须做的第一件事是建立SecurityManager
实例。
配置
虽然我们可以直接实例化一个SecurityManager
类,Shiro的SecurityManager
的实现有足够的配置选项和内部组件,使这个痛苦做Java源代码 - 这将是更容易的配置安全管理器具有灵活的基于文本的配置格式。
为此,Shiro提供了通过基于文本的INI配置一个默认的“公分母”的解决方案。人们很厌倦了使用笨重的XML文件,这些天,INI易于阅读,易于使用,并且需要很少的依赖关系。您还可以在后面看到,随着对象图导航一个简单的了解,INI可有效地用于配置简单的对象图像的SecurityManager。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XZgX9JDp-1651232933185)(https://cwiki.apache.org/confluence/images/icons/emoticons/check.gif)]
许多配置选项
Shiro的SecurityManager
的实现和所有支持组件都是兼容所有JavaBeans。这允许Shiro与几乎任何结构格式,例如XML(Spring,JBoss中,Guice等),YAML,JSON Groovy的生成器的标记进行配置。 INI只是Shiro的“公分母”格式,允许在其他情况下,选择任何环境配置不可用。
shiro.ini
所以在这个简单的应用程序我们将使用一个INI文件来配置ShiroSecurityManager
。首先,创建起在那里的pom.xml
的是同一目录下的src/main/resources
目录。然后用下面的内容是新的目录中创建一个shiro.ini
文件:
src/main/resources/shiro.ini
# =============================================================================
Tutorial INI configuration
Usernames/passwords are based on the classic Mel Brooks’ film “Spaceballs” 😃
=============================================================================
-----------------------------------------------------------------------------
Users and their (optional) assigned roles
username = password, role1, role2, …, roleN
-----------------------------------------------------------------------------
[users]
root = secret, admin
guest = guest, guest
presidentskroob = 12345, president
darkhelmet = ludicrousspeed, darklord, schwartz
lonestarr = vespa, goodguy, schwartz
-----------------------------------------------------------------------------
Roles with assigned permissions
roleName = perm1, perm2, …, permN
-----------------------------------------------------------------------------
[roles]
admin = *
schwartz = lightsaber:*
goodguy = winnebago:drive:eagle5
正如你看到的,这个配置基本上建立了一个小套的静态用户帐户,对于我们的第一个应用程序已经够用了。在后面的章节中,你会看到我们如何使用更复杂的用户数据源,如关系数据库,LDAP的ActiveDirectory,等等。
引用配置
现在我们已经定义了一个INI文件,我们可以在我们的教程应用程序类创建SecurityManager
实例。改变的main
方法,以反映以下更新:
public static void main(String[] args) {
log.info("My First Apache Shiro Application");
//1.
Factory<SecurityManager\> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//2.
SecurityManager securityManager = factory.getInstance();
//3.
SecurityUtils.setSecurityManager(securityManager);
System.exit(0);
}
而且我们去 - Shiro在我们的示例应用程序中启用只增加3行代码!多么容易呢?
随意mvn compile exec:java和看到的一切仍然运行成功(由于调试或更低的Shiro的默认日志,你将不会看到任何Shiro日志消息 - 如果它启动和运行都没有错误,那么你就知道一切都是还行)。
下面是上面的添加是这样做的:
-
我们使用Shiro的
IniSecurityManagerFactory
实施摄取我们这是设在classpath的根shiro.ini文件。此实施反映Shiro的支持工厂方法设计模式。在classpath:前缀是一种资源的指标,告诉Shiro从哪里加载ini文件(其他前缀,如url:
和file
:支持以及)。 -
factory.getInstance()
方法被调用,它解析INI文件,并返回了SecurityManager
实例反映的配置。 -
在这个简单的例子中,我们设置了
SecurityManager
是一个静态的(内存)单例,跨JVM的访问。但是请注意,如果你将永远有一个以上的Shiro功能的应用程序在单个JVM,这是不合乎要求的。对于这个简单的例子,它是确定的,但更复杂的应用环境中通常会放置在SecurityManager
的应用程序特定的内存(如在一个Web应用程序的ServletContext
还是Spring,Guice或JBoss DI容器实例)。
使用Shiro
现在,我们的SecurityManager准备就绪,现在我们可以开始做我们真正关心的东西 - 执行安全操作。
当确保我们的应用程序,可能我们问自己最相关的问题是“谁是当前用户?”或“当前用户被允许做X”?这是常见的问这些问题,因为我们正在编写代码或设计用户界面:应用程序通常建立基于用户的故事,你要根据每个用户的基础功能代表(并固定)。所以,对于我们思考的安全在我们的应用中最自然的方式是基于当前用户。 Shiro的API,从根本上代表着“当前用户”,其主题理念的概念。
几乎在所有的环境中,你可以通过下面的调用获取当前正在执行的用户:
Subject currentUser = SecurityUtils.getSubject();
使用SecurityUtils.getSubject(),我们可以得到当前正在执行的[Subject](http://shiro.apache.org/static/current/apidocs/org/apache/shiro/subject/Subject.html)
。Subject是一个安全术语,基本意思是“当前正在执行的用户的安全专用视图” 。这不叫’User’,因为这个词’User’通常与一个人有关。在安全领域,术语“Subject”可以指一个人,也是一个第三方程序,定时任务,守护进程帐户,或类似的东西。它只是意味着“当前与该软件进行交互的东西” 。对于大多数的意图和目的,虽然,你能想到的Subject
为Shiro的“User”的概念。
在一个独立的应用程序的getSubject()
调用可能会返回基于应用程序中的特定位置的用户数据的主题,并在服务器环境中(例如web应用程序) ,它获取基于当前线程或传入请求关联的用户数据的主题。
现在,你有一个Subject,你可以做什么呢?
如果你想提供给用户当前会话与应用程序中的事情,你可以得到他们的会话:
Session session = currentUser.getSession();
session.setAttribute( “someKey”, “aValue” );
Session
是一个Shiro特定实例提供了大部分你已经习惯了与常规的HttpSessions,但也有一些额外的好东西,一个很大的不同:它并不需要一个HTTP环境!
如果部署一个web应用程序中,默认的HttpSession
基于Session
。但是,在非网络环境中,像这样简单的教程应用程序,Shiro会自动使用其企业会话管理的默认。这意味着您可以使用相同的API在应用程序中,在任何级别,无论部署环境!这将打开一个全新的世界的应用程序,因为任何需要的会话应用程序并不需要被迫使用HttpSession中或EJB有状态会话Bean 。而且,任何客户端技术现在可以共享会话数据。
所以,现在你可以获取一个Subject
和他们的Session。那么像检查,如果他们被允许做的事情,例如检查对角色和权限的真正有用的东西?
好了,我们只能做那些检查已知用户。我们上面的Subject
实例表示当前用户,但谁是当前用户?嗯,他们是匿名的 - 也就是说,直到他们至少登录一次。所以,让我们这样做:
if ( !currentUser.isAuthenticated() ) {
//collect user principals and credentials in a gui specific manner
//such as username/password html form, X509 certificate, OpenID, etc.
//We’ll use the username/password example here since it is the most common.
UsernamePasswordToken token = new UsernamePasswordToken(“lonestarr”, “vespa”);
//this is all you have to do to support 'remember me' (no config - built in!):
token.setRememberMe(true);
currentUser.login(token);
}
就是这样!这再简单不过了。
但是,如果他们的登录尝试失败?你可以捕捉各种各样的具体的异常,告诉你到底发生了什么事并允许您处理:
try {
currentUser.login( token );
//if no exception, that’s it, we’re done!
} catch ( UnknownAccountException uae ) {
//username wasn’t in the system, show them an error message
} catch ( IncorrectCredentialsException ice ) {
//password didn’t match, try again
} catch ( LockedAccountException lae ) {
//account for that username is locked - can’t login. Show them a message
}
… more types exceptions to check if you want …
} catch ( AuthenticationException ae ) {
//unexpected condition - error
}
有许多不同类型的异常,您可以检查,或抛出自己的自定义条件Shiro可能不占。见AuthenticationException的JavaDoc更多。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7I8xAev9-1651232933186)(https://cwiki.apache.org/confluence/images/icons/emoticons/check.gif)]
方便的提示
最安全的做法是给通用登录失败信息给用户,因为你不想帮助攻击者试图闯入你的系统。
好了,现在,我们有一个登录的用户。我们可以怎么做?
比方说,他们是谁:
//print their identifying principal (in this case, a username):
log.info( “User [” + currentUser.getPrincipal() + “] logged in successfully.” );
我们也可以测试一下,看看他们是否有特定的角色或不:
if ( currentUser.hasRole( “schwartz” ) ) {
log.info(“May the Schwartz be with you!” );
} else {
log.info( “Hello, mere mortal.” );
}
我们也可以看到,如果他们有一个权限作用于一个特定类型的实体:
if ( currentUser.isPermitted( “lightsaber:weild” ) ) {
log.info(“You may use a lightsaber ring. Use it wisely.”);
} else {
log.info(“Sorry, lightsaber rings are for schwartz masters only.”);
}
另外,我们可以进行一个非常强大的实例级别的权限检查 - 看看用户是否有访问类型的特定实例的能力:
if ( currentUser.isPermitted( “winnebago:drive:eagle5” ) ) {
log.info("You are permitted to ‘drive’ the ‘winnebago’ with license plate (id) ‘eagle5’. " +
“Here are the keys - have fun!”);
} else {
log.info(“Sorry, you aren’t allowed to drive the ‘eagle5’ winnebago!”);
}
小意思,对不对?
最后,使用应用程序完成用户的时候,他们可以登出:
currentUser.logout(); //removes all identifying information and invalidates their session too.
最后的教程类
在上面的代码示例中添加后,这里是我们最终的教程类文件。随意编辑和播放它并更改安全检查(和INI配置),只要你喜欢:
Final src/main/java/Tutorial.java
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Tutorial {
private static final transient Logger log = LoggerFactory.getLogger(Tutorial.class);
public static void main(String\[\] args) {
log.info("My First Apache Shiro Application");
Factory<SecurityManager\> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
// get the currently executing user:
Subject currentUser = SecurityUtils.getSubject();
// Do some stuff with a Session (no need for a web or EJB container!!!)
Session session = currentUser.getSession();
session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");
if (value.equals("aValue")) {
log.info("Retrieved the correct value! \[" + value + "\]");
}
// let's login the current user so we can check against roles and permissions:
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
token.setRememberMe(true);
try {
currentUser.login(token);
} catch (UnknownAccountException uae) {
log.info("There is no user with username of " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {
log.info("The account for username " + token.getPrincipal() + " is locked. " +
"Please contact your administrator to unlock it.");
}
// ... catch more exceptions here (maybe custom ones specific to your application
catch (AuthenticationException ae) {
//unexpected condition error
}
}
//say who they are:
//print their identifying principal (in this case, a username):
log.info("User \[" + currentUser.getPrincipal() + "\] logged in successfully.");
//test a role:
if (currentUser.hasRole("schwartz")) {
log.info("May the Schwartz be with you!");
} else {
log.info("Hello, mere mortal.");
}
//test a typed permission (not instance-level)
if (currentUser.isPermitted("lightsaber:weild")) {
log.info("You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
//a (very powerful) Instance Level permission:
if (currentUser.isPermitted("winnebago:drive:eagle5")) {
log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +
"Here are the keys - have fun!");
} else {
log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
}
//all done - log out!
currentUser.logout();
System.exit(0);
}
}
概要
希望本次的介绍教程帮助您了解如何建立Shiro在一个基本的应用程序,以及Shiro的主要设计概念,Subject
和SecurityManager。
但是,这是一个相当简单的应用程序。您可能会问自己,“我不希望使用INI用户帐户,而是要连接到一个更复杂的用户数据源,如果有什么?”
要回答这个问题,需要Shiro的架构有点更深入的了解和支持配置机制。我们会在下面Shiro的Architecture。
Apache Shiro 架构
Apache Shiro的设计目标是由是直观和易于使用,以简化应用程序的安全性。 Shiro的核心设计模型大多数人是如何看待的应用程序的安全性 - 在某人(或某物)的上下文进行交互与应用程序。
软件应用程序通常是基于用户故事设计。也就是说,你经常会设计用户界面或服务的API基于用户如何将(或应该)与软件进行交互。例如,你可能会说, “如果我的应用程序交互的用户登录时,我会告诉他们一个按钮就可以点击查看他们的帐户信息,如果他们还没有登录,我将呈现的注册按钮。 “
这个例子声明表示应用程序在很大程度上写入,以满足用户的要求和需要。即使’用户’是另一种软件系统,而不是一个人,你还是写代码,以反映根据谁(或什么)的行为目前正在与软件进行交互。
Shiro体现在自己的设计这些概念。通过匹配什么已经是直观的面向软件开发人员,Apache Shiro仍然直观,易于使用的几乎任何应用程序。
高级概述
在最高级别的概念,Shiro的建筑有3个主要概念:Subject,SecurityManager
和Realms。下图是这些组件如何交互的高级概述,我们将介绍以下每一个概念:
-
Subject:正如我们在教程所提到的,
Subject
基本上是当前正在执行的用户的安全性特定的“视图”。而所说的“User”往往意味着一个人,一个Subject
可以是一个人,但它也可能是一个第三方服务,守护进程帐户,定时作业,或任何类似的 - 基本上任何目前正与该软件进行交互。Subject
实例都绑定到了(而且必须)SecurityManager。当你与一个Subject进行交互,这些交互转化为特定的subject交互通过SecurityManager。 -
SecurityManager:SecurityManager是Shiro的架构的心脏,并作为一种’保护伞’对象的坐标,它们一起构成一个对象图其内部的安全组件。但是,一旦安全管理器及其内部对象图被配置为一个应用程序,它通常单独留在家中和应用程序开发人员花费了几乎所有的时间与SubjectAPI。
我们将讨论SecurityManager在后面详细,但要认识到,当你与一个Subject互动,实在是SecurityManager背后做所有繁重的任何Subject的安全操作的场景是很重要的。这反映在上面的基本流程图。
- Realms: Realms充当“桥梁”或Shiro和应用程序的安全性数据之间的“连接器” 。当谈到时间,实际上是与像用户帐户的安全相关的数据交互,以执行身份验证(登录)和授权(访问控制) ,Shiro查找这些东西很多从配置为应用程序的一个或多个国度。
在这个意义上的境界本质上是一个安全的DAO :它封装了连接详细信息的数据源,并根据需要做相关的数据提供给 Shiro 。在配置Shiro,你必须至少指定一个境界用于身份验证和/或授权。该安全管理器可以与多个境界构造,但至少有一个是必需的。
Shiro提供开箱即装即用的国度连接到一些安全的数据源(又名目录),如LDAP ,关系数据库( JDBC ) ,文本配置源像INI和属性文件,等等。你可以插入你自己的境界的实现来表示自定义数据源,如果默认的国度不符合您的需求。
像其他内部组件, Shiro
SecurityManager
如何管理三界用于收购安全和身份数据被表示为主题的实例。
详细的结构
下图显示了Shiro的核心架构概念之后每个简短的总结:
-
Subject(
[org.apache.shiro.subject.Subject](http://shiro.apache.org/static/current/apidocs/org/apache/shiro/subject/Subject.html)
)
A security-specific ‘view’ of the entity (user, 3rd-party service, cron job, etc) currently interacting with the software.
实体(用户,第三方服务,定时作业等)的特定安全“视图”与软件进行交互。 -
SecurityManager(
[org.apache.shiro.mgt.SecurityManager](http://shiro.apache.org/static/current/apidocs/org/apache/shiro/mgt/SecurityManager.html)
)
正如上面提到的,SecurityManager
是Shiro的架构的心脏。它主要是一个’保护伞’的对象,协调其管理组件,以确保他们的工作顺利在一起。它还管理每个应用程序用户的Shiro view、,所以它知道如何执行每个用户的安全操作。 -
Authenticator(
[org.apache.shiro.authc.Authenticator](http://shiro.apache.org/static/current/apidocs/org/apache/shiro/authc/Authenticator.html)
)
认证者(Authenticator
)是负责对用户执行和响应认证(注册)尝试的成分。当一个用户试图登录中,这种逻辑是由验证器执行。认证者知道如何与存储相关用户/帐户信息的一个或多个国度协调。从这些领域获得的数据是用来验证用户的身份,以保证用户确实是他们说他们是谁。- Authentication Strategy(
[org.apache.shiro.authc.pam.AuthenticationStrategy](http://shiro.apache.org/static/current/apidocs/org/apache/shiro/authc/pam/AuthenticationStrategy.html)
)
如果不止一个Realm
被配置时,AuthenticationStrategy
将协调Realms,以确定下一个认证尝试成功或失败的条件(例如,如果一个领域成功,但另一些则不能,是尝试成功?必须各个领域的成功吗?只有第一个?)。
- Authentication Strategy(
-
Authorizer(
[org.apache.shiro.authz.Authorizer](http://shiro.apache.org/static/current/apidocs/org/apache/shiro/authz/Authorizer.html)
)
该认证器是在应用程序中的组件负责确定用户的访问控制。这是最终说,如果一个用户被允许做某件事,或者没有的机制。像验证器时,授权人也知道如何与多个后端数据源,以协调访问角色和权限信息。该授权人使用此信息来确定到底是否允许用户执行一个给定的操作。 -
SessionManager(
[org.apache.shiro.session.mgt.SessionManager](http://shiro.apache.org/static/current/apidocs/org/apache/shiro/session/mgt/SessionManager.html)
)
SessionManager
知道如何创建和管理用户会话的生命周期,以在各种环境中提供强大的会话体验的用户。这是在安全框架的世界一个独特的功能 - Shiro在任何环境下本地管理用户会话的能力,即使没有Web/ Servlet或者EJB容器提供。默认情况下,Shiro将利用现有的会话机制,如果有的话,(如Servlet容器),但如果没有一个,如在一个独立的应用程序或者非网络环境中,它会使用其内置的企业会话管理提供相同的编程经验。该SessionDAO存在允许使用任何数据源坚持会话。- SessionDAO(
[org.apache.shiro.session.mgt.eis.SessionDAO](http://shiro.apache.org/static/current/apidocs/org/apache/shiro/session/mgt/eis/SessionDAO.html)
)
该SessionDAO
执行会话持久性(CRUD)操作代表是SessionManager的。这样就可以插入到会话管理基础设施的任何数据存储。
- SessionDAO(
-
CacheManager(
[org.apache.shiro.cache.CacheManager](http://shiro.apache.org/static/current/apidocs/org/apache/shiro/cache/CacheManager.html)
)
CacheManager
创建和管理用于其他组件ShiroCache实例的生命周期。因为Shiro可以访问许多后端数据源的身份验证,授权和会话管理,高速缓存一直是一流的架构特点的框架,同时利用这些数据源来提高性能。任何现代化的开放式源代码或企业的缓存产品,可以插在Shiro提供一个快速,高效的用户体验。 -
Cryptography(
[org.apache.shiro.crypto.*](http://shiro.apache.org/static/current/apidocs/org/apache/shiro/crypto/package-summary.html)
)
密码学是一种天然的除了企业的安全框架。 Shiro的加密套件包含易于使用和理解crytographic密码算法的表示,哈希(又名消化)和不同的编解码器的实现。所有的类在这个包中经过精心设计,非常易于使用和易于理解。任何人谁使用了Java的原生加密支持知道它可以是一个具有挑战性的动物驯服。 Shiro的加密API的简化了复杂的Java机制,使加密易于使用的普通凡人的人类。 -
Realms(
[org.apache.shiro.realm.Realm](http://shiro.apache.org/static/current/apidocs/org/apache/shiro/realm/Realm.html)
)
如上所述,Realms充当“桥梁”或Shiro和应用程序的安全性数据之间的“连接器”。当谈到时间,实际上是与像用户帐户的安全相关的数据交互,以执行身份验证(登录)和授权(访问控制),Shiro查找这些东西很多从配置为应用程序的一个或多个国度。根据您的需要(通常每个数据源一个)可以配置为在许多领域四郎将与他们协调所必需的身份验证和授权。
TheSecurityManager
因为Shiro的API,鼓励一个Subject为中心的编程方法,大多数应用程序开发人员很少,如果有的话,用SecurityManager直接交互(框架开发但有时可能会发现它很有用)。即便如此,它仍然是重要的,知道SecurityManager怎样的功能,配置一个用于应用程序时尤其如此。
设计
如前所述,应用程序的SecurityManager
执行安全操作和管理状态,所有的应用程序的用户。在Shiro的默认SecurityManager
的实现,这包括:
- Authentication
- Authorization
- Session Management
- Cache Management
- Realmcoordination
- Event propagation
- “Remember Me” Services
- Subject creation
- Logout
and more.
但是,这是一个很大的功能,试图以管理一个单一的组成部分。而且,如果一切都被集中到一个单一的实现类使这些东西灵活和可定制将是非常困难的。
为了简化配置并启用灵活配置/可插, Shiro的实现都高度模块化设计 - 所以模块化事实上,该安全管理器实现(和它的类层次结构)并没有做很多的。相反,SecurityManager实现大多是作为一个轻量级的“容器”组件,委派几乎所有的行为,以嵌套/包裹组件。这种“包装”的设计体现在上述的详细架构图。
而组件实际执行的逻辑,SecurityManager
的实现知道如何以及何时协调组件正确的行为。
SecurityManager
的实现和JavaBeans兼容,允许你(或配置机制)来轻松定制可插拔的组件,通过标准JavaBeans访问器/ 修改器方法(get*/set*)。这意味着Shiro的架构模块可以转化为非常容易配置的自定义行为。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t4a5I08O-1651232933187)(https://cwiki.apache.org/confluence/images/icons/emoticons/check.gif)]
简易配置
因为JavaBeans的兼容性,它是很容易通过支持JavaBeans风格配置的机制,如Spring,Guice,JBoss等,配置安全管理器与自定义组件
接下来我们将介绍Configuration。
Apache Shiro 配置
Shiro被设计在任何环境下工作,从简单的命令行应用程序到最大的企业集群应用。因为环境的这种多样性,有一些是适合于配置的配置机制。本节介绍仅由Shiro核心支持的配置机制。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UZSwjnp0-1651232933188)(https://cwiki.apache.org/confluence/images/icons/emoticons/check.gif)]
许多配置选项
Shiro的SecurityManager的实现和所有支持组件都是兼容所有JavaBeans。这使得Shiro与几乎任何配置格式,如常规的Java,XML(Spring,JBoss的,Guice等),YAML,JSON,Groovy的生成器的标记等。
可编程配置(不被推荐)
绝对简单的方法来创建一个安全管理器,并使其对应用程序可用的是创建一个org.apache.shiro.mgt.DefaultSecurityManager,并将其连接起来的代码。例如:
Realm realm = //instantiate or acquire a Realm instance. We’ll discuss Realms later.
SecurityManager securityManager = new DefaultSecurityManager(realm);
//Make the SecurityManager instance available to the entire application via static memory:
SecurityUtils.setSecurityManager(securityManager);
出人意料的是,之后只有3行代码,你现在有适合多种应用的全功能Shiro环境。多么容易的!?
SecurityManager对象图
由于在架构章节中讨论,Shiro的SecurityManager
的实现基本上是嵌套的安全特定组件的模块化对象图。因为他们也是JavaBeans的兼容,可以调用任何嵌套组件的getter和setter方法来配置的SecurityManager
及其内部对象图。
例如,如果你想配置SecurityManager
实例使用自定义SessionDAO
自定义会话管理,你可以直接与嵌套是SessionManager的setSessionDAO
方法设置SessionDAO
:
…
DefaultSecurityManager securityManager = new DefaultSecurityManager(realm);
SessionDAO sessionDAO = new CustomSessionDAO();
((DefaultSessionManager)securityManager.getSessionManager()).setSessionDAO(sessionDAO);
…
采用直接方法调用,您可以配置任何部分SecurityManager的对象图。
但是,就这么简单编程定制是,它并不代表大多数现实世界的应用的理想配置。有几个原因,编程配置可能不适合您的应用程序:
- 它需要你了解和实例化一个直接执行。这将是更好,如果你没有了解具体的实现和在哪里可以找到他们。
- 因为Java的类型安全性,你需要投通过
get*
方法其具体实施获取的对象。这么多的铸造是丑陋的,冗长的,并且紧密关联您实现类。 - 该
SecurityUtils.setSecurityManager
方法调用,使安全管理器实例化实例的虚拟机的静态单例,其中,精而对于许多应用,如超过一名Shiro功能的应用程序在同一个JVM上运行会造成问题。它可能是更好的,如果该实例是一个应用程序单,但不是一个静态的内存引用。 - 它需要你,你想使一个Shiro配置更改每次重新编译应用程序。
然而,即使有这些警告,直接编程操作方法可能仍然是有价值的在内存受限的环境中,如智能手机的应用程序。如果您的应用程序不会在内存受限的环境中运行,你会发现基于文本的配置更容易使用和阅读。
INI 配置
大多数应用程序,而不是从中受益的源代码,可以独立修改,甚至使事情更容易理解对于那些不十分熟悉Shiro的API的基于文本的配置。
为了确保能够在各种环境下以最小的第三方依赖关系工作公分母基于文本的配置机制,Shiro支持的INI格式建立的SecurityManager对象图及其配套零部件。 INI易于阅读,易于配置,并且易于设置和适合大多数应用良好。
通过INI创建SecurityManager from
以下是如何构建基于INI配置了SecurityManager两个例子。
SecurityManager from an INI resource
我们可以从一个INI资源路径创建安全管理器实例。当前缀分别是file:
,classpath:
或者url:
资表示资源来源于文件系统、类路径或URL。此示例使用一个Factory
从classpath的根目录摄取shiro.ini文件,并返回安全管理器的实例:
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.util.Factory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.config.IniSecurityManagerFactory;
…
Factory<SecurityManager> factory = new IniSecurityManagerFactory(“classpath:shiro.ini”);
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
SecurityManager from an INI instance
该INI配置可以构造编程,以及如果通过[org.apache.shiro.config.Ini](http://shiro.apache.org/static/current/apidocs/org/apache/shiro/config/Ini.html)
类所需。 ini的类的功能类似于JDK中的[java.util.Properties](http://download.oracle.com/javase/6/docs/api/java/util/Properties.html)
类,但还要通过节名支持分割。
例如:
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.util.Factory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.config.Ini;
import org.apache.shiro.config.IniSecurityManagerFactory;
…
Ini ini = new Ini();
//populate the Ini instance as necessary
…
Factory<SecurityManager> factory = new IniSecurityManagerFactory(ini);
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
现在我们知道了如何从INI配置构建一个安全管理器,让我们来看看究竟是如何定义Shiro INI配置。
INI Sections
INI is basically a text configuration consisting of key/value pairs organized by uniquely-named sections. Keys are unique per section only, not over the entire configuration (unlike the JDKProperties). Each section may be viewed like a singleProperties
definition however.
Commented lines can start with either with an Octothorpe (# - aka the ‘hash’, ‘pound’ or ‘number’ sign) or a Semi-colon (‘;’)
Here is an example of the sections understood by Shiro:
INI是其基本组成由唯一命名的键/值对组成的文本配置。键是每款只有独特的,而不是在整个配置(不同于JDK的属性)。每一节都可以像一个单一的属性定义,但可以查看。
注释行以井号(# - 又名“散列”,“磅”或“编号”符号)或分号(‘;’)开始 。
这里是由Shiro理解的部分的一个示例:
# =======================
Shiro INI configuration
=======================
[main]
Objects and their properties are defined here,
Such as the securityManager, Realms and anything
else needed to build the SecurityManager
[users]
The ‘users’ section is for simple deployments
when you only need a small number of statically-defined
set of User accounts.
[roles]
The ‘roles’ section is for simple deployments
when you only need a small number of statically-defined
roles.
[urls]
The ‘urls’ section is used for url-based security
in web applications. We’ll discuss this section in the
Web documentation
[main]
[main]部分,可以配置应用程序的SecurityManager的实例和任何依赖关系,如与Realms。
像安全管理器或任何其依赖关系配置的对象实例听起来像一个困难的事情与INI ,在这里我们只能使用名称/值对。但通过一点点的公约和对象图的理解,你会发现你可以做很多。Shiro使用这些假设,使一个简单但相当简洁的配置机制。
我们常常喜欢把这种方法称为“穷人的”依赖注入,虽然没有那样强大全面的Spring/Guice/ JBoss的XML文件,你会发现它得到了很多没有太多的复杂性做了。当然,与其他配置机制,可为好,但使用Shiro它们不是必需的。
只是为了激起你的兴趣,这里是一个有效的[main]
配置的一个例子。我们将讨论它在下面详细,但你可能会发现,你了解了不少的东西:已由直觉
[main]
sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
myRealm = com.company.security.shiro.DatabaseRealm
myRealm.connectionTimeout = 30000
myRealm.username = jsmith
myRealm.password = secret
myRealm.credentialsMatcher = $sha256Matcher
securityManager.sessionManager.globalSessionTimeout = 1800000
定义一个对象
考虑下面的[main]
部分片段:
[main]
myRealm = com.company.shiro.realm.MyRealm
…
此行实例化类型com.company.shiro.realm.MyRealm的一个新的对象实例,使下进一步参考和配置MYREALM名可用的对象。
如果实例化的对象实现了org.apache.shiro.util.Nameable接口,则Nameable.setName方法将在对象上调用的名称值(MYREALM在这个例子中)。
设置对象属性
Primitive Values
简单的原始属性可以只使用等号赋值:
…
myRealm.connectionTimeout = 30000
myRealm.username = jsmith
…
配置这些线路转化成方法调用:
…
myRealm.setConnectionTimeout(30000);
myRealm.setUsername(“jsmith”);
…
这怎么可能呢?它假设所有的对象都是Java Beans的兼容的POJO。
在后台,Shiro默认使用Apache共享BeanUtils的设置这些属性时做所有繁重。因此,尽管INI值是文本,BeanUtils的知道如何将字符串值转换为正确的原始类型,然后调用相应的JavaBeans的setter方法。
Reference Values
如果你需要设置的值不是一个原始的,但另一个对象?那么,你可以使用一个美元符号($)来引用先前定义的实例。例如:
…
sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
…
myRealm.credentialsMatcher = $sha256Matcher
…
这只是定位由名sha256Matcher定义的对象,然后使用BeanUtils的设置该对象上的MYREALM实例(通过调用myRealm.setCredentialsMatcher(sha256Matcher)方法)。
Nested Properties
在INI线的左侧用点号等号,你可以遍历对象图来得到你想要设定的最终目标/属性。举例来说,这个配置行:
…
securityManager.sessionManager.globalSessionTimeout = 1800000
…
转化(通过BeanUtils)转换成以下逻辑:
securityManager.getSessionManager().setGlobalSessionTimeout(1800000);
该图的遍历可以深如必要:object.property1.property2....propertyN.value = blah
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eDos5Sg3-1651232933189)(https://cwiki.apache.org/confluence/images/icons/emoticons/check.gif)]
BeanUtils属性支持
由BeanUtils.setProperty方法支持的任何属性赋值操作将工作的Shiro的[main]部分,包括set/list/ map元素赋值。请参阅Apache下议院的BeanUtils网站和文档了解更多信息。
Byte Array Values
因为原始字节数组,不能在本地文本格式指定的,我们必须使用字节数组的文本编码。该值可以指定为Base64编码的字符串(缺省值)或十六进制编码的字符串。默认值是Base64的,因为Base64编码要求,代表值减去实际文本 - 它有一个更大的编码字母,意思是你的令牌是较短的(有点更好的文本配置)。
# The ‘cipherKey’ attribute is a byte array. By default, text values
for all byte array properties are expected to be Base64 encoded:
securityManager.rememberMeManager.cipherKey = kPH+bIxk5D2deZiIxcaaaA==
…
但是,如果您更喜欢使用十六进制编码代替,你必须前缀为0x(‘zero’ ‘X’)的字符串标记:
securityManager.rememberMeManager.cipherKey = 0x3707344A4093822299F31D008
Collection Properties
Lists, Sets and Maps可以像任何其他财产-直接或作为一个嵌套属性。对于sets and lists,只需指定用逗号分隔的设定值或对象的引用。
例如,一些SessionListeners:
sessionListener1 = com.company.my.SessionListenerImplementation
…
sessionListener2 = com.company.my.other.SessionListenerImplementation
…
securityManager.sessionManager.sessionListeners = $sessionListener1, $sessionListener2
对于Maps,您可以指定用逗号分隔的键值对,其中每个键 - 值对用冒号分隔的列表’:’
object1 = com.company.some.Class
object2 = com.company.another.Class
…
anObject = some.class.with.a.Map.property
anObject.mapProperty = key1: o b j e c t 1 , k e y 2 : object1, key2: object1,key2:object2
在上面的例子中,$object1引用的对象会在地图下方的String键key1,即map.get(“key1”)返回object1
。您也可以使用其他对象的键:
anObject.map =
o
b
j
e
c
t
K
e
y
1
:
objectKey1:
objectKey1:objectValue1,
o
b
j
e
c
t
K
e
y
2
:
objectKey2:
objectKey2:objectValue2
…
Considerations
Order Matters
INI格式和约定以上是非常方便和容易理解的,但它不是一样强大的其他文字/基于XML的配置机制。利用上述机制时,需要理解的最重要的事情是 Order Matters!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IdaF5S7o-1651232933189)(https://cwiki.apache.org/confluence/images/icons/emoticons/warning.gif)]
小心
每个对象实例化和每个值分配在它们出现在[main]片段的顺序被执行。这些线最终转化为一个JavaBeans的getter / setter方法调用,所以这些方法都以相同的顺序调用!
写你的配置时,请记住这一点。
Overriding Instances
任何对象都可以通过后面的配置中定义一个新的实例所覆盖。因此,例如,第二MYREALM定义将覆盖第一个:
…
myRealm = com.company.security.MyRealm
…
myRealm = com.company.security.DatabaseRealm
…
这将导致MYREALM是一个com.company.security.DatabaseRealm实例和以前的实例将永远不会被使用(垃圾回收)
Default SecurityManager
你可以在上面完整的例子已经注意到了安全管理器实例的类没有被定义,我们跳右中只设置一个嵌套属性:
myRealm = …
securityManager.sessionManager.globalSessionTimeout = 1800000
…
这是因为securityManager实例是一个特别的 - 它已经实例化你准备好了,这样你就不需要知道具体的SecurityManager实现类实例化。
当然,如果你真的想要指定自己的实现,你可以,只要你定义实施的“重写实例”一节中规定:
…
securityManager = com.company.security.shiro.MyCustomSecurityManager
…
当然,这是很少需要 - Shiro的SecurityManager的实现是非常定制,通常可以使用任何必要的配置。您可能要问自己(或用户列表中)如果你真的需要这样做。
[users]
在[users]
部分允许您定义一组静态的用户帐户。这是在环境中极少数的用户帐户或在用户帐户不需要在运行时动态地创建最有用。下面是一个例子:
[users]
admin = secret
lonestarr = vespa, goodguy, schwartz
darkhelmet = ludicrousspeed, badguy, schwartz
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iPf41XSs-1651232933190)(https://cwiki.apache.org/confluence/images/icons/emoticons/information.gif)]
自动 IniRealm
只是定义非空[users] 或者 [roles]部分会自动触发创建一个[org.apache.shiro.realm.text.IniRealm](http://shiro.apache.org/static/current/apidocs/org/apache/shiro/realm/text/IniRealm.html)
,并使其在名为iniRealm下的[main]部分找到。可以象如上所述的任何其他对象进行配置。
Line Format
在[user]部分中的每一行都必须符合以下格式:
username
=password
,roleName1,roleName2, …,roleNameN
- 上等号左边的值是用户名
- 上等号右边的第一个值是用户的密码。需要密码。
- 密码之后,任何逗号分隔的值是分配给该用户角色的名称。角色名是可选的。
Encrypting Passwords
如果您不希望[user]部分的密码是纯文本,你可以,只要你喜欢用你最喜欢的哈希算法(MD5,SHA1,SHA256等)进行加密,并使用生成的字符串作为密码值。默认情况下,密码字符串预计将十六进制编码,但可以配置为Base64编码代替(见下文)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3LCMrIdC-1651232933191)(https://cwiki.apache.org/confluence/images/icons/emoticons/check.gif)]
简单安全密码
为了节省时间,并使用最佳实践,你可能想使用Shiro的命令行散列器Command Line Hasher,将哈希密码以及任何其他类型的资源。它是用于加密的INI[user]密码特别方便。
一旦你指定的哈希密码的文本值,你一定要告诉Shiro,这些都是加密的。你这样做,通过配置在[main]部分中的隐式创建iniRealm使用与您所指定的哈希算法适当CredentialsMatcher实现:
[main]
…
sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
…
iniRealm.credentialsMatcher = $sha256Matcher
…
[users]
user1 = sha256-hashed-hex-encoded password, role1, role2, …
user1 = 2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b, role1, role2, …
您可以配置在CredentialsMatcher
像任何其他对象的任何属性,以反映你的散列策略,例如,指定是否加盐使用或多少哈希迭代执行。见[org.apache.shiro.authc.credential.HashedCredentialsMatcher](http://shiro.apache.org/static/current/apidocs/org/apache/shiro/authc/credential/HashedCredentialsMatcher.html)
的JavaDoc以更好地了解散列策略,如果他们可能对你有用。
例如,如果用户的密码字符串进行Base64编码,而不是默认Hex,,您可以指定:
[main]
…
true = hex, false = base64:
sha256Matcher.storedCredentialsHexEncoded = false
[roles]
在[roles]
部分允许您使用权限Permissions在[user]部分定义的角色相关联。再次,这是在环境中具有少量的角色或者角色不需要在运行时动态创建的是有用的。下面是一个例子:
[roles]
‘admin’ role has all permissions, indicated by the wildcard ‘*’
admin = *
The ‘schwartz’ role can do anything (*) with any lightsaber:
schwartz = lightsaber:*
The ‘goodguy’ role is allowed to ‘drive’ (action) the winnebago (type) with
license plate ‘eagle5’ (instance specific id)
goodguy = winnebago:drive:eagle5
Line Format
在[roles]部分的每一行必须定义一个角色到权限的键/值映射,具有以下格式:
rolename
=permissionDefinition1,permissionDefinition2, …,permissionDefinitionN
其中permissionDefinition是一个任意的字符串,但大多数人将要使用符合字符串为易用性和灵活性的[org.apache.shiro.authz.permission.WildcardPermission](http://shiro.apache.org/static/current/apidocs/org/apache/shiro/authz/permission/WildcardPermission.html)
格式。查看权限文档权限的更多信息,以及如何可以从中受益。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HaazldCu-1651232933191)(https://cwiki.apache.org/confluence/images/icons/emoticons/information.gif)]
内部逗号
需要注意的是,如果一个人permissionDefinition?需要在内部用逗号分隔(例如printer:5thFloor:print,info),您将需要围绕这个定义用双引号(“),以避免解析错误:? "printer:5thFloor:print,info"
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e1zP4MQH-1651232933192)(https://cwiki.apache.org/confluence/images/icons/emoticons/check.gif)]
角色没有权限
如果您有角色不需要的权限关联,您不必一一列举在[roles]部分,如果你不想。只是定义在[user]部分中的角色名称就足以创造中的作用,如果它不存在。
[urls]
]