Skip to main content

Securing Your ADF Applications Using Apache Shiro (Java Security Framework)

In case if you are using ADF Essential(free version of Oracle ADF) for building applications, or if you want to make you application portable across multiple application servers(certified as well as non certified), you may need to compromise on using certain non portable pieces from the ADF stack(security, MDS etc). In such cases, for security related things you may end up in using JAAS or some custom Java security frameworks. In this post I'm sharing some tips on using a really cool Java security framework known as Apache Shiro. The thing that I personally liked is its usability aspects- its very flexible and easy to use. Let us see how Shiro can be used for securing an ADF application.

A note of thanks... 
I would not have tried Apache Shiro unless I had seen the following blog post while googling for Java security frameworks http://balusc.blogspot.sg/2013/01/apache-shiro-is-it-ready-for-java-ee-6.html. A well written article. So the credit goes to Bauke Scholtz(BalusC) who wrote that great stuff. Note that, the following section  is not going to detail the configuration aspects of Shiro. So please go through the above article from BalusC to get  a clear understanding on the usage part.

Setting Up the Environment

Step 1: The sample that I'm using to demonstrate Apache Shiro is a simple ADF application which uses MySQL database. btw, MySQL has been improved a lot over a period. Installing and using it on Windows is now pretty simple :) . Give a try. You can download the MySQL from here http://www.mysql.com/downloads/

Step 2: As we are using Apache Shiro, let us go and grab the required Shiro jars before starting building the application. You can download the binaries from this link http://shiro.apache.org/download.html#Download-1.2.1.BinaryDistribution. If you are very keen, then go ahead and check out the source from SVN source repository given in the same page. The following files are required for running this sample.
shiro-core-1.2.1.jar, shiro-web-1.2.1.jar, slf4j-api-1.7.2.jar and slf4j-simple-1.7.2.jar
You can also find these jars in the example zip file that I uploaded towards the end of this post(see Download section). Find them in <ADFEssentialDemo>/shirojars folder.

Step 3: Now let us build the application. I'm not going to detail the steps for building an ADF Fusion Web application here,  rather I just want to highlight few odd steps that you may not be usually doing. As this application use MySQL database, while initializing the Model project select SQL 92 as SQL Platform and Java as Data Type Map. Now create a connection to MySQL. This example uses sample sakila database that comes with MySQL by default.


Step 4: Generate business components by choosing appropriate tables.

Step 5:  Next is to add the required jars to the View Controller project for using Shiro APIs. See Step 2 to know more about the jars.


Step 6: Now go to the web.xml file and add the following  filter entry.

 <filter>  
      <filter-name>shiroFilter</filter-name>  
      <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>  
 </filter>  
 <filter-mapping>  
      <filter-name>shiroFilter</filter-name>  
      <url-pattern>/*</url-pattern>  
      <dispatcher>REQUEST</dispatcher>  
      <dispatcher>FORWARD</dispatcher>  
      <dispatcher>INCLUDE</dispatcher>  
      <dispatcher>ERROR</dispatcher>  
 </filter-mapping>  

Step 7: Create a  a text file and name it as shiro.ini file in your WEB-INF folder. This is text-based configuration file for use by Shiro.  We will add entries in this file later. Update the web.xml file with the location of this file as shown below.

 <context-param>  
      <param-name>shiroConfigLocations</param-name>  
      <param-value>/WEB-INF/shiro.ini</param-value>  
 </context-param>  

You can optionally remove JpsFilter related entries from web the filter section as we are not going to use it any more.

Step 8: Now lets us talk about security things. The Apache Shiro let you to configure users, roles and permission in shiro.ini file as well  in other data stores such as database and ldap. This example uses database for storing application users, roles for users and permissions for  roles. It also uses a JDBC realm(org.apache.shiro.realm.jdbc.JdbcRealm) that comes with framework by default.

You can have your own structure for tables for storing user credentials. Only contract here is that the query that you later specify in shiro.ini for reading users and roles should follow the rules set by default JdbcRealm. For example, the authentication query must take the user's username as a single parameter and return a single result with the user's password as the first column. Create the following tables:

 
USE sakila;
CREATE TABLE `user` (  
  `ID` bigint(20) NOT NULL,  
  `PASSWORD` varchar(255) NOT NULL,  
  `USERNAME` varchar(255) NOT NULL,  
  PRIMARY KEY (`ID`),  
  UNIQUE KEY `USERNAME` (`USERNAME`)  
 );   
 CREATE TABLE `userroles` (  
  `USERID` bigint(19) NOT NULL,  
  `ROLE` varchar(255) NOT NULL,  
  `DESCRIPTION` varchar(255) DEFAULT NULL,  
  `ROLEID` bigint(20) NOT NULL,  
  PRIMARY KEY (`ROLEID`)  
 );  
 CREATE TABLE `rolepermission` (  
  `PERMISSIONID` bigint(20) NOT NULL,  
  `PERMISSION` varchar(45) DEFAULT NULL,  
  `ROLEID` bigint(20) NOT NULL,  
  PRIMARY KEY (`PERMISSIONID`)  
 );   

A user can have many roles and each role can have many permissions. So while entering data you must maintain this relation between these tables. Note that as this is just a demo, you may not be seeing any foreign key settings between tables here. I'm bit lazy. You can have it in your real life app. The following screen shot displays data that I used for testing.


Step 9: Build the JSF pages as per your business need.

Step 10: We are into the last step of configuration. You need to tell Shiro about the login page to be used, queries to be used for authenticating and authorizing users, data source for default JDBC realm and  also the resources/pages that needs to be secured. This is done by using shiro.ini that we created in Step 7. The shiro.ini used in this example is shown below:

 [main]  
 user = view.filter.FacesAjaxAwareUserFilter  
 shiro.loginUrl = /faces/login.jsf  
 user.loginUrl = /faces/login.jsf  
 # DataSource config  
 ds = org.apache.shiro.jndi.JndiObjectFactory   
 ds.requiredType = javax.sql.DataSource  
 ds.resourceName = jdbc/MySQLDS  
 # JDBC realm config  
 jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm  
 jdbcRealm.permissionsLookupEnabled = true  
 # Configure JDBC realm SQL queries.  
 jdbcRealm.authenticationQuery = SELECT password FROM sakila.User WHERE username = ?  
 jdbcRealm.userRolesQuery = SELECT role FROM sakila.UserRoles WHERE userId = (SELECT id FROM sakila.User WHERE username = ?)  
 jdbcRealm.permissionsQuery = SELECT permission FROM sakila.RolePermission WHERE roleId = (SELECT roleId FROM sakila.UserRoles WHERE role = ?)  
 jdbcRealm.dataSource = $ds  
 [urls]  
 /faces/login.jsf = user  
 /faces/index.jsf = user  
 /faces/app/** = user  

Step 11: For deploying this application in to Glassfish server, please see the following blog post from Shay
https://blogs.oracle.com/shay/entry/deploying_oracle_adf_applications_to

Download


You can download the sample workspace from here.
[ Runs with Oracle JDeveloper  11.1.2.3.0 +  GlassFish 3.1.x + MySQL(sakila sample database) + Shiro 1.2.1 ]

How to run the sample?

This uses sakila sample database which comes with MySQL by default. Change the connection to MySQL database as per your local environment. We will use GlassFish as application server for running this sample. (This may also work in WLS though I've not tested it)

  1. Create tables for storing users, roles and permissions. Then insert required data as appropriate. Details are given in Step 8.
  2. Add Shiro jars to view controller project. You may find them in <ADFEssentialDemo>/shirojars folder. See step 5 in the above section 
  3. Create data source in Glassfish and deploy the application. See Step 11 for details.
  4. Try accessing index.jsf,  system will redirect you to login page. Enter admin/password and hit Login. System takes you to the index.jsf page. See displays the details.
  5. Click Logout, system takes you to login page. Enter  normal/password,and hit Login.  The normal user is not allowed to view address details. So the system hides address task flow in this case.
Disclaimer: I've not tested the above stuff for all security scenarios :) If you are planning to use it, run one round of complete test and make sure Shiro meets(and works) all your security  use cases. Enjoy !

Comments

Anonymous said…
Just a warning: we found out that Shiro puts a wrapper round the HttpSession-object and when we wanted it distributed via Coherence - Distribute, ADF BC was very unstable, even when there was only one WebLogic-server active.

After disabling Shiro and instead used Coherence*Web the problem was solved.

I think the problem is due to that ADF BC wants to distribute the Application Module and needs the HttpSession-object to reconnect on other servers.
Thanks for the heads up. Would be great if you can pass this to the Shiro team as well
raj s said…
Hi Sir, can you please tell me how to change default 'Loading..' animation in adf?
Anonymous said…
Hi Jobinesh,

Previously ADF apps Deployed with JDK11.2.3, shiro, Mysql in Glassfish.

Now JDK updated with 12c and try to deploy it is giving error.

Error is : Error occurred during deployment: Exception while loading the app : java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: java.lang.IllegalArgumentException: java.lang.ClassNotFoundException: org.apache.shiro.web.env.EnvironmentLoaderListener. Please see server.log for more details.

Help on this..
sunil ravinder said…
Does it work with Oracle SSO
Endrik Lelo said…
This comment has been removed by the author.

Popular posts from this blog

How to set Bind Variable Values at runtime ?

In this post I'm sharing a couple of approaches for programmatically setting bind variables values at run time. This post is an attempt to explain 'When to use what ?'[ In case if you are familiar with 'Bind Variables' in ADF BC, please refer Section 5.10, Working with Bind Variables in Fusion Developer's Guide ]

1. Set the Bind Variable value using RowSet::setNamedWhereClauseParam(...)

You can use use the setNamedWhereClauseParam(...) method on the ViewObject interface (which extends oracle.jbo.RowSet) to set the value for bind variables. Please note this sets the value on default RowSet. In other words, this doesn't have any effect on the secondary RowSets that you/system generates.
ViewObject vo = am.findViewObject("EmployeesView1"); vo.setNamedWhereClauseParam("bindVarDeptId", new Number(10)); vo.executeQuery();
2. Set the Bind Variable value using ViewObject's VariableValueManager::setVariableValue(...)

VariableValueManager Ma…

Happy New Year 2018 !

We can't go back and change the beginning, but we always can start where we are and change the ending. Believe in yourself and you will be unstoppable!

Wishing you and your family a very happy new year 2018 !!!