Apache Shiro Reference Documentation
1.Overview
- 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</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.8</source>
<target>1.8</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.3.2</version>
</dependency>
<!-- Shiro uses SLF4J for logging. We'll use the 'simple' binding
in this example app. -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.6.1</version>
<scope>test</scope>
</dependency>
</dependencies> </project> - src\main\resources\shiro.ini
# =============================================================================
# Tutorial INI configuration
#
# Usernames/passwords are based on the classic Mel *s' film "Spaceballs" :)
# ============================================================================= # -----------------------------------------------------------------------------
# Users and their (optional) assigned roles
# username = password, role1, role2, ..., roleN
# -----------------------------------------------------------------------------
[users]
root = secret, admin
agror = agror, goodguy, schwartz
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 - 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"); //1.
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); //2.
SecurityManager securityManager = factory.getInstance(); //3.
SecurityUtils.setSecurityManager(securityManager); //4.
Subject currentUser = SecurityUtils.getSubject();
//Session session = currentUser.getSession();
//session.setAttribute( "someKey", "aValue" );
//session.setAttribute( "argor", "argor" ); //5.
// Login
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");
UsernamePasswordToken token = new UsernamePasswordToken("agror", "agror"); //this is all you have to do to support 'remember me' (no config - built in!):
token.setRememberMe(true); try {
currentUser.login( token );
//if no exception, that's it, we're done! //print their identifying principal (in this case, a username):
log.info( "User [" + currentUser.getPrincipal() + "] logged in successfully." ); //6.
TestAuthorization( currentUser );
} 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?
} catch ( AuthenticationException ae ) {
//unexpected condition - error?
} finally {
log.info( "User login status: [- " + currentUser.isAuthenticated() + " -]." ); // Logout
currentUser.logout();
//removes all identifying information and invalidates their session too.
}
} System.exit(0);
} private static void TestAuthorization ( Subject currentUser ) {
//6.
// We can also test to see if they have specific role or not:
if ( currentUser.hasRole( "schwartz" ) ) {
log.info( currentUser.getPrincipal() + " May the Schwartz be with you!" );
} else {
log.info( currentUser.getPrincipal() + " , mere mortal, has't schwartz." );
} if ( currentUser.hasRole( "goodguy" ) ) {
log.info( currentUser.getPrincipal() + " May the goodguy be with you!" );
} else {
log.info( currentUser.getPrincipal() + " , mere mortal, has't goodguy." );
} if ( currentUser.hasRole( "admin" ) ) {
log.info( currentUser.getPrincipal() + " May the admin be with you!" );
} else {
log.info( currentUser.getPrincipal() + " , mere mortal, has't admin." );
} // We can also see if they have a permission to act on a certain type of entity:
if ( currentUser.isPermitted( "lightsaber:weild" ) ) {
log.info("You may use a lightsaber ring.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
// Also, we can perform an extremely powerful instance-level permission
// check - the ability to see if the user has the ability to access a
// specific instance of a type:
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!");
}
}
} - run
E:\Users\Administrator\Desktop\shiro\shiro-tutorial>mvn compile exec:java
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building First Apache Shiro Application 1.0.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ shiro-tutorial ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO]
[INFO] --- maven-compiler-plugin:2.0.2:compile (default-compile) @ shiro-tutorial ---
[INFO] Compiling 1 source file to E:\Users\Administrator\Desktop\shiro\shiro-tutorial\target\classes
[INFO]
[INFO] >>> exec-maven-plugin:1.1:java (default-cli) > validate @ shiro-tutorial >>>
[INFO]
[INFO] <<< exec-maven-plugin:1.1:java (default-cli) < validate @ shiro-tutorial <<<
[INFO]
[INFO] --- exec-maven-plugin:1.1:java (default-cli) @ shiro-tutorial ---
0 [Tutorial.main()] INFO Tutorial - My First Apache Shiro Application
47 [Tutorial.main()] INFO Tutorial - User login status: [- false -]. E:\Users\Administrator\Desktop\shiro\shiro-tutorial>mvn compile exec:java
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building First Apache Shiro Application 1.0.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ shiro-tutorial ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO]
[INFO] --- maven-compiler-plugin:2.0.2:compile (default-compile) @ shiro-tutorial ---
[INFO] Compiling 1 source file to E:\Users\Administrator\Desktop\shiro\shiro-tutorial\target\classes
[INFO]
[INFO] >>> exec-maven-plugin:1.1:java (default-cli) > validate @ shiro-tutorial >>>
[INFO]
[INFO] <<< exec-maven-plugin:1.1:java (default-cli) < validate @ shiro-tutorial <<<
[INFO]
[INFO] --- exec-maven-plugin:1.1:java (default-cli) @ shiro-tutorial ---
0 [Tutorial.main()] INFO Tutorial - My First Apache Shiro Application
31 [Tutorial.main()] INFO org.apache.shiro.session.mgt.AbstractValidatingSessionManager - Enabling session validation scheduler...
109 [Tutorial.main()] INFO Tutorial - User [agror] logged in successfully.
109 [Tutorial.main()] INFO Tutorial - agror May the Schwartz be with you!
109 [Tutorial.main()] INFO Tutorial - agror May the goodguy be with you!
109 [Tutorial.main()] INFO Tutorial - agror , mere mortal, has't admin.
109 [Tutorial.main()] INFO Tutorial - You may use a lightsaber ring.
109 [Tutorial.main()] INFO Tutorial - You are permitted to 'drive' the 'winnebago' with license plate (id) 'eagle5'. Here are the keys - have fun!
109 [Tutorial.main()] INFO Tutorial - User login status: [- true -].第一次执行时,使用一个错误的密码;第二次执行使用正确的密码;
2.Core
- Authentication
- Authenticating Subjects
Step 1: Collect the Subject’s principals and credentials//Example using most common scenario of username/password pair:
UsernamePasswordToken token = new UsernamePasswordToken(username, password); //"Remember Me" built-in:
token.setRememberMe(true);Step 2: Submit the principals and credentials
Subject currentUser = SecurityUtils.getSubject();
currentUser.login(token);Step 3: Handling Success or Failure
try {
currentUser.login(token);
} catch ( UnknownAccountException uae ) { ...
} catch ( IncorrectCredentialsException ice ) { ...
} catch ( LockedAccountException lae ) { ...
} catch ( ExcessiveAttemptsException eae ) { ...
} ... catch your own ...
} catch ( AuthenticationException ae ) {
//unexpected error?
}<Login Failure Tip> The security best practice is to only show a generic failure message , for example, "Incorrect username or password".
- Logging Out
currentUser.logout(); //removes all identifying information and invalidates their session too.
- Authenticating Subjects
- Authorization
- Understanding Permissions in Apache Shiro
Wildcard Permissions & Checking PermissionsSimple Usage
subject.isPermitted("sshdStart")
subject.isPermitted("sshdStop")
subject.isPermitted("sshdRestart") Multiple Parts
subject.isPermitted("sshd:start")
subject.isPermitted("sshd:stop")
subject.isPermitted("sshd:restart") Multiple Values
subject.isPermitted("sshd:start,stop,restart")
subject.isPermitted("sshd:*") Instance-Level Access Control
subject.isPermitted("sshd:start:root")
subject.isPermitted("sshd:*:root")
subject.isPermitted("sshd:*:*") Missing Parts
subject.isPermitted("sshd:start:")//is equivalent to below
subject.isPermitted("sshd:start:*")
subject.isPermitted("sshd")//is equivalent to below
subject.isPermitted("sshd:*:*") - Elements of Authorization
♠Permission statements reflect behavior (actions associated with resource types) only.
♠A Role is a named entity that typically represents a set of behaviors or responsibilities.
♠A user essentially is the ‘who’ of an application. As we’ve covered previously however, the Subject is really Shiro’s ‘User’ concept. - Authorizing Subjects
- ♠Programmatically
Role-Based AuthorizationSubject currentUser = SecurityUtils.getSubject(); if (currentUser.hasRole("administrator")) {
//show the admin button
} else {
//don't show the button? Grey it out?
}Permission-Based Authorization
Permission printPermission = new PrinterPermission("laserjet4400n", "print"); Subject currentUser = SecurityUtils.getSubject(); if (currentUser.isPermitted(printPermission)) {
//show the Print button
} else {
//don't show the button? Grey it out?
}Subject currentUser = SecurityUtils.getSubject(); if (currentUser.isPermitted("printer:print:laserjet4400n")) {
//show the Print button
} else {
//don't show the button? Grey it out?
}Subject currentUser = SecurityUtils.getSubject(); Permission p = new WildcardPermission("printer:print:laserjet4400n"); if (currentUser.isPermitted(p) {
//show the Print button
} else {
//don't show the button? Grey it out?
} - ♠JDK annotations( <need> to enable AOP support in your application)
The RequiresAuthentication annotation@RequiresAuthentication
public void updateAccount(Account userAccount) {
//this method will only be invoked by a
//Subject that is guaranteed authenticated
...
}
//This is mostly equivalent to the following Subject-based logic: public void updateAccount(Account userAccount) {
if (!SecurityUtils.getSubject().isAuthenticated()) {
throw new AuthorizationException(...);
} //Subject is guaranteed authenticated here
...
}The RequiresGuest annotation
@RequiresGuest
public void signUp(User newUser) {
//this method will only be invoked by a
//Subject that is unknown/anonymous
...
}
//This is mostly equivalent to the following Subject-based logic: public void signUp(User newUser) {
Subject currentUser = SecurityUtils.getSubject();
PrincipalCollection principals = currentUser.getPrincipals();
if (principals != null && !principals.isEmpty()) {
//known identity - not a guest:
throw new AuthorizationException(...);
} //Subject is guaranteed to be a 'guest' here
...
}The RequiresPermissions annotation
@RequiresPermissions("account:create")
public void createAccount(Account account) {
//this method will only be invoked by a Subject
//that is permitted to create an account
...
}
//This is mostly equivalent to the following Subject-based logic: public void createAccount(Account account) {
Subject currentUser = SecurityUtils.getSubject();
if (!subject.isPermitted("account:create")) {
throw new AuthorizationException(...);
} //Subject is guaranteed to be permitted here
...
}The RequiresRoles permission
@RequiresRoles("administrator")
public void deleteUser(User user) {
//this method will only be invoked by an administrator
...
}
//This is mostly equivalent to the following Subject-based logic: public void deleteUser(User user) {
Subject currentUser = SecurityUtils.getSubject();
if (!subject.hasRole("administrator")) {
throw new AuthorizationException(...);
} //Subject is guaranteed to be an 'administrator' here
...
}The RequiresUser annotation
@RequiresUser
public void updateAccount(Account account) {
//this method will only be invoked by a 'user'
//i.e. a Subject with a known identity
...
}
This is mostly equivalent to the following Subject-based logic: public void updateAccount(Account account) {
Subject currentUser = SecurityUtils.getSubject();
PrincipalCollection principals = currentUser.getPrincipals();
if (principals == null || principals.isEmpty()) {
//no identity - they're anonymous, not allowed:
throw new AuthorizationException(...);
} //Subject is guaranteed to have a known identity here
...
}
- ♠Programmatically
- Understanding Permissions in Apache Shiro
3.Integrating into Spring-based Applications
Shiro applications need an application singleton SecurityManager
instance.
- web.xml
Here is how to configure Shiro in a Spring-based web application:<!-- The filter-name matches name of a 'shiroFilter' bean inside applicationContext.xml -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter> ... <!-- Make sure any request you want accessible to Shiro is filtered. /* catches all -->
<!-- requests. Usually this filter mapping is defined first (before all others) to -->
<!-- ensure that Shiro works in subsequent filters in the filter chain: -->
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping> - applicationContext.xml
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<!-- override these for application-specific URLs if you like:
<property name="loginUrl" value="/login.jsp"/>
<property name="successUrl" value="/home.jsp"/>
<property name="unauthorizedUrl" value="/unauthorized.jsp"/> -->
<!-- The 'filters' property is not necessary since any declared javax.servlet.Filter bean -->
<!-- defined will be automatically acquired and available via its beanName in chain -->
<!-- definitions, but you can perform instance overrides or name aliases here if you like: -->
<!-- <property name="filters">
<util:map>
<entry key="anAlias" value-ref="someFilter"/>
</util:map>
</property> -->
<property name="filterChainDefinitions">
<value>
# some example chain definitions:
/admin/** = authc, roles[admin]
/docs/** = authc, perms[document:read]
/** = authc
# more URL-to-FilterChain definitions here
</value>
</property>
</bean> <!-- Define any javax.servlet.Filter beans you want anywhere in this application context. -->
<!-- They will automatically be acquired by the 'shiroFilter' bean above and made available -->
<!-- to the 'filterChainDefinitions' property. Or you can manually/explicitly add them -->
<!-- to the shiroFilter's 'filters' Map if desired. See its JavaDoc for more details. -->
<bean id="someFilter" class="..."/>
<bean id="anotherFilter" class="..."> ... </bean>
... <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- Single realm app. If you have multiple realms, use the 'realms' property instead. -->
<property name="realm" ref="myRealm"/>
<!-- By default the servlet container sessions will be used. Uncomment this line
to use shiro's native sessions (see the JavaDoc for more): -->
<!-- <property name="sessionMode" value="native"/> -->
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> <!-- Define the Shiro Realm implementation you want to use to connect to your back-end -->
<!-- security datasource: -->
<bean id="myRealm" class="...">
...
</bean> - Enabling Shiro Annotations
Here is how to enable these annotations. Just add these two bean definitions toapplicationContext.xml
:<!-- Enable Shiro Annotations for Spring-configured beans. Only run after -->
<!-- the lifecycleBeanProcessor has run: -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
.