Apache Shiro Research and Investigation

In this post we will talk about (refer to) some posts with regard to Apache Shiro used in the user access control of ERP programs.

Application Security With Apache Shiro

Core Concepts: Subject, SecurityManager, and Realms

Now that we’ve covered Shiro’s benefits, let’s jump right in to its API so you can get a feel for it. Shiro’s architecture has three main concepts - the Subject, the SecurityManager, and Realms.

Subject

When you’re securing your application, probably the most relevant questions to ask yourself are, “Who is the current user?” or “Is the current user allowed to do X”? It is common for us to ask ourselves these questions as we’re writing code or designing user interfaces: applications are usually built based on user stories, and you want functionality represented (and secured) based on a per-user basis. So, the most natural way for us to think about security in our application is based on the current user. Shiro’s API fundamentally represents this way of thinking in its Subject concept.

The word Subject is a security term that basically means “the currently executing user”. It’s just not called a ‘User’ because the word ‘User’ is usually associated with a human being. In the security world, the term ‘Subject’ can mean a human being, but also a 3rd party process, daemon account, or anything similar. It simply means ‘the thing that is currently interacting with the software’. For most intents and purposes though, you can think of this as Shiro’s ‘User’ concept. You can easily acquire the Shiro Subject anywhere in your code as shown in Listing 1 below.

Listing 1. Acquiring the Subject
import org.apache.shiro.subject.Subject; import org.apache.shiro.SecurityUtils; ... Subject currentUser = SecurityUtils.getSubject();

Once you acquire the Subject, you immediately have access to 90% of everything you’d want to do with Shiro for the current user, such as login, logout, access their session, execute authorization checks, and more - but more on this later. The key point here is that Shiro’s API is largely intuitive because it reflects the natural tendency for developers to think in ‘per-user’ security control. It is also easy to access a Subject anywhere in code, allowing security operations to occur anywhere they are needed.

SecurityManager

The Subject’s ‘behind the scenes’ counterpart is the SecurityManager. While 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.

So how do you set up a SecurityManager? Well, that depends on your application environment. For example, a web application will usually specify a Shiro Servlet Filter in web.xml, and that will set up the SecurityManager instance. If you’re running a standalone application, you’ll need to configure it another way. But there are many of configuration options.

There is almost always a single SecurityManager instance per application. It is essentially an application singleton (although it does not need to be a static singleton). Like almost all things in Shiro, the default SecurityManager implementations are POJOs and are configurable with any POJO-compatible configuration mechanism - normal Java code, Spring XML, YAML, .properties and .ini files, etc. Basically anything that is capable of instantiating classes and calling JavaBeans-compatible methods may be used.

To that end, Shiro provides a default ‘common denominator’ solution via text-based INI configuration. INI is easy to read, simple to use, and requires very few dependencies. You’ll also see that with a simple understanding of object graph navigation, INI can be used effectively to configure simple object graph like the SecurityManager. Note that Shiro also supports Spring XML configuration and other alternatives, but we’ll cover INI here.

The simplest example of configuring Shiro based on INI is shown in the example in Listing 2 below.

Listing 2. Configuring Shrio with INI
[main] 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

In Listing 2, we see the example INI configuration we will use to configure the SecurityManager instance. 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. In this example, we see two objects being configured:

The cm object, which is an instance of Shiro’s HashedCredentialsMatcher class. As you can see, various properties of the cm instance are being configured via ‘nested dot’ syntax - a convention used by the IniSecurityManagerFactory shown in Listing 3, to represent object graph navigation and property setting.
The iniRealm object, which is a component used by the SecurityManager to represent user accounts defined in the INI format.
The [users] section is where you can specify a static list of user accounts - convenient for simple applications or when testing.

For the purposes of this introduction, it is not important to understand the intricacies of each section, but rather to see that INI configuration is one simple way of configuring Shiro. For more detailed information on INI configuration, please see Shiro’s documentation.

Listing 3. 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);

In Listing 3, we see a three-step process in this simple example:

Load the INI configuration that will configure the SecurityManager and its constituent components.
Create the SecurityManager instance based on the configuration (using Shiro’s Factory concept that represents the Factory Method design pattern).
Make the SecurityManager singleton accessible to the application. In this simple example, we set it as a VM-static singleton, but this is usually not necessary - your application configuration mechanism can determine if you need to use static memory or not.

Realms

The third and final core concept in Shiro is that of a 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. More than one Realm may be configured, but at least one is required.

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. Listing 4 below is an example of configuring Shiro (via INI) to use an LDAP directory as one of the application’s Realms.

Authentication

Authentication is the process of verifying a user’s identity. That is, when a user authenticates with an application, they are proving they actually are who they say they are. This is also sometimes referred to as ‘login’. This is typically a three-step process.

  • Collect the user’s identifying information, called principals, and supporting proof of identity, called credentials.
  • Submit the principals and credentials to the system.
  • 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.

A common example of this process that everyone is familiar with is that of the username/password combination. When most users login to a software application, they usually provide their username (the principal) and their supporting password (the credential). If the password (or representation of it) stored in the system matches what the user specifies, they are considered authenticated.

Shiro supports this same workflow in a simple and intuitive way. As we’ve said, 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). This example is shown in Listing 5 below.

Listing 5. 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:
currentUser.login(token);

As you can see, Shiro’s API easily reflects the common workflow. You’ll continue to see this simplicity as a theme for all of the Subject’s operations. When the login method is called, the SecurityManager will receive the AuthenticationToken and dispatch it to one or more configured Realms to allow each to perform authentication checks as required. Each Realm has the ability to react to submitted AuthenticationTokens as necessary. But what happens if the login attempt fails? What if the user specified an incorrect password? You can handle failures by reacting to Shiro’s runtime AuthenticationException as shown in Listing 6.

Listing 6. Handle Failed Login

//3. Login: try { currentUser.login(token); } catch (IncorrectCredentialsException ice) { … } catch (LockedAccountException lae) { … } … catch (AuthenticationException ae) {… }

You can choose to catch one of the AuthenticationException subclasses and react specifically, or generically handle any AuthenticationException (for example, show the user a generic “Incorrect username or password” message). The choice is yours depending on your application requirements.

After a Subject logs-in successfully, they are considered authenticated and usually you allow them to use your application. But just because a user proved their identity doesn’t mean that they can do whatever they want in your application. That begs the next question, “how do I control what the user is allowed to do or not?” Deciding what users are allowed to do is called authorization. We’ll cover how Shiro enables authorization next.

Authorization

Authorization is essentially access control - controlling what your users can access in your application, such as resources, web pages, etc. Most users perform access control by employing concepts such as roles and permissions. That is, a user is usually allowed to do something or not based on what roles and/or permissions are assigned to them. Your application can then control what functionality is exposed based on checks for these roles and permissions. As you might expect, the Subject API allows you to perform role and permission checks very easily. For example, the code snippet in Listing 7 shows how to check if a Subject has been assigned a certain role.

Listing 7. Role Check

if ( subject.hasRole(“administrator”) ) { //show the ‘Create User’ button } else { //grey-out the button? }

As you can see, your application can enable or disable functionality based on access control checks.

Permission checks are another way to perform authorization. Checking for roles as in the example above suffers from one significant flaw: you can’t add or delete roles at runtime. Your code is hard-coded with role names, so if you changed the role names and/or configuration, your code would be broken! If you need to be able to change a role’s meaning at runtime, or add or delete roles as desired, you have to rely on something else.

To that end, Shiro supports its notion of permissions. A permission is a raw statement of functionality, for example ‘open a door’, ‘create a blog entry’, ‘delete the ‘jsmith’ user’, etc. By having permissions reflect your application’s raw functionality, you only need to change permission checks when you change your application’s functionality. In turn, you can assign permissions to roles or to users as necessary at runtime.

As an example, shown in Listing 8 below, we can rewrite our previous role check and instead use a permission check.

Listing 8. Permission Check

if ( subject.isPermitted(“user:create”) ) { //show the ‘Create User’ button } else { //grey-out the button? }

This way, any role or user assigned the “user:create” permission can click the ‘Create User’ button, and those roles and assignments can even change at runtime, providing you with a very flexible security model.

The “user:create” string is an example of a permission string that adheres to certain parsing conventions. Shiro supports this convention out of the box with its WildcardPermission. Although out of scope for this introduction article, you’ll see that the WildcardPermission can be extremely flexible when creating security policies, and even supports things like instance-level access control.

Listing 9. Instance-Level Permission Check

if ( subject.isPermitted(“user:delete:jsmith”) ) {
//delete the ‘jsmith’ user
} else {
//don’t delete ‘jsmith’
}

This example shows that you can control, even down to a very fine-grained instance level, access to individual resources if you needed to. You could even invent your own permission syntax if you wanted to. See the Shiro Permission documentation for more information. 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.

So that is a brief overview of Shiro’s authorization capabilities. And while most security frameworks stop at authentication and authorization, Shiro provides much more. Next we’ll talk about Shiro’s advanced Session Management capabilities.

Java Authorization Guide with Apache Shiro

Authorization, or access control, is the function of specifying access rights to resources. In other words, who has access to what.

Examples of authorization checks are: Is the user allowed to look at this webpage, edit this data, view this button, or print to this printer? Those are all decisions determining what a user has access to.

Elements of Authorization

Authorization has three core elements that we reference quite a bit in Shiro– permissions, roles, and users.

#[[###Permissions Defined]]#

Permissions are the most atomic level of a security policy and they are statements of functionality. Permissions represent what can be done in your application. A well formed permission describes a resource types and what actions are possible when you interact with those resources. Can you open a door? Can you read a file? Can you delete a customer record? Can you push a button?

Common actions for data-related resources are create, read, update, and delete, commonly referred to as CRUD.

It is important to understand that permissions do not have knowledge of who can perform the actions– they are just statements of what actions can be performed.

#[[####Levels of permission granularity]]#

The permissions above all specify an actions (open, read, delete, etc) on a resource (door, file, customer record, etc). In Shiro, you can define a permission to any depth you like. Here are a few common permission levels in order of granularity.

  • Resource Level - This is the broadest and easiest to build. A user can edit customer records or open doors. The resource is specified but not a specific instance of that resource.
  • Instance Level - The permission specifies the instance of a resource. A user can edit the customer record for IBM or open the kitchen door.
  • Attribute Level - The permission now specifies an attribute of an instance or resource. A user can edit the address on the IBM customer record.

For more information on Permissions please check out the Permissions Documentation

#[[###Roles Defined]]#

In the context of Authorization, Roles are effectively a collection of permissions used to simplify the management of permissions and users. So users can be assigned roles instead of being assigned permissions directly, which can get complicated with larger user bases and more complex applications. So, for example, a bank application might have an administrator role or a bank teller role.

There are two types of roles that you need to be aware of and Shiro will support both.

#[[####Implicit Roles]]#

Most people view roles as what we define as an implicit role where your application implies a set of permissions because a user has a particular role as opposed to the role explicitly being assigned permissions or your application checking for those permissions. Role checks in code are generally a reflection of an implicit role. You can view patient data because you have the administrator role. You can create an account because you have the bank teller role. The fact that these names exist does not have a correlation to what the software can actually do. Most people use roles in this manner. It is easiest but it can create a lot of maintenance and management problems for all but the simplest application.

#[[####Explicit Roles]]#

An explicit role has permissions explicitly assigned to it and therefore is an explicit collection of permissions. Permission checks in code are a reflection of an explicit role. You can view patient data because because you have the view patient data permission as part of your administrator role. You can create an account because you have the create account permission as part of your bank teller role. You can perform these actions, not because of some implicit role name based on a string but because the corresponding permission was explicitly assigned to your role.

The big benefits of explicit roles are easier manageability and lower maintenance of your application. If you ever need to add, remove, or change a role, you can do so without touching your source code. And in Shiro, you’ll also be able to dynamically add, remove, or change roles at runtime and your authorization checks will always have up to date values. This means you won’t have to force users to log out and log back in order to get their new permissions.

#[[###Users Defined]]#

A user is the “who” of an application. In Shiro, though, the concept of a user is really the Subject instance. We use word Subject instead of user because user usually implies a human being and in Shiro a Subject can be anything interacting with your application– whether it be a human or a service.

Users are allowed to perform certain actions in your application through their association with roles or direct permissions. So you are able to open a customer record because you’ve been assigned the open customer record permission, either through a role you’ve been assigned or through a direct permission assignment.

For more information on Users, aka Subjects, please check out the Subject Documentation.

#info(‘Note’, ‘Ultimately, your Realm implementation is what communicates with your data source (RDBMS, LDAP, etc). So your realm is what will tell Shiro whether or not roles or permissions exist. You have full control over how your authorization model works.’)

How to perform Authorization in Java with Shiro

Authorization in Shiro can be handled in four ways.

  • Programmatically - You can perform authorization checks in your java code with structures like if and else blocks.
  • JDK annotations - You can attach an authorization annotation to your Java methods
  • JSP/GSP TagLibs - You can control jsp or gsp page output based on roles and permissions

#[[###Programmatic Authorization]]#

Checking for permissions and roles, programmatically in your Java code is the traditional way of handling authorization. Here’s how you can perform a permission check or role check in Shiro.

#[[####Role Check]]#

This is an example of how you do a role check programmatically in your application. We want to check if a user has the administrator role and if they do, then we’ll show a special button, otherwise we won’t show it.

First we get access to the current user, the Subject. Then we pass the adminstrator to the Subject’s .hasRole() method. It will return TRUE or FALSE.

1
2
3
4
5
6
7
8
//get the current Subject
Subject currentUser = SecurityUtils.getSubject();

if (currentUser.hasRole("administrator")) {
//show a special button‏
} else {
//don’t show the button?)‏
}

Now a role based check is quick and easy to implement but it has a major drawback. It is implicit.

What if you just want to add, remove, or redefine a role later? You’ll have to crack open your source code and change all your role checks to reflect the change in your security model. You’ll have to shut down the application, crack open the code, test it, and then restart it everytime.

In very simple applications this is probably good enough but for larger apps this can be a major problem throughout the life of your application and drive a large maintenance cost for your software.

#[[####Permission Check]]#

This is an example of how you do security checks by permission. We want to check if a user has permission to print to laserjet3000n and if they do, then we’ll show a print button, otherwise we won’t show it. This is an example of an instance level permission or instance level authorization.

Again, first you get access to the current user, the Subject. Then you construct a Permission object or an instance that represents an action on a resource. In this case, the instance is named printerPermission, the resource is laserjet3000n, and the action is print. Then we pass printerPermission to the Subject’s .isPermitted() method. It will return true or false.

1
2
3
4
5
6
7
8
9
Subject currentUser = SecurityUtils.getSubject();

Permission printPermission = new PrinterPermission("laserjet3000n","print");

If (currentUser.isPermitted(printPermission)) {
//do one thing (show the print button?)‏
} else {
//don’t show the button?
}

#[[####Permission Check (String-based)]]#

You can also a permission check using a simple string instead of a permission class.

So, if you don’t want to implement our permission interface then you just pass in a String. In this example, we pass the .isPermitted() method a string, printer:print:LaserJet4400n

1
2
3
4
5
6
7
String perm = "printer:print:laserjet4400n";

if(currentUser.isPermitted(perm)){
//show the print button?
} else {
//don’t show the button?
}

You can construct the permission string the way you want so long as your Realm knows how to work with it. In this example we use Shiro’s optional permission syntax, WildCardPermissions. WildCardPermissions are powerful and intuitive. If you’d like to learn more about them then check out the Permissions Documentation.

With string-based permission checks, you get the same functionality as the example before. The benefit is that you are not forced to implement a permission interface and you can construct the permission via a simple string. The downside is that you don’t have type safety and if you needed more complicated permission capabilities that are outside the scope of what this represents, you’re going to want to implement your own permission objects based on the permission interface.

#[[###Annotation Authorization]]#

If you don’t want to do code level authorization checks, then you can use Java Annotations as well. Shiro offers a number of Java annotations that allow you to annotate methods.

#[[####Enabling Annotation Support]]#

Before you can use Java annotations, you’ll need to enable AOP support in your application. There are a number of different AOP frameworks so, unfortunately, there is no standard way to enable AOP in an application.

For AspectJ, you can review our AspectJ sample application.

For Spring, you can look into our Spring Integration documentation.

For Guice, you can look into our Guice Integration documentation.

#[[####Permission Check]]#

In this example, we want to check that a user has the account:create permission before they can invoke the openAccount method. If they do, then the method is called as expected, and if they don’t, then an exception is thrown.

Like programmatic checks, you can use the Permission objects or the simple string methods with this annotation.

1
2
3
4
5
6
7
//Will throw an AuthorizationException if none
//of the caller’s roles imply the Account
//'create' permission
@RequiresPermissions("account:create")‏
public void openAccount( Account acct ) {
//create the account
}

#[[####Role Check]]#

In this example, we want to check that a user has the teller role before they can invoke the openAccount method. If they do, then the method is called as expected, and if they don’t, then an exception is thrown.

1
2
3
4
5
6
7
//Throws an AuthorizationException if the caller
//doesn’t have the ‘teller’ role:
@RequiresRoles( "teller" )
public void openAccount( Account acct ) {
//do something in here that only a teller
//should do
}

#[[###JSP TagLib Authorization]]#

For JSP/GSP based web applications, Shiro also offers a tag library for you to use.

In this example, we’re going to show users with the users:manage permission a link to the Manage Users page. If they do not have the permission, then we’ll show them a nice message.

First, we’ll need to add the Shiro taglib to our web application. Next, we add the <shiro:hasPermission> tag with a check for users:manage. Within the <shiro:hasPermission> tags we will place the code we want to execute if the user has the permission we’re checking for. If we want to take an action if the user lacks the permission, then we need to also add the <shiro:lacksPermission> tag, again checking for users:manage. And any code we want to excute if the user lacks the permission will need to be placed within the <shiro:lacksPermission> tags.

1
2
3
4
5
6
7
8
9
10
11
12
13
<%@ taglib prefix="shiro" uri=http://shiro.apache.org/tags %>
<html>
<body>
<shiro:hasPermission name="users:manage">
<a href="manageUsers.jsp">
Click here to manage users
</a>
</shiro:hasPermission>
<shiro:lacksPermission name="users:manage">
No user management for you!
</shiro:lacksPermission>
</body>
</html>

Of course, there also tags for checking roles and other user data and states.

For more information on JSP/GSP Tags please check out the JSP Tag Library and for more information on integration your application in your web application, please read the Web Integration Documentation

Caching Authorization

TBD

#lendAHandDoc()