如果你是Apache Shiro的新手,这个简短的教程将告诉你如何设置一个由Apache Shiro保护的初始和非常简单的应用程序。我们将讨论Shiro的核心概念,以帮助您熟悉Shiro的设计和API。
如果您不想按照本教程实际编辑文件,则可以获取几乎相同的示例应用程序,并在您参考时进行参考。选择地点:
samples/quickstart
目录中。源分发可从下载页面获取。在这个简单的例子中,我们将创建一个非常简单的命令行应用程序,它将运行并快速退出,这样您可以感受到Shiro的API。
任何应用程序Apache Shiro是从第一天开始设计的,以支持任何应用程序 - 从最小的命令行应用程序到最大的集群Web应用程序。即使我们为本教程创建了一个简单的应用程序,也知道无论应用程序如何创建或部署在其中,都会使用相同的使用模式。
本教程需要Java 1.5或更高版本。我们还将使用Apache Maven作为我们的构建工具,但是当然这不需要使用Apache Shiro。您可以购买Shiro的.jars并以任何你喜欢的方式将它们合并到应用程序中,例如使用Apache Ant和Ivy。
对于本教程,请确保您使用的是Maven 2.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
并将以下Maven pom.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>
</project>
我们将运行一个简单的命令行应用程序,因此我们需要使用一个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语句现在 - 我们会很快得到他们。但是现在,我们有一个典型的命令行程序“shell”。所有这个程序都会打印出文字“我的第一个Apache Shiro应用程序”并退出。
要尝试我们的Tutorial应用程序,请在教程项目的根目录(例如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
后,每次我们添加一些更多的代码,以查看我们的更改的结果。
在应用程序中启用Shiro时,首先要了解的是,Shiro中的几乎所有内容都与称为的中央/核心组件相关SecurityManager
。对于那些熟悉Java安全性的人,这是Shiro的一个SecurityManager的概念 - 它不是一样的东西java.lang.SecurityManager
。
虽然我们将在“ 架构”章节中详细介绍Shiro的设计,但现在已经足够了解Shiro SecurityManager
是应用程序的Shiro环境的核心,而且每个应用程序SecurityManager
都必须存在。因此,我们在Tutorial应用程序中必须做的第一件事是设置SecurityManager
实例。
虽然我们可以SecurityManager
直接实例化一个类,但是Shiro的SecurityManager
实现有足够的配置选项和内部组件,这使得在Java源代码中做这件事变得很困难 - SecurityManager
使用灵活的基于文本的配置格式配置更容易。
为此,Shiro通过基于文本的INI配置提供了默认的“公分母”解决方案。人们对这些天使用庞大的XML文件感到厌烦,INI很容易阅读,使用简单,而且只需要非常少的依赖。稍后还会看到,通过对对象图形导航的简单理解,INI可以有效地用于配置简单对象图形,如SecurityManager。
许多配置选项Shiro的SecurityManager
实现和所有支持组件都是JavaBeans兼容的。这允许Shiro几乎配置任何配置格式,如XML(Spring,JBoss,Guice等),YAML,JSON,Groovy Builder标记等。INI只是Shiro的“公分母”格式,允许在任何环境中进行配置,以防其他选项不可用。
因此,我们将使用一个INI文件来SecurityManager
为这个简单的应用程序配置Shiro 。首先,创建一个src/main/resources
目录,从同一目录开始,其中pom.xml
。然后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
在我们的Tutorial应用程序类中创建该实例。更改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
实现来获取shiro.ini
位于类路径根目录的文件。这个实现反映了Shiro对工厂方法设计模式的支持。该classpath:
前缀是一个资源的指标,告诉四郎在哪里加载从ini文件(其它前缀,如url:
和file:
以及支持)。
该factory.getInstance()
方法被调用,它解析INI文件并返回一个SecurityManager
反映配置的实例。
在这个简单的示例中,我们将其设置SecurityManager
为静态(内存)单例,可通过JVM访问。但请注意,如果您在单个JVM中将有多个启用Shiro的应用程序,这是不可取的。对于这个简单的例子,它是确定,但更复杂的应用程序环境通常会放置SecurityManager
在应用程序特定的内存(如在Web应用程序ServletContext
或Spring,Guice或JBoss DI容器实例)。
现在我们的SecurityManager已经设置好了,现在我们可以开始做我们真正关心的事情 - 执行安全操作。
当保护我们的应用程序时,我们可能最相关的问题是“当前用户是谁”或“当前用户是否允许做X”?在我们编写代码或设计用户界面时,常常会提出这些问题:应用程序通常基于用户故事构建,并且您希望基于每个用户表示(和安全)的功能。因此,我们在应用程序中考虑安全性的最自然的方式是基于当前用户。Shiro的API从根本上代表了“当前用户”的Subject
概念。
在几乎所有环境中,您可以通过以下调用获取当前正在执行的用户:
Subject currentUser = SecurityUtils.getSubject();
使用SecurityUtils
。getSubject(),我们可以获取当前正在执行的Subject
。主题是一个安全术语,基本上意味着“当前正在执行的用户的安全特定视图”。它不被称为“用户”,因为“用户”这个词通常与一个人相关联。在安全世界中,术语“主题”可以指人,也可以是第三方进程,cron作业,守护进程帐户或任何类似的。它只是指“当前与软件交互的东西”。对于大多数意图和目的,你可以认为Subject
是Shiro的'用户'概念。
独立应用程序中的getSubject()
调用可以Subject
基于应用程序特定位置中的用户数据返回,并且在服务器环境(例如web应用程序)中,它Subject
基于与当前线程或传入请求相关联的用户数据来获取。
现在你有了Subject
,你能用它做什么?
如果您想在用户在应用程序的当前会话期间使用户可用,您可以获取其会话:
Session session = currentUser.getSession();
session.setAttribute( "someKey", "aValue" );
这Session
是一个Shiro特定的实例,提供了大多数你习惯了与常规HttpSessions,但有一些额外的好处和一个巨大的区别:它不需要HTTP环境!
如果在Web应用程序中部署,默认情况下Session
将HttpSession
基于。但是,在非Web环境中,像这个简单的教程应用程序,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。
方便的提示安全最佳做法是向用户提供通用登录失败消息,因为您不想帮助攻击者试图进入您的系统。
好的,所以到现在为止,我们有一个登录用户。我们还能做什么?
让我们说他们是谁:
//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.
在添加上面的代码示例之后,这里是我们最终的Tutorial类文件。随意编辑和玩它,并更改安全检查(和INI配置),你喜欢:
最终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的建筑。
https://www.leftso.com/article/122.html