Introducing Apache Shiro

Use Apache Shiro for user authentication for your web application

Shiro is an Apache Incubator project aimed at making authentication and authorization easier. Get to know Apache Shiro and walk through examples that let you experiment with Shiro for authentication and authorization in a Groovy web application.

Nathan A. Good, Senior Information Engineer, Freelance Developer

Nathan GoodNathan A. Good lives in the Twin Cities area of Minnesota. Professionally, he does software development, software architecture, and systems administration. When he's not writing software, he enjoys building PCs and servers, reading about and working with new technologies, and trying to get his friends to make the move to open source software. He's written and co-written many books and articles, including Professional Red Hat Enterprise Linux 3, Regular Expression Recipes: A Problem-Solution Approach, and Foundations of PEAR: Rapid PHP Development.



14 September 2010

Also available in Chinese Russian Japanese

Apache Shiro is a framework that you can use for authentication and authorization. This article gives a few examples of how to use Shiro in a Java™ application and provides an overview of how to use it in a Grails web application. To get the most out of this article, you should be comfortable creating Java applications and have the following components installed:

  • A Java 1.6 JDK
  • Grails (to run the web application examples)

Frequently used acronyms

  • API: Application programming interface
  • HTTP: Hypertext Transfer Protocol
  • JAR: Java archive
  • JDBC: Java database connectivity
  • JDK: Java software development kit
  • LDAP: Lightweight Directory Access Protocol

Authentication and authorization

When securing systems, two elements of security are important: authentication and authorization. Though the two terms mean different things, they are sometimes used interchangeably because of their respective roles in application security.

Authentication deals with verifying a user's identity. When you authenticate users, you confirm that they really are who they claim to be. In most applications, authentication is done through a combination of a user name and password. As long as users choose passwords that are sufficiently difficult for others to guess, the combination of a user name and password is usually enough to establish identity. However, other means of authentication, such as fingerprints, certificates, and generated keys, are also available.

Once the authentication process successfully establishes identity, authorization takes over to restrict or grant access. It is possible that through authentication, a user can log in to a system but, through authorization, not be allowed to do anything. It is also possible to have a certain level of authorization for users that have not been authenticated.

When planning the security model for your application, you must address both elements to make sure your system has a sufficient level of security. Authentication is a common problem across applications (especially when done with only a user name and password), so letting a framework handle the work is a good idea. Proper frameworks offer the advantage of being tested and maintained, letting you focus on your business problem instead of re-solving a problem whose solution has already been achieved.

Apache Shiro offers a usable security framework that a variety of clients can apply to their applications. The examples in this article introduce Shiro and focus on the basic task of authenticating a user.


Getting to know Shiro

Shiro is a framework implemented in the Java language that provides both authentication and authorization in an easy-to-use API. By using Shiro, you can provide security for your application without writing all of the code from the beginning.

Total authentication solution from IBM

The white paper "Enhancing Identity Assurance across all Access Scenarios Cost Effectively with IBM ISS Identity and Access Management Services" explores a fully integrated, enterprise-wide, and cost-effective authentication solution across various business applications and access scenarios.

The solution delivers a highly secure, centralized authentication infrastructure that can be easily integrated with various applications or IT infrastructure components through its API or standard authentication protocols like RADIUS or LDAP. It supports a wide range of hardware, software, or mobile phone-based authentication tokens that give maximum flexibility in choosing a specific authentication method.

Because Shiro offers authentication with so many different data sources, as well as Enterprise Session Management, it's ideal for implementing single sign-on (SSO)—a desirable feature in large enterprises where users routinely log in to and use many different systems in one day. These data sources include JDBC, LDAP, Kerberos, and Microsoft® Active Directory® Directory Services (AD DS).

Shiro's Session object lets you use a user's session without having an HttpSession. By using a generic Session object, you can use the same code even if that code is not running inside a web application. By avoiding the requirement of application server or web application server session management, you can use Shiro even in command-line environments. In other words, the code that you write using Shiro's API lets you build command-line applications that connect to an LDAP server and is the same code inside a web application that accesses the LDAP server.

Downloading and installing Shiro

Shiro comes in pre-built binary distributions. You can either download the Shiro JAR files or put entries into Apache Maven or Apache Ivy to have the files installed automatically. This example uses Ivy to download the Shiro JAR files and other required libraries using the simple scripts shown in Listing 1.

Listing 1. The Apache Ivy file and Apache Ant script
<?xml version="1.0" encoding="UTF-8"?>
<ivy-module version="2.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
    <info organisation="com.nathanagood.examples" module="shirotest" />
    <configurations>
        <conf name="dist" description="Dependency configuration for distribution." />
    </configurations>
    <dependencies>
        <dependency org="commons-logging" name="commons-logging"
            rev="1.1.1" conf="dist->default" />
        <dependency org="org.slf4j" name="slf4j-log4j12" rev="1.5.8"
            conf="dist->default" />
        <dependency org="org.apache.shiro" name="shiro-core" rev="1.0.0-incubating"
            conf="dist->default" />
        <dependency org="org.apache.shiro" name="shiro-web" rev="1.0.0-incubating"
            conf="dist->default" />
    </dependencies>
</ivy-module>

<project name="shiroTestApp" default="usage" basedir="." 
    xmlns:ivy="antlib:org.apache.ivy.ant">
    <property name="project.lib" value="lib" />
    <path id="ivy.task.path">
        <fileset dir="${basedir}/ivy-lib">
            <include name="**/*.jar" />
        </fileset>
    </path>

    <target name="resolve">
        <taskdef resource="org/apache/ivy/ant/antlib.xml" 
            uri="antlib:org.apache.ivy.ant" classpathref="ivy.task.path" />
        <ivy:resolve />
        <ivy:retrieve pattern="${project.lib}/[conf]/[artifact].[ext]" sync="true" />
    </target>

    <target name="usage">
        <echo message="Use --projecthelp to learn more about this project" />
    </target>
</project>

See Resources for more information about using Ivy. If you don't use Maven or Ivy, download the Shiro JAR files from the download site, which is also provided in Resources.

Once you download the libraries, simply add them to your CLASSPATH. Write the simple code shown in Listing 2, which obtains a reference to the current user and reports that the user is not authenticated. (You use the Subject class to represent the user.)

Listing 2. The ShiroTest Java class
package com.nathanagood.examples.shirotest;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ShiroTest {

    private static Logger logger = LoggerFactory.getLogger(ShiroTest.class);

    public static void main(String[] args) {
        // Using the IniSecurityManagerFactory, which will use the an INI file
        // as the security file.
        Factory<org.apache.shiro.mgt.SecurityManager> factory = 
            new IniSecurityManagerFactory("auth.ini");

        // Setting up the SecurityManager...
        org.apache.shiro.mgt.SecurityManager securityManager 
            = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);

        Subject user = SecurityUtils.getSubject();

        logger.info("User is authenticated:  " + user.isAuthenticated());
    }
}

Once you add this code, create a file called auth.ini. For now, this file is blank; it exists only so that you can run the example here to check that the code is working correctly.

After you create the file, run your sample. You should see output that contains an INFO logging message, which reports that the user is not logged in.

The SecurityUtils object is a singleton, which means that different objects can use it to gain access to the current user. Once you successfully set the SecurityManager, you can call SecurityUtils.getSubject() in different parts of your application to get the current user's information.


User tokens

In Shiro terms, a token is a key that you use to log in to a system. A basic and common token is the UsernamePasswordToken, which lets you specify a name and password for a user.

The UsernamePasswordToken class implements the AuthenticationToken interface, which provides a way to get credentials and the user's principal (the account identity). The UsernamePasswordToken works for most applications, and you can also extend the AuthenticationToken interface as needed to include your own method of getting credentials. For example, you can extend the interface to provide the contents of a key file that your application uses to authenticate users.


Simple authentication

So far, this simple example has covered starting the Shiro SecurityManager, getting the current user, and logging that the user isn't authenticated yet. This example will use the UsernamePasswordToken, along with a user record stored in an INI file, to show authentication through a user name and password.

The example auth.ini file shown in Listing 3 now contains a record for the user; this record contains both the user name and password. You can define roles in this record as well provide authorization for your application.

Listing 3. The auth.ini file
[users]
bjangles = dance

Now, create the UsernamePasswordToken object introduced in the previous section, as shown in Listing 4.

Listing 4. Using the UsernamePasswordToken class
// snipped... same as before.
public class ShiroTest {

    private static Logger logger = LoggerFactory.getLogger(ShiroTest.class);

    public static void main(String[] args) {
        // Using the IniSecurityManagerFactory, which will use the an INI file
        // as the security file.
        Factory<org.apache.shiro.mgt.SecurityManager> factory = 
            new IniSecurityManagerFactory("auth.ini");

        // Setting up the SecurityManager...
        org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);

        Subject user = SecurityUtils.getSubject();

        logger.info("User is authenticated:  " + user.isAuthenticated());
        
        UsernamePasswordToken token = new UsernamePasswordToken("bjangles", "dance");
        
        user.login(token);
        
        logger.info("User is authenticated:  " + user.isAuthenticated());
    }
}

The UsernamePasswordToken object is instantiated with the user name and password combination. Then the token is passed into the Subject class' login() method.

Run the example again. Notice that the logging message now reports that the user is authenticated.

To make sure that the code is working properly and that you aren't getting a false positive, change either the password in the code or the INI file and run the example again. The login() method now throws an IncorrectCredentialsException. This exception will be caught specifically in your production code so that your application can respond graciously when a user supplies an incorrect password.

If the user is incorrect, the login() method throws an UnknownAccountException. You might want to consider handling this exception but opt not to give the user too much information. One common practice is to deny the user any indication that the user name is valid but that the password is incorrect. If someone is attempting to gain access by guessing, you don't want to give that person hints that the guessed user name is correct.


Authentication with LDAP

LDAP is a protocol for querying a directory over TCP/IP. These directories can hold any amount of information about a user, including a user ID, contact information, and group membership, among other things. LDAP directories are useful for corporate address books and are widely used.

AD DS, which is a common directory used for user and group management, supports LDAP. Shiro does not include a generic LDAP security realm, but it does include an ActiveDirectoryRealm object that lets you authenticate users against LDAP. This example uses the ActiveDirectoryRealm object configured in an INI file to authenticate users. Though AD DS is not the same thing as LDAP, there is no generic LDAP object that comes with the version of Shiro used in this article.

Getting an LDAP server to test this sample against can be considerably more work than writing and running the example itself. If you don't have access to an AD DS server, consider downloading and installing Apache Directory to provide a sample LDAP server implementation. Apache Directory is written in the Java language. Similarly, Apache Active Directory Studio is an Eclipse plug-in that lets you browse the LDAP data. It also comes with some sample data, which provides a quick way for you to write code against known values without wondering whether any problems you encounter are code or data problems.

Listing 5 shows the code used to authenticate a user stored in Apache Directory.

Listing 5. Authenticating against LDAP
// snipped...
public class ShiroLDAPTest {

    private static Logger logger = LoggerFactory.getLogger(ShiroLDAPTest.class);

    /**
     * @param args
     */
    public static void main(String[] args) {

        // Using the IniSecurityManagerFactory, which will use the an INI file
        // as the security file.
        Factory<org.apache.shiro.mgt.SecurityManager> factory = 
            new IniSecurityManagerFactory("actived.ini");

        // Setting up the SecurityManager...
        org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);

        Subject user = SecurityUtils.getSubject();

        logger.info("User is authenticated:  " + user.isAuthenticated());

        UsernamePasswordToken token = 
        new UsernamePasswordToken(
            "cn=Cornelius Buckley,ou=people,o=sevenSeas", "argh");

        user.login(token);

        logger.info("User is authenticated:  " + user.isAuthenticated());
    }
}

Aside from the name of the INI file and the user name and password, the code is identical to that used to authenticate a user using the records in an INI file. This similarity results because you can configure Shiro using the INI file. The INI records used to set up Shiro for authentication against Apache Directory are shown in Listing 6.

Listing 6. The actived.ini file
[main]
activeDirectoryRealm = org.apache.shiro.realm.activedirectory.ActiveDirectoryRealm
activeDirectoryRealm.systemUsername = uid=admin,ou=system
activeDirectoryRealm.systemPassword = secret
activeDirectoryRealm.searchBase = o=sevenSeas,ou=people
activeDirectoryRealm.url = ldap://localhost:10389

Note: I used Apache Directory Studio to change the user's password to a value that I could put into the test code to make sure it worked.


Running a Grails web application

You can apply Shiro to two basic techniques in a web application. First, you can simply use the API to incorporate a version of the code presented here in a base servlet. Second, you can use the HTTP filters that come with Shiro. This example demonstrates the second technique because using filters takes advantage of built-in web application server technology and pre-written code from the Shiro project.

This example shows how to use the filters in Grails. Grails is a project aimed at allowing you to write Groovy web applications as quickly as possible, using a convention-over-configuration approach. See Resources for more information on Grails.

You would usually add the necessary filter entries to the web.xml file for Shiro's filters manually. However, Grails generates the web.xml file each time you start your application, so it isn't necessary to modify a web.xml file by hand.

Fortunately, Grails supports plug-ins that hook into the web.xml generation process and let you be involved in writing the entries in the web.xml file. Many plug-ins are already available for Grails, including one for Shiro. Try the Shiro Grails plug-in, which provides a few new scripts that you can run to create different realms and controllers.

Alternatively, you can write your own plug-in if you prefer to add the entries and do the configuration yourself. With Grails, writing a new plug-in is easy. To create a Grails plug-in that adds the necessary Shiro filter entries to your web.xml file, begin by using the following command:

> grails create-plugin ShiroWebXml

Once you create the plug-in project, edit the ShiroWebXmlPlugin.groovy file, and add the code shown in Listing 7.

Listing 7. The sample plug-in
class ShiroWebXmlPlugin {
   
   // snipped plugin details... 

   def doWithWebDescriptor = { xml ->

       def filterElement = xml.'filter'
       def lastFilter = filterElement[filterElement.size() - 1]

       lastFilter + {
           'filter' {
               'filter-name'("ShiroFilter")
               'filter-class'("org.apache.shiro.web.servlet.IniShiroFilter")
               'init-param' {
                   'param-name'("config")
                   'param-value'("\n#config")
               }
           }
       }

       def filterMappingElement = xml.'filter-mapping'
       def lastFilterMappingElement = 
           filterMappingElement[filterMappingElement.size() - 1]

       lastFilterMappingElement + {
           'filter-mapping' {
               'filter-name'("ShiroFilter")
               'url-pattern'("/*")
               }
           }
       }
}

This code runs when Grails executes the web.xml file.

Before starting the plug-in application to test it, copy the Shiro JAR files that you downloaded with Ivy into the lib folder of the plug-in. With the JAR files in place, test whether the plug-in works with the following command:

grails run-app

If the plug-in application starts successfully, you can package it for use in an example project. To package the plug-in, use the following command:

grails package-plugin

You can install the new ShiroWebXmlPlugin with the following command:

cd myapp
grails install-plugin /path/to/shiro-web-xml-1.0.zip

Troubleshooting

If you get an UnavailableSecurityManagerException, then the SecurityManager has not been properly set up. Make sure that it is set on the SecurityUtils object before calling the getSubject() method.

Connecting to an LDAP server can be difficult. If you get a javax.naming.CommunicationException, verify the host name and port of the LDAP server. Even if you don't use Apache Directory, Apache Directory Studio (which you can install separately) can help you troubleshoot connection problems and issues with names.

If you didn't use the Ant Ivy script to initialize your environment, you might get errors about missing classes. The INI file example runs even without the Apache Commons Logging library (commons-logging), but running the LDAP example results in an exception. Use Apace Commons BeanUtils to parse the INI file and set values on objects.


Conclusion

Shiro is a framework in the Apache Incubator that lets you add authentication and authorization to your applications. It supports different authentication stores, such as LDAP, Kerberos, and AD DS. The combination of Shiro's minimal dependencies and its relative ease of configuration makes it a good option as a security framework in your application.

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Web development on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development
ArticleID=521316
ArticleTitle=Introducing Apache Shiro
publish-date=09142010