SHIRO
你的Apache Shiro第一个应用
如果你是shiro新手 ,这个教程会教你创建一个被shiro保护的基本的简单应用。我们将讨论shiro核心观念并顺便让你熟悉shiro的设计和API。
如果你在阅读这篇文章的时候不想编辑文件,你可以得到一个和示例代码一样代码供你参考,选择一个站点:
·Apache shiro git仓库
https://github.com/apache/shiro/tree/master/samples/quickstart
·上一篇下载的源码中 samples/quickstart 目录 。
创建
这是一个简单的示例,我们将创建一个非常简单的命令行应用 ,它会很快运行完并退出。这样你能感受一下shiro的api.
这个应用要求jdk1.5以及以上版本。我们还需要用到apache maven ,当然shiro不要求使用maven。你或许拿到shiro jar包并且任何你喜欢的方式整合到你的应用。举个例子,使用apache ant 和lvy。
这个教程开始之前先确认你在使用maven 2.2.1或以上的版本。你应该可以在命令行中输入 mvn--version ,会看到类似下面的东西。
测试maven安装版本
现在在你的创系统盘上建一个目录 ,比如 shiro-tutorial 再将下面的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>
Tutorial class
我们将运行一个简单的命令行应用 ,所以我们需要创建java class 包含 public static void main(String[] args)方法
在包含pom.xml的目录下创建一个src/main/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("我的第一个shiro应用!");
System.exit(0);
}
}
现在不要担心这些内容 -我们将简短说明。但是现在我已经获得一个典型的命令行程序‘壳子’。这些程序将会打印‘我的第一个shiro应用!’并退出。
测试运行
为了测试‘教程’应用, 命令行框中进到刚才创建的shiro 根目录下运行 mvn compile exec:java
你会看到我们的‘教程应用’运行并退出。你会看到类似下面的东西
我们已经测试应用运行成功 ,现在启动shiro 。当我们继续这个教程,你可以每次添加代码以后执行 mvn compile exec:java 查看改变的结果。
启动shiro
应用中启动shiro第一个要需要理解的是SecurityManager ,它是shiro中几乎每个事件都会涉及到的核心组件。对于熟悉Java security的开发者来说,shiro就相当于SecurityManager。但它不是java.lang.SecurityManager
我们将会在《结构章节中》涵盖shiro的详细设计,现在足够理解shiro Security Manager是shiro应用环境的核心并且每个应用必须存在。所以我们第一件需要做的事情就是实例化一个SecurityManager。
配置
虽然我们可以直接实例化一个Securtiymanger,但是shiro SecurityManager实现类有足够多的配置选项和内部组件,这是在java源码中很难做到 --SecurityManager基于文本格式的灵活配置会变得简单很多。
为此 ,shiro提供通过文本格式的默认‘公分母’ ini 配置文件。 人们已经厌倦了臃肿的xml配置文件,ini是个易于阅读,易用,并且需要很少的依赖。稍后你也会看到’对象图’ ,ini可以有效配置
简单的‘对象视图’,像是SecurityManager
(shiro的SercurityManager 实现类和所有支持的组件都是JAVABeans兼容的。这个使得shiro能配置为任何格式的,像xml (Spring,JBoss ,Guice等等),YAML,JSON,GROVY BUILDER markup 等等。INI只是shiro‘common denominator’格式,它能在其他配置不可得的任何环境中都能配置)
shiro.ini
在这个教程中我们使用INI文件配置shiro的SecurityManager。首先,在pom.xml所在的目录创建一个 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等。
引用配置
我们现在已经定义好我们的ini文件了,我们可以在教程的Tutorial类中实例化一个SecurityManager 。改变main函数来体现一下的更新。
public class Tutorial {
private static final transient Logger log = LoggerFactory.getLogger(Tutorial.class);
public static void main(String[] args) {
log.info("我的第一个shiro应用!");
Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro.ini");
//初始化securitymanager
SecurityManager securityManager=factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
System.exit(0);
}
}
到这里我们添加了三行代码在实例应用启动了shiro!很简单对吗?
随意的执行mvn compile exec:java 你仍然会看到运行成功。
上面的三行代码都干什么了呢?
1.我们用shiro的IniSecurityManagerFactory类去加载classpath根目录下的shiro.ini文件。这个实现类体现了shiro 支持工厂方法设计模式的支持。Classpath:前缀是一个告诉shiro到什么位置加载ini文件的资源指针。
2.factory.getInstance() 方法调用后转换ini文件并返回体现配置的SecurityManager。
3.在这个简单的示例中,我们SecurityManager创建成静态单例,可以通过JVM访问。但是请注意,在单个JVM中有一个以上的shiro应用这是不可取的。在这个应用中没什么问题,如果在更复杂的应用中经常把SecurityManager放在应用指定的内存中(像是servletcontext,或者spring ,JBoss等容器中)
使用Using Shrio
现在我们的SecurityManager已经被创建,我们现在可以开始做我们关心的事情 -执行安全操作。
当我们保护我们的应用时,或许相关的一些问题是‘谁是当前用户?’或者是‘当前用户被允许做XXX?’这些是时我们设计用户接口或写代码的时候问的最普遍的问题。应用常常建立在用户故事,并且开发者希望基于用 户来表示功能。所以,我们想到保护应用最自然的方式就是基于‘当前用户’。Shiro的api根本上表示 ‘当前用户’是‘主体’的概念。
在大部分环境中我们可以调用一下方法来获取当前执行的用户:
Subject currentUser = SecurityUtils.getSubject();
使用 SecurityUtils.getSubject()这个方法,我们可以获取到当前执行的主体。‘主体’是一个基本上意味着‘一个当前执行用户的安全特定视图’的安全术语。这个不会被称为‘用户’,因为‘用户’这个词一般被联 想成人类。在安全领域中,Subject(主体)这个安全术语能代表人类,也能是第三方程序,恶意账号或者类似的东西。简单的意思是‘当前和软件正在交互的东西’。对于大多数意图和目的,你可以认为主体是shiro的 ‘用户’。
这个getSubject()方法在独立的应用中调用后应该基于用户指定的数据库位置返回一个subjec(主体),在一个服务器环境中(web app)这个方法会获取关联到当前线程或者请求中的一个用户数据。
既然现在有了subject(主体),我们能做什么呢?
如果你想让用户在会话期间能够得到一些东西,你能获取他们的session(会话):
Session session=currentUser.getSession();
session.setAttribute(“key”,”value”);
这个shiro特定的 Session 不但提供你习惯的常规Httpsession 还有其他的好处和很大的区别:他不需要Http环境!
如果部署在一个web应用中,Session 默认是基于HttpSession的。但是在一个没有web的环境中,像这个简单的教程示例,shiro将默认自动使用他的Enterprise Session Management(企业会话管理)。这个意味着你可以在应用中使用同样的api,在任何层,不管他的部署环境。这打开应用程序的全新的一个世界,因为需要session的应用不需要强制使用HttpSession 或者EJB 静态 session bean。 并且任何客户端界都能共享这个会话数据。
现在你有一个subject(主体)和它的session(会话)。我们能做真正有用的事情是检查他们被允许做什么东西,比如检查角色和权限?
好,我们只能对一个已知的用户能做这些。我们的subhect(主体)代表了当前用户,但是谁是当前用户?他们至少在登陆一次之前是匿名的,所以让我做这个:
Subject currentuser=SecurityUtils.getSubject();
if(!currentuser.isAuthenticated()){
log.info("用户登陆");
UsernamePasswordToken token=new UsernamePasswordToken("root","admin");
token.setRememberMe(true);
//登陆
currentuser.login(token);
}
就是这样的!不能再简单了吧。
但是他们的登陆失败了怎么办?你能捕获所有类型的异常会告诉你到底发生什么了,并且可以对应处理他们。
try{
currentuser.login(token);
}catch(UnknownAccountException unknownAccountException){
log.info("不存在的账号");
}catch(IncorrectCredentialsException incorrectCredentialsException){
log.info("密码不正确");
}catch(LockedAccountException lockedAccountException){
log.info("被锁的账号");
}catch(Exception e){
log.info("异常");
}
然后让他们说出他们是谁
log.info(currentuser.getPrincipal+"-->登陆成功");
我们还能测试一下他们是否具有特定的权限:
log.info(currentuser.getPrincipal()+"-->登陆成功");
if(currentuser.hasRole("admin")){
log.info("拥有admin角色");
} else{
log.info("无此角色");
}
if(currentuser.isPermitted("lightsaber:user:delete")){
log.info("可delete操作");
}else{
log.info("不可delete操作");
}
希望这篇教程帮助你理解额怎么创建一个shiro应用以及理解shiro最初设计的观念,Subject(主体)和securityManager(安全管理器)。
但这是一个相对简单的应用,你可能会问 “如果不用.ini配置文件,怎么去连接更为复杂的数据源?”
为了回答这个问题需要一个更深层次的理解‘shiro结构’和支持的配置机制。
好我们将要讲解‘shiro结构’
最后贴出源码 (也可以从shiro官网进行下载)
public static void main(String[] args) {
log.info("我的第一个shiro应用!");
Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro.ini");
//初始化securitymanager
SecurityManager securityManager=factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject currentuser=SecurityUtils.getSubject();
if(!currentuser.isAuthenticated()){
log.info("执行用户登陆流程");
UsernamePasswordToken token=new UsernamePasswordToken("lonestarr","vespa");
token.setRememberMe(true);
try{
currentuser.login(token);
}catch(UnknownAccountException unknownAccountException){
log.info("不存在的账号");
}catch(IncorrectCredentialsException incorrectCredentialsException){
log.info("密码不正确");
}catch(LockedAccountException lockedAccountException){
log.info("被锁的账号");
}catch(Exception e){
log.info("异常");
}
}
log.info(currentuser.getPrincipal()+"-->登陆成功");
if(currentuser.hasRole("admin")){
log.info("拥有admin角色");
} else{
log.info("无此角色");
}
if(currentuser.isPermitted("lightsaber:user:delete")){
log.info("可delete操作");
}else{
log.info("不可delete操作");
}
System.exit(0);
}
Shiro.ini配置文件
# -----------------------------------------------------------------------------
# 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/用户/ 创建’ 权限
schwartz = lightsaber:user:create
goodguy = winnebago:drive:eagle5