在本教程中,咱们会写一个简单的、仅仅输出一些内容命令行程序,从而对Shiro有一个大致的感受。html
本教程须要Java1.5+,而且咱们用Maven生成项目,固然Maven不是必须的,你也能够经过导入Shiro jar包的方式、或使用Ant、Ivy,喜欢哪一种就用哪一种。java
开始以前,肯定你的Maven版本为2.2.1+(若是你用的是Maven的话),用mvn --version肯定Maven的版本。web
如今,咱们将正式开始。首先新建一个文件夹,好比说shiro-tutorial,而后将下面的Maven pom.xml文件放到该文件夹下。数据库
pom.xmlapache
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 5 6 <modelVersion>4.0.0</modelVersion> 7 <groupId>org.apache.shiro.tutorials</groupId> 8 <artifactId>shiro-tutorial</artifactId> 9 <version>1.0.0-SNAPSHOT</version> 10 <name>First Apache Shiro Application</name> 11 <packaging>jar</packaging> 12 13 <properties> 14 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 15 </properties> 16 17 <build> 18 <plugins> 19 <plugin> 20 <groupId>org.apache.maven.plugins</groupId> 21 <artifactId>maven-compiler-plugin</artifactId> 22 <version>2.0.2</version> 23 <configuration> 24 <source>1.5</source> 25 <target>1.5</target> 26 <encoding>${project.build.sourceEncoding}</encoding> 27 </configuration> 28 </plugin> 29 30 <!-- This plugin is only to test run our little application. It is not 31 needed in most Shiro-enabled applications: --> 32 <plugin> 33 <groupId>org.codehaus.mojo</groupId> 34 <artifactId>exec-maven-plugin</artifactId> 35 <version>1.1</version> 36 <executions> 37 <execution> 38 <goals> 39 <goal>java</goal> 40 </goals> 41 </execution> 42 </executions> 43 <configuration> 44 <classpathScope>test</classpathScope> 45 <mainClass>Tutorial</mainClass> 46 </configuration> 47 </plugin> 48 </plugins> 49 </build> 50 51 <dependencies> 52 <dependency> 53 <groupId>org.apache.shiro</groupId> 54 <artifactId>shiro-core</artifactId> 55 <version>1.1.0</version> 56 </dependency> 57 <!-- Shiro uses SLF4J for logging. We'll use the 'simple' binding 58 in this example app. See http://www.slf4j.org for more info. --> 59 <dependency> 60 <groupId>org.slf4j</groupId> 61 <artifactId>slf4j-simple</artifactId> 62 <version>1.6.1</version> 63 <scope>test</scope> 64 </dependency> 65 </dependencies> 66 67 </project>
因为咱们的目的是建立一个命令行程序,所以咱们须要先新建一个具备public static void main(String[] args)的Java类。小程序
在和pom.xml所处的同一目录下,建立src/main/java子文件夹。在src/main/java文件夹下建立Tutorial.java文件,文件内容以下:设计模式
src/main/java/Tutorial.java安全
1 import org.apache.shiro.SecurityUtils; 2 import org.apache.shiro.authc.*; 3 import org.apache.shiro.config.IniSecurityManagerFactory; 4 import org.apache.shiro.mgt.SecurityManager; 5 import org.apache.shiro.session.Session; 6 import org.apache.shiro.subject.Subject; 7 import org.apache.shiro.util.Factory; 8 import org.slf4j.Logger; 9 import org.slf4j.LoggerFactory; 10 11 public class Tutorial { 12 13 private static final transient Logger log = LoggerFactory.getLogger(Tutorial.class); 14 15 public static void main(String[] args) { 16 log.info("My First Apache Shiro Application"); 17 System.exit(0); 18 } 19 }
在往下进行以前,先测试一下是否能够运行。服务器
首先进入项目根目录(本教程为shiro-tutorial目录,即pom.xml所在的目录),打开控制台,输入命令:session
mvn compile exec:java
而后你就会看到这个小程序运行起来,而且有以下相似的输出:
Run the Application 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\$
如今咱们已经验证了程序能够运行,接下来让咱们使用Shiro。每次改变程序后,均可以运行mvn compile exec:java运行程序。
在Shiro中有一个很是重要的组件--SecurityManager,Shiro的全部功能几乎都与这个组件相关。这样Java security相似,可是和java.lang.SecurityManager是不同的。
咱们会在后续教程中对SecurityManager作详细介绍。可是如今咱们就要明确一点:SecurityManager是Shrio的核心组件,而且任何一个应用都要有一个SecurityManager才可使用Shiro的其余功能。全部,在本教程中必需要作的事就是实例化一个SecurityManager。
即便咱们能够直接实例化一个SecurityManager,然而SecurityManager仍是有比较多的配置和内部组件的,直接用java代码配置这些内容比较麻烦。经过配置文件进行配置会比较简单。
Shiro提供了一个默认的‘common denominator’,这是一个简单的文本配置文件(INI格式)。与XML格式相比,INI格式更加易读、易用而且几乎不须要任何依赖。INI格式能够轻松的配置SecurityManager。
事实上,因为Shiro彻底兼容了JavaBeans,因此Shiro能够用XML、YAML、JSON、Groovy等不少格式进行配置。
下面咱们用INI文件配置本教程的SecurityManager。首先,在pom.xml所在文件夹下建立src/main/resources文件夹,而后在src/main/resources文件夹下新建一个名为shiro.ini的文件,该文件内容以下:
src/main/resources/shiro.ini
1 # ============================================================================= 2 # Tutorial INI configuration 3 # 4 # Usernames/passwords are based on the classic Mel Brooks' film "Spaceballs" :) 5 # ============================================================================= 6 7 # ----------------------------------------------------------------------------- 8 # Users and their (optional) assigned roles 9 # username = password, role1, role2, ..., roleN 10 # ----------------------------------------------------------------------------- 11 [users] 12 root = secret, admin 13 guest = guest, guest 14 presidentskroob = 12345, president 15 darkhelmet = ludicrousspeed, darklord, schwartz 16 lonestarr = vespa, goodguy, schwartz 17 18 # ----------------------------------------------------------------------------- 19 # Roles with assigned permissions 20 # roleName = perm1, perm2, ..., permN 21 # ----------------------------------------------------------------------------- 22 [roles] 23 admin = * 24 schwartz = lightsaber:* 25 goodguy = winnebago:drive:eagle5
如你所见,这个文件设置了一些基本的用户账户,这对本教程已经足够了。在后续章节中,你将会学习如何使用更加复杂的用户数据源,如数据库、LDAP、ActiveDirectory等。
如今咱们已经有了一个INI文件,这样咱们就能够建立一个SecurityManager对象了。将Tutorial.java文件中的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引入到咱们的项目中了。能够用mvn compile exec:java检测一下程序是否能够运行。
在上述代码中,咱们作了三件事:
如今SecurityManager已经设置好了,咱们终于能够作一些真正地与安全相关的操做了、
当咱们考虑应用的安全性时,一般会遇到的问题是“当前用户是谁?”,“当前用户能够作什么?”因此,应用的安全性工做主要创建在当前用户之上。在shiro API中用Subject这个含义更广的概念代替当前用户这个概念。
几乎在任何环境中,你均可以经过下述代码得到当前正在执行程序的用户。
Subject currentUser = SecurityUtils.getSubject();
使用SecurityUtils.getSubject方法,咱们能够得到当前正在执行程序的Subject。咱们并不称之为前正在执行程序的用户,由于用户一般是指人,而Subject能够指人、进程、计划任务、守护进程等。准确的说,Subject指的是“当前和软件交互的事物”。在多数场景中,你能够将Subject粗暴地认为是用户。
在一个独立的程序中,getSubject()函数基于应用内存中的用户数据返回一个Subject,在一个服务器环境中(如web应用),Subject一般是基于与当前进程有关的用户数据或是来到的请求。
既然咱们已经有了Subject,咱们能够拿它来作什么?
你能够获取当前Session并存储一些东西。
Session session = currentUser.getSession();
session.setAttribute( "someKey", "aValue" );
这里的Session是基于Shiro的,它的功能与HttpSession相似,可是有一个巨大的不一样:它不须要HTTP环境!
若是在web应用中部署shiro,则Session默认就是基于HttpSession的。可是在非web应用中,好比本教程,Shiro会自动用它的Enterprise Session Managerment。这样你就可使用同样的API而不用管部署环境是什么了。
如今咱们已经得到了Subject和它的Session,那么怎么用这些东西去检测Subject是否具备某权限、某许可呢?
咱们只能对当前用户检测这些东西。咱们的Subject对象就是当前用户,可是Subject是谁?不知道,它是匿名的,除非它至少登陆过一次,不然咱们无从得知Subject是谁。因此,咱们让Subject登陆:
1 if ( !currentUser.isAuthenticated() ) { 2 //collect user principals and credentials in a gui specific manner 3 //such as username/password html form, X509 certificate, OpenID, etc. 4 //We'll use the username/password example here since it is the most common. 5 UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa"); 6 7 //this is all you have to do to support 'remember me' (no config - built in!): 8 token.setRememberMe(true); 9 10 currentUser.login(token); 11 }
如今,Subject已经登陆了。
若是登陆失败,咱们能够捕获异常而且作相应处理。
1 try { 2 currentUser.login( token ); 3 //if no exception, that's it, we're done! 4 } catch ( UnknownAccountException uae ) { 5 //username wasn't in the system, show them an error message? 6 } catch ( IncorrectCredentialsException ice ) { 7 //password didn't match, try again? 8 } catch ( LockedAccountException lae ) { 9 //account for that username is locked - can't login. Show them a message? 10 } 11 ... more types exceptions to check if you want ... 12 } catch ( AuthenticationException ae ) { 13 //unexpected condition - error? 14 }
这里有不少不一样类型的异常,你也能够自定义本身的异常。
到这一步,咱们已经有了一个登陆过的用户,咱们能够来作些什么呢?
咱们来看看它是谁:
//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."); }
咱们也能够进行一些insstance-level(实例级别)的许可检测。即检测用户是否被容许访问某些实例。
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.
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,而且了解Subject和SecurityManager这两个基本概念。
可是这只是一个很是很是简单的应用。你可能会问“若是我不想要INI而想用更加复杂的数据源该怎么作?”
为了解答这个问题,咱们须要更加深刻地了解一下shiro的结构,我门将在后面的章节中学习。