Apache Shiro 1.2.0 was released on Tuesday, January 24 2012 with a lot of new features and improvements that most of the community will find useful. Thanks to everyone who contributed to this release; it was a significant undertaking and reflects a big step forward for the project.

In this article, we’ll break the improvements up into four categories - Tools, Core, Web, and Support Modules - and cover them below.

Tools

Previously Apache Shiro has been exclusively an embedded application framework. The 1.2 retains this design goal, but it adds a very small convenience command-line cryptographic hashing program. Many will find this program useful when configuring Shiro-enabled applications or when needing general purpose hashing behavior on any JVM-enabled machine.

Command-Line Hasher

The new shiro-tools-hasher-1.2.0-cli.jar file has been provided which is an executable jar program. You can run it by executing the following:

> java –jar shiro-tools-hasher-1.2.0-cli.jar

This command will print out a full list of instructions and options. The program can be used to hash almost anything: standard input, passwords, text strings, byte data, and any file or URL-based resource. While you can run the command above to see the full list of configuration options, here are some examples:

Password Hashing (the –p/–password flag):

> java –jar shiro-tools-hasher-1.2.0-cli.jar -p
Password to hash:
Password to hash (confirm):

By default, this will generate a SHA-256 hash using 500,000 hash iterations and a 128-bit securely randomly generated salt, printed in a string safe to store in a file or database. The Hash algorithm, number of iterations and salt size or salt source are configurable of course.

File checksums (the –r/–resource flag):

> java –jar shiro-tools-hasher-1.2.0-cli.jar –r RESOURCE_PATH

Where RESOURCE_PATH is a file, url, or classpath location of a document for which to compute the checksum. By default an MD5 hash (checksum) is calculated, but you can specify another JVM-recognized hash algorithm name with the –a/--algorithm option.

We’ll soon see how to use this program to securely represent passwords in a shiro.ini file or other text-based configuration file.

Core

A few nice features were added to Shiro’s core API as well.

PasswordService

Before Shiro 1.2 it was easy enough to generate password hashes. For example:

String digestString =
    new Sha256Hash(password, salt, numIterations).toBase64();

While this is incredibly easy compared to practically any other alternatives for the JVM, it is still a general-purpose hashing solution.

It became clear that it would be even nicer to abstract Hash logic and corresponding configuration (secure random salt generation, iterations, hash algorithm name, etc.) to a dedicated component. This way, the application developer only needed to worry about a single user-submitted value: the password and only the password. All hashing and configuration concerns could be internalized in a component and reused easily in the application.

The new PasswordService was created to satisfy this need. For example:

import org.apache.shiro.authc.credential.*;
…

String rawPassword = “test”;
PasswordService svc = new DefaultPasswordService();

//e.g. during account signup or password reset:
String encrypted = svc.encryptPassword(rawPassword);

//e.g. during login:
assert svc.passwordsMatch(rawPassword, encrypted);

You can configure the PasswordService instance with hashing strategies and re-use that instance where necessary.

PasswordMatcher

To authenticate users, Shiro already supports credentials comparison for Realms with its CredentialsMatcher concept. But for comparing passwords, a new component, the PasswordMatcher (view source) has been introduced. The PasswordMatcher is an implementation of the CredentialsMatcher interface that uses a PasswordService to perform password comparisons during authentication.

Realm configuration

To configure a Realm for password matching, you would configure a password matcher, and ensure the matcher can use a password service. For example, if using shiro.ini configuration:

[main]
…
passwordMatcher = org.apache.shiro.authc.credential.PasswordMatcher
passwordService = org.apache.shiro.authc.credential.DefaultPasswordService
#config the passwordService w/ hashing strategies as necessary

passwordMatcher.passwordService = $passwordService
…
myRealm.credentialsMatcher = $passwordMatcher

Ensure the AuthenticationInfo instance supplied by your Realm returns the encrypted password string from its getCredentials() implementation. That is:

authenticationInfo.getCredentials() === encrypted_password

Text-configured User Accounts

If using text configuration to define a static list of user accounts, you can use the new PasswordMatcher and the aforementioned Command-Line Hasher to securely store user passwords in text configuration directly.

For example, let’s say we want to create a new user account with username ‘jsmith’. Using Shiro’s INI user account configuration as an example, we can enable the account in 3 steps:

  1. Enable the PasswordMatcher on the implicit iniRealm
    [main]
    …
    passwordMatcher = org.apache.shiro.authc.credential.PasswordMatcher
    passwordService = org.apache.shiro.authc.credential.DefaultPasswordService
    passwordMatcher.passwordService = $passwordService
    iniRealm.credentialsMatcher = $passwordMatcher
    …
    
  2. Hash the user password

    Use the Command-Line Hasher (covered above):

    > java –jar shiro-tools-hasher-1.2.0-cli.jar –p
    Password to hash: test
    Password to hash (confirm): test
    $shiro1$...(cut for brevity)
    
  3. Add the user

    Copy and paste the Command-Line Hasher’s output into the [users] section as a new user definition line:

    [users]
    …
    jsmith = $shiro1$...(cut for brevity)
    

And that’s it! When the ‘jsmith’ user enters ‘test’ in a login form password field (for example), their authentication will succeed.

Note that this is just an example for INI-based configuration. The Command-Line Hasher’s password output is safe for any text-based location (e.g. spring.xml, database column, etc).

Authentication Caching

Authentication Caching is a new feature that was added to support applications that perform repeated authentication of the same accounts in a relatively short period of time. This is often done in stateless architectures such as REST or SOAP-based applications that authenticate every request.

When authenticating every request, it might be too computationally expensive to query a data store for account information every time the application services a request. Authentication caching was added to allow certain Realms to cache AuthenticationInfo (account data) lookups for future authentication attempts to alleviate strain on the underlying account data store.

To enable authentication caching in Apache Shiro 1.2, you must do 3 things:

  1. Your Realm implementation must subclass the AuthenticatingRealm class (or one of its subclasses such as the more commonly used AuthorizingRealm).
  2. You must configure the Shiro SecurityManager with a general-purpose CacheManager or configure the Realm with a specific CacheManager. For example:
    [main]
    cacheManager = com.foo.some.CacheManager
    …
    # configure a CacheManager for only a particular realm:
    # myRealm.cacheManager = $cacheManager
    # or, configure a CacheManager for all of Shiro’s needs:
    securityManager.cacheManager = $cacheManager
    
  3. Enable authentication caching for the realm.
    [main]
    …
    myRealm.authenicationCachingEnabled = true
    …
    

As explained in the AuthenticatingRealm JavaDoc, enable authenticationCaching ONLY if you know it is safe to do so.

Web

Prior to Shiro 1.2, Shiro’s SecurityManager initialization was performed in a Servlet Filter. This meant that any application that needed Shiro’s APIs at startup before the Shiro Filter initialized could not function effectively. Also, any application that might have wanted access to configuration objects other than just the SecurityManager could not do so, because the Shiro Filter only exposed just the SecurityManager object to the application. These two concerns have been addressed in Shiro 1.2 by initializing Shiro with a Servlet Context Listener and the introduction of the new Environment and WebEnvironment API.

Initialization and Environment

To allow applications to have access to all Shiro-related objects at startup (and not just the SecurityManager), new Environment (and web-specific WebEnvironment) interfaces were introduced. An Environment instance allows an application developer to obtain anything Shiro-related if necessary, for example, configured objects like a PasswordService instance. The Shiro Filter still exists to filter and secure requests, but now the Filter relies on objects in the WebEnvironment instead of being responsible for constructing them itself.

The following web.xml snippet is how you enable Apache Shiro 1.2 and later in a web application:


    
        org.apache.shiro.web.env.EnvironmentLoaderListener
    

    ShiroFilter
    org.apache.shiro.web.servlet.ShiroFilter


    ShiroFilter
    /*

By default this configuration assumes Shiro is configured with a shiro.ini file located either at /WEB-INF/shiro.ini or at the root of the classpath. You can also control where the file resides, use another configuration format entirely, or even how to specify custom WebEnvironment implementations. Please see the Shiro web documentation for details.

Accessing the WebEnvironment

When the Shiro Servlet Context Listener initializes, it will place the WebEnvironment instance in the ServletContext so it may be accessed by the application at any time. You can access the WebEnvironment and the objects it contains by using Shiro’s WebUtils class:

import org.apache.shiro.web.util.WebUtils;
…
ServletContext servletContext = //get your apps’s servlet context
WebEnvironment webEnv = WebUtils.getRequiredWebEnvironment(servletContext);

Easily Enable or Disable Filters

Typically most developers disable a filter by simply removing it from a filter chain. But for filter chains enforcing security, this can be a pain – you often want to leave the chain alone and only turn on or off filters as necessary.

For example, perhaps you are using Shiro’s ssl filter to ensure that web requests for a filter chain are only allowed if the channel is secure (HTTPS). You probably always want to enforce this in production, but in development, setting up a private SSL certificate and configuring it in your dev web server can be a real chore. So, while you would like to always ensure the SSL filter is enabled in production, you’d like to disable it in development.

Any servlet filter that extends the OncePerRequestFilter can be enabled or disabled via the enabled property. All of Shiro’s out-of-the-box filters extend this class and can therefore be enabled or disabled accordingly. You can subclass this filter for your own filters to support the same behavior. In future Shiro versions, this behavior will likely be supported for any filter, even those that don’t subclass OncePerRequestFilter.

The following is a shiro.ini example of how you might support the ssl use case described above:

[main]
…
# uncomment the following line to disable the ‘ssl’ filter:
# ssl.enabled = false

[urls]
/securedEndpoint/** = ssl, someFilter, anotherFilter, ...

By uncommenting the ssl.enabled = false line in development, the /securedEndpoint/** filter chain will not enforce SSL communication. Commenting the line will enforce SSL again.

LogoutFilter

The LogoutFilter was added to support a very simple but common use case: when a user logs out, typically most apps will simply want to call subject.logout() and redirect the user to a default ‘after logout’ page (e.g. a thank you page, the login page, etc). Shiro 1.2 now supports this common flow with the LogoutFilter.

Example shiro.ini configuration that redirects to the login page with a status message:

[main]
...
logout.redirectUrl = /login.jsp?status=loggedOut

[urls]
# requests to /logout will be handled by the ‘logout’ filter
# configured above:
/logout = logout

Enforcing Statelessness: The NoSessionCreationFilter

This is a simple but extremely useful filter, especially for enforcing stateless application architectures like those with REST and/or SOAP endpoints. The NoSessionCreationFilter, available as the default noSessionCreation filter, will prevent a new session from being created at any point further down in the filter chain.

If the noSessionCreation filter is the very first filter in your filter chains, you don’t have to search through code to figure out if someone or something might be creating sessions unexpectedly – the noSessionCreation filter will explicitly prevent it.

The noSessionCreation filter will enforce that a new session cannot be created at a later point during request execution. It does not have any effect if a session has been created prior to a Subject visiting a filter chain. For example, in shiro.ini:

[urls]
/rest/** = noSessionCreation, someFilter, anotherFilter, ...

This guarantees that if anything in the /rest/** filter chain (another filter, an MVC framework, etc) tries to create a new session, an exception will be thrown. More concretely, if a session does not exist, any call to the following four methods downstream will throw a DisabledSessionException:

  • subject.getSession()
  • subject.getSession(true)
  • httpServletRequest.getSession()
  • httpServletRequest.getSession(true)

If a session already exists before the filter chain is executed, the above methods are allowed.

Finally, calls to the following two methods are allowed in all cases:

  • subject.getSession(false)
  • httpServletRequest.getSession(false)

The NoSessionCreationFilter can be leveraged with the aforementioned Authentication Caching support to great effect to efficiently secure modern stateless REST-enabled applications.

New Support Modules

Apache Shiro has a number of support modules representing integration with 3rd-party APIs, such as the Spring Framework and AspectJ. Shiro 1.2 introduces three new support modules. While a detailed description of each module is out of scope for this overview, please see the linked documentation to learn more:

Get Started with Stormpath

User Management API for Developers