你的第一个Apache Shiro的应用程序
如果你是新到Apache Shiro,这个简短的教程将向您展示如何设置一个初始和非常简单的应用程序的Apache Shiro。 一路上我们将讨论Shiro的核心概念,帮助你熟悉Shiro的设计和API。
如果你不想编辑文件当你跟随本教程中,您可以获得一个几乎相同的示例应用程序和参考。 选择一个地点:
- 在Apache Shiro的Subversion存储库:https://svn.apache.org/repos/asf/shiro/trunk/samples/quickstart/
- 在Apache Shiro的源分布样品/快速入门目录中。 源分布是可用的下载页面。
设置
在这个简单的示例中,我们将创建一个非常简单的命令行应用程序,它将运行并迅速退出,这样你可以领略到Shiro的API。
![]() | 任何应用程序 Apache Shiro设计从一开始就支持任何应用程序——从最小的命令行应用程序最大的集群web应用程序。 尽管我们创建一个简单的应用程序对于本教程,知道应用相同的使用模式无论如何您的应用程序创建或部署。 |
本教程需要Java 1.5或更高版本。 我们还将使用ApacheMaven作为我们的构建工具,当然这不是必须使用Apache Shiro。 你可能获得Shiro的。 罐子,把他们以任何方式你喜欢到您的应用程序,例如也许使用Apache蚂蚁和艾薇。
对于本教程,请确保您使用Maven 2.2.1或更高版本。 你应该能够类型mvn——版本在一个命令提示并看到类似如下:
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文件的目录:
<?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> </project>
本教程类
我们将运行一个简单的命令行应用程序,因此我们将需要创建一个Java类公共静态void main(String[]args)方法。
包含在同一个目录下pom.xml文件,创建一个*src /主/ java子目录。 在src /主/ 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);
}
}
不要担心现在的导入语句——我们会很快。 但是现在,我们有一个典型的命令行程序“壳”。 这个程序会打印出所有文本“我的第一个Apache Shiro应用程序”并退出。
测试运行
尝试我们的教程应用程序中,执行以下命令提示符中您的教程项目的根dirctory(如。shiro-tutorial),输入以下:
mvn编译执行:java
,你就会看到我们的小教程应用程序的运行和退出。 您应当会看到类似于下面的(注意粗体文本,表明我们的输出):
lhazlewood:~ /项目/ shiro-tutorial mvn编译执行美元:java
… 一群Maven输出……
1(Tutorial.main()]信息教程——我第一次Apache Shiro应用
lhazlewood:~ /项目/ shiro-tutorial \ $
我们已经验证了应用程序成功运行——现在让我们使Apache Shiro。 当我们继续学习教程,您可以运行mvn编译执行:java每次我们添加更多的代码之后看到我们的变化的结果。
使Shiro
首先要理解在一个应用程序启用Shiro,几乎所有在Shiro有关中央/核心组件称为SecurityManager。 对于那些熟悉Java安全,这是Shiro的SecurityManager——它的概念不相同的事情java.lang.SecurityManager。
当我们将介绍Shiro的详细设计体系结构章,现在它已经足够好了知道ShiroSecurityManager是Shiro环境的核心应用程序和一个SecurityManager每个应用程序必须存在。 因此,我们必须做的第一件事在我们教程应用程序设置SecurityManager实例。
配置
虽然我们可以实例化一个SecurityManager类直接,Shiro的SecurityManager实现有足够的配置选项和内部组件,使这痛苦在Java源代码——它会更容易配置SecurityManager一个灵活的基于文本的配置格式。
为此,Shiro提供了一个默认的“公分母”通过基于文本的解决方案INI配置。 人很累这些天使用笨重的XML文件,和INI易于阅读,易于使用,并且需要极少依赖。 您还将看到以后,用一个简单的理解对象图导航,可以有效地使用INI配置简单对象SecurityManager的图。
![]() | 许多配置选项 Shiro的SecurityManagerjavabean实现和支持所有组件都是兼容的。 这允许配置Shiro几乎任何配置格式(如XML(春天,JBoss,Guice等),YAML、JSON、Groovy生成器标记等等。 INI只是Shiro的“公分母”格式,允许在任何环境中配置,以防其他选项不可用。 |
shiro.ini
所以我们将使用INI文件来配置ShiroSecurityManager对于这个简单的应用程序。 首先,创建一个src / main /资源目录在同一个目录开始pom.xml是。 然后创建一个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在我们的教程应用程序类实例。 改变主要方法以反映以下更新:
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编译执行:java和看到,一切仍然成功运行(由于Shiro的默认日志记录调试或更低,你不会看到任何Shiro日志消息——如果它启动和运行没有错误,那么你知道一切都还好)。
这是上面的添加在做什么:
- 我们使用Shiro的IniSecurityManagerFactory实现我们摄取shiro.ini文件位于类路径的根。 这个实现反映了Shiro的支持工厂方法设计模式。 的类路径:前缀是一个资源指标,告诉shiro加载ini文件(其他前缀,如URL:和文件:支持)。
- 的factory.getInstance()方法被调用时,该解析并返回一个INI文件SecurityManager实例反映了配置。
- 在这个简单的例子中,我们设置SecurityManager是一个静态(内存)单跨JVM访问。 但是要注意,这不是必要如果你会有多个Shiro-enabled应用程序在单个JVM。 对于这个简单的示例,它是好的,但更复杂的应用程序环境通常会把SecurityManager在特定于应用程序的内存(比如在一个web应用程序的servletcontext中或Spring、Guice或JBoss DI容器实例)。
使用Shiro
现在我们的SecurityManager设置和ready-to去,现在我们可以开始做我们真正关心的东西——执行安全操作。
在保护我们的应用程序时,可能我们问自己最相关的问题是“当前用户是谁? ”或“X”是当前用户允许呢? 通常问这些问题当我们编写代码或设计用户界面:通常构建应用程序基于用户故事,和你想要的功能(保护)基于用户的基础上。 所以,最自然的方式为我们思考安全在我们的应用程序是基于当前用户。 Shiro的API从根本上代表的概念与当前用户主题的概念。
在几乎所有的环境中,您可以获得当前执行的用户通过以下电话:
Subject currentUser = SecurityUtils.getSubject();
使用securityutils。getSubject(),我们可以获得当前执行的主题。主题是一个安全术语,其基本意思是“当前执行的安全特定的视图用户”。 这不是所谓的“用户”,因为“用户”这个词通常跟一个人联系在一起。 在安全的世界里,“主题”这个词可以指一个人,也是一个第三方的过程,cron作业,守护进程帐户,或者类似的东西。 它仅仅意味着“目前的东西与软件的交互。 如果没有意图和目的,不过,你能想到的主题Shiro的“用户”的概念。
的getSubject()在一个独立的应用程序可能会返回一个电话主题基于用户数据在一个特定于应用程序的位置,在服务器环境(例如web应用程序),它获得的主题基于用户数据与当前线程或传入请求。
现在,您已经有了一个主题,你可以用它来做什么?
如果你想让事情在当前会话用户可用的应用程序,您可以获取会话:
Session session = currentUser.getSession();
session.setAttribute( "someKey", "aValue" );
的会话是Shiro-specific实例,提供你习惯与普通的httpsession但与一些额外的好处,一个呢大区别:它不需要HTTP环境!
如果部署在一个web应用程序,默认情况下会话将httpsession的基础。 但在非web环境中,像这样简单的教程应用程序,Shiro会自动使用其默认企业会话管理。 这意味着你可以在您的应用程序使用相同的API,在任何层,无论部署环境! 这将打开一个全新的世界的应用程序从任何应用程序需要会话不需要被迫使用httpsession或EJB有状态会话bean。 现在,任何客户机技术可以共享会话数据。
所以现在你可以获得主题和他们的会话。 那真的有用的东西像检查如果他们被允许做的事情,喜欢对角色和权限检查吗?
好吧,我们只能做那些检查一个已知的用户。 我们的主题上面的实例代表当前用户,但是谁当前用户吗? 好吧,他们是匿名的——也就是说,直到他们至少登录一次。 所以,让我们这样做:
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更多。
![]() | 方便的提示 安全最佳实践是给普通用户登录失败的消息,因为你不想帮助攻击者试图进入你的系统。 |
好吧,现在,我们有一个登录用户。 我们还能做什么?
让我们说他们是谁:
//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配置),你喜欢:
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的主要设计概念,主题和SecurityManager。
但这是一个相当简单的应用程序。 你可能会问自己,“如果我不想使用INI用户帐号,而想要连接到一个更复杂的用户数据源?”
回答这个问题需要一个小Shiro的架构的深入了解和支持配置机制。 我们将讨论Shiro的体系结构下一个。