Saturday, March 15, 2014

Understanding Apache Shiro

Excerpted from:

What is Apache Shiro?

Apache Shiro (pronounced “shee-roh”, the Japanese word for ‘castle’) is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management and can be used to secure any application - from the command line applications, mobile applications to the largest web and enterprise applications. 

Shiro provides the application security API to perform: 
  • Authentication - proving user identity, often called user ‘login’.
  • Authorization - access control
  • Cryptography - protecting or hiding data from prying eyes - simplifies JCA usage.
  • Session Management - per-user time-sensitive state
I only used Shiro for session management as for our application we already had an existing authentication/authorization framework in place. 

Shiro framework was created in 2003 (so its already 10+ years old at the time of this writing). 

Why Shiro came into being?

1. Shiro was created to overcome the shortcomings of JAAS (Java Authentication and Authorization Service). 

JAAS was heavily tied to Virtual Machine-level security concerns, for example, determining if a class should be allowed to be loaded in the JVM. As an application developer, I cared more about what an application end-user could do rather than what my code could do inside the JVM.

2. Shiro was also created to provide a clean, container-agnostic session mechanism (unlike the HttpSession that requires web container, or EJB Stateful Session Bean which requires EJB container).

Benefits of using Apache Shiro in your project

  1. Easy to use framework
  2. Flexible - you can use Shiro for only session management and by pass its other features if you already have say authentication/authorization custom framework in place. Also Shiro can work in web, EJB and IoC container or standalone Java application.
  3. Web capabilities - Shiro security can be configured for REST based web services quickly and very intuitively by defining URL mappings in shiro.ini.
  4. Pluggable - Shiro integrates easily with other frameworks like Spring, Grails, Vaadin, Wicket etc.
  5. Supported - Both open source community and commercial support available. It is also top level Apache project.
  6. Wide Adoption - Shiro is used in several open source frameworks like Spring, Grails etc. and is widely used by companies of all sizes.

Core Concepts of Shiro

  • Subject - represents the current user
import org.apache.shiro.subject.Subject;
import org.apache.shiro.SecurityUtils;
Subject currentUser = SecurityUtils.getSubject();
Now you can do almost everything you’d want to do with Shiro for the current user, such as login, logout, access their session, execute authorization checks, and more.
  • Security ManagerWhile the Subject represents security operations for the current user, the SecurityManager manages security operations for all users. It is the heart of Shiro’s architecture and acts as a sort of ‘umbrella’ object that references many internally nested security components that form an object graph. However, once the SecurityManager and its internal object graph is configured, it is usually left alone and application developers spend almost all of their time with the Subject API.
There is almost always a single SecurityManager instance per application. It is essentially an application singleton. SecurityManager (and its associated object graph) can be configured in several ways including a text based INI configuration. 

Configuring Shiro with INI
cm = org.apache.shiro.authc.credential.HashedCredentialsMatcher
cm.hashAlgorithm = SHA-512
cm.hashIterations = 1024
# Base64 encoding (less text):
cm.storedCredentialsHexEncoded = false
iniRealm.credentialsMatcher = $cm
[users] jdoe = TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJpcyByZWFzb2 asmith = IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbXNoZWQsIG5vdCB
There are two INI sections: [main] and [users].
The [main] section is where you configure the SecurityManager object and/or any objects (like Realms) used by the SecurityManager.
The [users] section is where you can specify a static list of user accounts - convenient for simple applications or when testing.

Loading shiro.ini Configuration File
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.util.Factory;
//1. Load the INI configuration Factory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//2. Create the SecurityManager SecurityManager securityManager = factory.getInstance();
//3. Make it accessible SecurityUtils.setSecurityManager(securityManager);
  • Realm A Realm acts as the ‘bridge’ or ‘connector’ between Shiro and your application’s security data. That is, when it comes time to actually interact with security-related data like user accounts to perform authentication (login) and authorization (access control), Shiro looks up many of these things from one or more Realms configured for an application.
In this sense a Realm is essentially a security-specific DAO: it encapsulates connection details for data sources and makes the associated data available to Shiro as needed. When configuring Shiro, you must specify at least one Realm to use for authentication and/or authorization. 

Shiro provides out-of-the-box Realms to connect to a number of security data sources (aka directories) such as LDAP, relational databases (JDBC), text configuration sources like INI and properties files, and more. You can plug-in your own Realm implementations to represent custom data sources if the default Realms do not meet your needs. 
Example realm configuration snippet to connect to LDAP user data store
ldapRealm = org.apache.shiro.realm.ldap.JndiLdapRealm
ldapRealm.userDnTemplate = uid={0},ou=users,dc=mycompany,dc=com
ldapRealm.contextFactory.url = ldap://ldapHost:389
ldapRealm.contextFactory.authenticationMechanism = DIGEST-MD5 


 This is typically a three-step process.
  1. Collect the user’s identifying information, called principals (e.g. username), and supporting proof of identity, called credentials (e.g. password)
  2. Submit the principals and credentials to the system.
  3. If the submitted credentials match what the system expects for that user identity (principal), the user is considered authenticated. If they don’t match, the user is not considered authenticated.
Shiro has a Subject-centric API - almost everything you care to do with Shiro at runtime is achieved by interacting with the currently executing Subject. So, to login a Subject, you simply call its login method, passing an AuthenticationToken instance that represents the submitted principals and credentials (in this case, a username and password). 
Subject Login
//1. Acquire submitted principals and credentials:
AuthenticationToken token =new UsernamePasswordToken(username, password);
//2. Get the current Subject: Subject currentUser = SecurityUtils.getSubject();
//3. Login: try { currentUser.login(token); } catch (IncorrectCredentialsException ice) { } catch (LockedAccountException lae) { } catch (AuthenticationException ae) { }


Perform access control by employing concepts such as roles and permissions.
Role Check
if ( subject.hasRole(“administrator”) ) {
    //show the ‘Create User’ button
} else {
    //grey-out the button?
Permission Check
if ( subject.isPermitted(“user:create”) ) {
    //show the ‘Create User’ button
} else {
    //grey-out the button?
Finally, just as with authentication, the above calls eventually make their way to the SecurityManager, which will consult one or more Realms to make the access control decisions. This allows a Realm to respond to both authentication and authorization operations as necessary.

Session Management

Container agnostic session management: Application developers who wish to use sessions are no longer forced to use Servlet or EJB containers if they don’t need them otherwise. Or, if using these containers, developers now have the option of using a unified and consistent session API in any tier, instead of servlet or EJB-specific mechanisms.

Shiro’s architecture allows for pluggable Session data stores, such as enterprise caches, relational databases, NoSQL systems and more. This means that you can configure session clustering once and it will work the same way regardless of your deployment environment - Tomcat, Jetty, JEE Server or standalone application. There is no need to reconfigure your app based on how you deploy your application.

Another benefit of Shiro’s sessions is session data can be shared across client technologies if desired. For example, a Swing desktop client can participate in the same web application session if desired - useful if the end-user is using both simultaneously. 

Subject’s Session
Session session = subject.getSession();
Session session = subject.getSession(boolean create);
The methods are identical in concept to the HttpServletRequest API. The first method will return the Subject’s existing Session, or if there isn’t one yet, it will create a new one and return it. The second method accepts a boolean argument that determines whether or not a new Session will be created if it does not yet exist. Once you acquire the Subject’s Session, you can use it almost identically to an HttpSession.

 Session methods
Session session = subject.getSession();
session.getAttribute(“key”, someValue); Date start = session.getStartTimestamp(); Date timestamp = session.getLastAccessTime(); session.setTimeout(millis);

Shiro Web Support

ShiroFilter in web.xml





Once configured, the Shiro Filter will filter every request and ensure the request-specific Subject is accessible during the request. And because it filters every request, you can perform security-specific logic to ensure only requests that meet certain criteria are allowed through.

URL-specific filter chains

Shiro supports security-specific filter rules through its innovative URL filter chaining capability. It allows you to specify ad-hoc filter chains for any matching URL pattern. 

Path-specific Filter Chains
/assets/** = anon
/user/signup = anon
/user/** = user
/rpc/rest/** = perms[rpc:invoke], authc
/** = authc
For each line, the values on the left of the equals sign represent a context-relative web application path. The values on the right define a Filter chain - an ordered, comma delimited list of Servlet filters to execute for the given path. Each filter is a normal Servlet Filter, but the filter names you see above (anon, user, perms, authc) are special security-related filters that Shiro provides out-of-the-box. You can mix and match these security filters to create a very custom security experience. You can also specify any other existing Servlet Filter you may have.

How much nicer is this compared to using web.xml, where you define a block of filters and then a separate disconnected block of filter patterns? Using Shiro’s approach, it is much easier to see exactly the filter chain that is executed for a given matching path. 

If you wanted to, you could define only the Shiro Filter in web.xml and define all of your other filters and filter chains in shiro.ini for a much more succinct and easy to understand filter chain definition mechanism than web.xml. Even if you didn’t use any of Shiro’s security features, this one small convenience alone can make Shiro worth using.

Web Session Management

For web applications, Shiro defaults its session infrastructure to use the existing Servlet Container sessionsThat is, when you call the methods subject.getSession() and subject.getSession(boolean) Shiro will return Session instances backed by the Servlet Container’s HttpSession instance. The beauty of this approach is that business-tier code that calls subject.getSession() interacts with a Shiro Session instance - it has no ‘knowledge’ that it is working with a web-based HttpSession object. This is a very good thing when maintaining clean separation across architectural tiers.

If you’ve enabled Shiro’s native session management in a web application because you need Shiro’s enterprise session features (like container-independent clustering), you of course want the HttpServletRequest.getSession() and HttpSession API to work with the ‘native’ sessions and not the servlet container sessions.

No comments: