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.
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.
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:
> 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.
> java –jar shiro-tools-hasher-1.2.0-cli.jar –r RESOURCE_PATH
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
We’ll soon see how to use this program to securely represent passwords in a
shiro.ini file or other text-based configuration file.
A few nice features were added to Shiro’s core API as well.
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.
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.
[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
authenticationInfo.getCredentials() === encrypted_password
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:
- 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 …
- 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)
- 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 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:
- Your Realm implementation must subclass the
AuthenticatingRealmclass (or one of its subclasses such as the more commonly used
- You must configure the Shiro SecurityManager with a general-purpose
CacheManageror 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
- 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.
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
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.
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.
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);
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
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 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
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
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.
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
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:
NoSessionCreationFilter can be leveraged with the aforementioned Authentication Caching support to great effect to efficiently secure modern stateless REST-enabled applications.
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: