An Introduction to Shiro (formerly JSecurity) – A Beginner’s Tutorial Part 5
Introduction
NOTE: Updated in January 2011.
In part 4 of this series, I explained how you can use Shiro's tag library to control what is rendered in the JSPs. In part 3 of this series, I explained how you can use a users role to control access to specific parts of a web application. In this part of the series, I demonstrate how you can add another level of security to your web application by using permissions. Each role (e.g. admin, user) can have one or more permissions associated with it. Using Shiro you can restrict what someone can access based upon permission.
Using Permission to Control Access
In this example, our web application has three areas needing to be secured:
/users
/staff
/admin
Our business rules for securing each area are:
/users – user is authenticated and has role of user
/staff – user is authenticated and has role of staff or role of admin
/admin – user is authenticated and has role of admin
Since roles staff and admin both need to access the staff area, we can use permissions to control access to that area. We can assign the same permission to both the staff and admin role. We'll call that permission "secure." Any user with permission of "secure" can access the pages in the staff area. If in the future we add additional roles that need to access the staff area, we can give those new roles the "secure" permission also.
So our revised security business rules for each area are:
/users – user is authenticated and has role of user
/staff – user is authenticated and has permission of secure
/admin – user is authenticated and has role of admin
Here are our three users, their roles, and their permissions:
username |
password |
role |
permission |
bruce@hotmail.com |
bruce |
admin |
secure |
jack@hotmail.com |
jack |
staff |
secure |
sue@hotmail.com |
sue |
user |
none |
Applying our security business rules to these users we can identify who will be able to access which areas:
/user – only sue
/staff – both bruce and jack but not sue
/admin - only bruce
Example Application
You can download an example application (an archived Eclipse dynamic web project using Maven). You'll also need to download a new version of the securityDB Derby database that includes the roles_permissions table.
After downloading the securityDB Derby database, unzip it to the folder c:/derby. It's OK to overwrite the database that was there as this new version of the securityDB database should work for the previous example applications.
You can import the downloaded Eclipse archived project (named permissionsecuritywithtags) into Eclipse and then run it on a Tomcat server.
You can also use the Maven jetty plugin (see reference below for how to install Maven if you've don't already have Maven) to run the web application if you're not using Eclipse and Tomcat. Just open a command window and navigate to where you unzipped the permissionsecuritywithtags.zip download. Make sure you're in the permissionsecuritywithtags directory. Then do the following (in this example I unzipped permissionsecuritywithtags.zip to c:\jsecurity_examples):
c:\jsecurity_examples\ permissionsecuritywithtags \mvn clean
c:\jsecurity_examples\ permissionsecuritywithtags \mvn jetty:run
Once you see [INFO] Started Jetty Server in the command window, open your web browser and go to this URL: http://localhost:8080/permissionsecuritywithtags/ . You should see the contents of the index.jsp. To stop the Jetty server type control-c in the command window.
Login as each user (bruce@hotmail.com, jack@hotmail.com, and sue@hotmail.com). When you're logged in as sue@hotmail.com try to go to either http://localhost:8080/permissionsecuritywithtags/staff/index.jsp or http://localhost:8080/permissionsecuritywithtags/admin/index.jsp. You should be redirected to the unauthorized web page. When you're logged in as jack@hotmail.com try to go to the http://localhost:8080/permissionsecuritywithtags/admin/index.jsp page.
Implementing Permission Security in Shiro
So how do we implement security that uses permissions in Shiro? When you attempt to authenticate a user, Shiro will look for any permissions associated with the role that is associated with that user. To use Shiro's default configuration you just need a table named roles_permissions with a column named role_name and a column named permission. The default permissions query is specified in class JdbcRealm, which my class RoleSecurityJdbcRealm extended. In web.xml I specified this class as the value for the IniShiroFilter's realm (a realm is a resource that Shiro will use to authenticate users).
If your project cannot follow Shiro's defaults, you can configure Shiro to use your projects conventions (see the Shiro references below).
In Servlet class LoginUser I created a Subject object and call the Subject class's login method passing it the UsernamePasswordToken object that has the user's username and password. If Shiro can successfully authenticate this user by querying the users table, Shiro will then query the user_roles table for roles associated with this username, and then query the roles_permissions table for permissions associated with each role_name associated with this username. All of this information will be stored in the Subject object and placed into session scope. For more information on interfaces Subject and its associated interfaces AuthenticationInfo and and AuthorizationInfo see the Shiro API.
If you examine the web.xml you'll see the following statements in the configuration for the IniShiroFilter:
[main]
realmA = name.brucephillips.somesecurity.dao.RoleSecurityJdbcRealm
realmA.permissionsLookupEnabled=true
[filters]
roles.unauthorizedUrl = /unauthorized.jsp
perms.unauthorizedUrl = /unauthorized.jsp
#only let authenticated users
#with the appropriate role or permission
#view the web pages in the staff, user,
#and admin areas
[urls]
/staff/** = authc, perms[secure]
/admin/** = authc, roles[admin]
/user/** = authc, roles[user]
Under the [main] section the realmA.permissionsLookupEnabled=true configures Shiro to also lookup the permissions associated with a role. By default permissions lookup is false.
The statement perms.unauthorizedUrl = /unauthorized.jsp tells the filter to redirect people who attempt to view a web page in an area of the site for which they don't have the correct permission to the unauthorized.jsp web page.
The statement /staff/** = authc, perms[secure] means only allow people who have logged in successfully and have a permission of secure to view pages in the /staff folder.
In the Servlet LoginUser.java you'll find this statement around line number 134:
if (subject.isPermitted("secure") )
This statement uses the isPermitted method of the Subject class. This method returns true if the Subject object has the permission sent as the argument, otherwise the isPermitted method returns false.
Shiro also has a tag you can use to check a user's permission. In the JSP /index.jsp I use the hasPermission tag (around line 21) to render content for only those users with the secure permission.
Summary
Shiro is a comprehensive security library that gives you control over all aspects of your web application. By using permissions you can enable more specific access rules.
References
- An Introduction to Shiro (formerly JSecurity) – A Beginner's Tutorial Part 4, http://www.brucephillips.name/blog/index.cfm/2009/4/5/An-Introduction-to-Ki-formerly-JSecurity--A-Beginners--Tutorial-Part-4
- An Introduction to Shiro (formerly JSecurity) – A Beginner's Tutorial Part 3, http://www.brucephillips.name/blog/index.cfm/2009/4/5/An-Introduction-to-Ki-formerly-JSecurity--A-Beginners--Tutorial-Part-3
- Permission Security With Tags Example Application, http://www.brucephillips.name/jsecurity_examples/permissionsecuritywithtags_mvn.zip
- Apache Shiro http://shiro.apache.org/
- Apache Shiro API, http://shiro.apache.org/static/current/apidocs/
- Apache Shiro Tags API, http://shiro.apache.org/static/current/apidocs/org/apache/shiro/web/tags/package-summary.html
- Apache Shiro Mailing Lists, http://shiro.apache.org/mailing-lists.html
- Shiro Custom Tags TLD, http://www.brucephillips.name/jsecurity_examples/ki%20(jsecurity)%20tld.pdf
- Using Custom Tags, J2EE Tutorial, http://java.sun.com/javaee/5/docs/tutorial/doc/bnaiy.html
- Apache Derby, http://db.apache.org/derby/
- Apache Tomcat, http://tomcat.apache.org/
- Jetty, http://jetty.mortbay.org/jetty5/index.htmltp://incubator.apache.org/projects/ki.html
- Maven: The Definitive Guide, http://www.sonatype.com/books/maven-book/reference/public-book.html
- Developing with Eclipse and Maven, http://www.sonatype.com/books/m2eclipse-book/reference/index.html