For one of the projects I was recently involved in, we had to implement JAAS based authentication & authorization for our JSF Web Application, and we wanted to base it on the tables in our own application specifying the usernames and their roles. The users and roles can be managed through the application itself – instead of from some management console in the application server or worse still in some XML file in the middle tier.
Since we initially created the application with standard JAAS based authentication, we did not need to change a thing in the application itself, in order to switch from standard JAAS (file based in OC4J) to database based JAAS. This article describes how we implemented a database package that provided the functionality required by a custom JAAS Database powered Login Module and configured it in all of JDeveloper 10.1.3 embedded OC4J, standalone OC4J 10.1.3.1 and OPMN managed OC4J. You might want to first read the article Implement JAAS based Authentication and Authorization for ADF Faces applications on OC4J 10.1.3 in which I describe in detail how a Web Application can be configured to use JAAS in general for Authentication and Authorization.
Note: an important part of the inspiration for this article comes from Duncan Mills and Frank Nimphius in their OTN article:Declarative J2EE authentication and authorization with JAAS.
In this article, we will first create the most incredibly simple web application in the world. It is not about this web app, the focus for this article is on the JAAS Custom LoginModule and how to make it work for you. We will be using JDeveloper 10.1.3; anything I write applies to 10.1.3.x as well, as far as I have been able to establish.
Create an extremely simple Web Application
Here are the steps for implementing a secure web application:
1. Create a new application in JDeveloper 10.1.3
2. Create a new project inside this application
3. Create a new JSP with simple content – that includes the name of the currently logged in (JAAS) user
Go through the New JSP wizard:
And write the content for the JSP:
4. Configure Authentication and Authorization for this Web Application in the web.xml:
With these simple entries – security-constraint for specifying the URL patterns to protect and the roles that have access, login-config to configure the login method (in this case a Login.jsp) and security-role for each role that should be supported by the application and its authorization infrastructure – we have made our web application a ‘secure’ one. Note though that we now have to change our focus to the Application Server where we deploy our application in order to specify who are the users and what roles they have.
Configure JAAS Database Login on JDeveloper 10.1.3 Embedded OC4J
Since we have developed our web application in JDeveloper 10.1.3, we probably want to first try it out on the embedded OC4J server in JDeveloper. To that end, we have to configure our (well, actually Frank’s and Duncan’s) JAAS Database LoginModule for the embedded OC4J.
1. Copy the DBLoginModule.jar file (see download under Resources at the end of this article) to an appropriate directory, for example JDEV_HOME\jdev\lib.
2. Edit the application.xml file in JDEV_HOME\jdev\system\oracle.j2ee.10.1.3.36.73\embedded-oc4j\config
In this file, we have to create a new library definition, to make the DBLoginModule.jar available to the OC4J instance and the applications running in it. We also have to configure JAAS: specifying the fact that we are using a custom loginmodule.
There are two things in this fragment you should care about: the library element:
<library path="C:\jdevelop10_1_3\jdev\lib\DBLoginModule.jar"/>
and the jazn-provider element:
<jazn provider="XML">
<property name="custom.loginmodule.provider" value="true"/>
<property name="role.mapping.dynamic" value="true"/>
</jazn>
3. Configure the system-jazn-data.xml file for the current-workspace-app
The JDEV_HOME\jdev\system\oracle.j2ee.10.1.3.36.73\embedded-oc4j\config\system-jazn-data.xml file is used to configure the Custom LoginModule. Here we specify the class to be invoked by the Application Server when authentication is to be performed or a check if a user has a role must be performed. In our case, that is the oracle.sample.dbloginmodule.DBProcLM.DBProcOraDataSourceLoginModule that is in the DBLoginModule.jar that we downloaded from the OTN article by Frank and Duncan.
We also have to provide some configuration details for this LoginModule. The class we are using – DBProcOraDataSourceLoginModule – expects two parameters: data_source_name and plsql_procedure. The data_source_name refers to the JNDI name of a JDBC data source configured on the OC4J instance. This data source should provide access to a database schema that contains the PL/SQL function specified in the second parameter plsql_procedure. This second parameter refers to a stored function, possibly part of a package, that implements the following interface expected by the DBLoginModule.jar (or more specifically, the DBProcOraDataSourceLoginModule class):
function <whatevernameyoufancy>
( p_username in varchar2
, p_password in varchar2
, p_realm in varchar2)
return principal_ref
where the cursor returned is one that has two columns: a user identification and a role name. Note that the function should raise the FAILED_AUTHENTICATION exception if the p_username + p_password combination does not constitute valid login credentials.
4. Configure a JDBC Data Source on the Embedded OC4J instance
Configuring the data source is straightforward:
5. Implement the the plsql_procedure Stored Function
This function is where you implement the custom authorization logic. Here you specify how a username and password are authenticated and how it is determined which roles the user has. You can write complex queries against your very own application tables. Or do really anything you feel appropriate for your application. Here we use a very simple standalone implementation of the
required interface:
CREATE OR REPLACE PACKAGE BODY "JAAS_UTILS" IS
FUNCTION get_user_authentication (p_username in varchar2, p_password in varchar2, p_realm in varchar2)
RETURN principal_ref
AS
var_username varchar2(100);
var_userid number(10);
var_password varchar2(100);
role_cursor principal_ref;
FAILED_AUTHENTICATION exception;
BEGIN
select id
, username
, password
into var_userid
, var_username
, var_password
from ( select 1 id , 'ME' username ,'ME' password from dual
union
select 2 id , 'YOU' username ,'YOU' password from dual
union
select 3 id , 'HIM' username ,'HIM' password from dual
)
where username = p_username
;
if (var_password = p_password)
then
begin
-- do not cater for different realms for the moment
open role_cursor for
select rle
from ( select 1 usr_id, 'MANAGER' rle from dual
union
select 1 usr_id, 'USER' rle from dual
union
select 2 usr_id, 'USER' rle from dual
union
select 3 usr_id, 'ADMIN' rle from dual
union
select 3 usr_id, 'MANAGER' rle from dual
union
select 3 usr_id, 'USER' rle from dual
)
where usr_id = var_userid
;
end;
-- if password doesn't match, raise Exception for LM to
-- abort the authentication process
else raise FAILED_AUTHENTICATION;
end if;
RETURN role_cursor;
END get_user_authentication;
END JAAS_UTILS;
This function sets up three different users: ME, YOU and HIM (with each the same password as username). ME has roles MANAGER and USER, YOU only has (or should that be have?) the USER role while HIM has ADMIN, MANAGER and USER. Note that the names of these roles should match the roles specified in the security-role elements in the web.xml file of our application.
6. Running the SecureWebApp on the Embedded OC4J Server
Let’s run the web application – or more specifically the SecuredPage.jsp – from within JDeveloper, by simpling selecting Run from the Right Mouse Button Menu on this JSP. Since we have specified a security constraint in the web.xml file that protects this JSP, we will first be presented with the Login.jsp that we specified in the web.xml:
Once we provide the credentials and press the Inloggen button (sort of Dutch for Login), our custom JAAS DatabaseLoginModule kicks in to perform authentication and authorization. From the logging in the Console we can see what is happening:
Once authenticated and authorized we see our JSP:
Deploying and Configuring on Standalone OC4J 10.1.3
Deployment on a standalone OC4J instance is slightly different from deployment on the Embedded OC4J server. First, in order to deploy our web application, we will create a WAR file. In JDeveloper, this is most easily accomplished through a Deployment Profile. From the New Gallery, let’s create a new WAR File deployment profile:
Specify the name of the Profile:
And specify name and location of the WAR profile:
Copy the DBLoginModule.jar to the j2ee\home\lib directory on the standalone OC4J 10.1.3.1 instance:
And configure the JAR as new library in the application.xml:
Start the standalone OC4J instance
Go to the Enterprise Manager Console
And deploy our Web Application through the SecureWebApp.war.
Once the application is deployed, we can try to access the SecuredPage.jsp from our browser:
However, no matter what we try, we can not into the application. Logging in does not lead to successful authentication – as we have not yet configured our JAAS login details.
Well, we know from before that we need to do the following things:
- define the Custom JAAS LoginModule for this web application
- define the data-source
- specify the parameters data-source-name and plsql-procedure
All of this can be done through the Enterprise Manager console:
First of all, let’s create a new Managed Connection Pool for the database schema that contains the PL/SQL package JAAS_UTILS (where else than in the SCOTT schema)?
Select New Connection Pool and press Continue.
Specify the connection details for this Pool. Press Finish.
Next we create a Data Source based on this Pool:
and step two:
Now it is time for configuration of the Security Provider for our SecureWebApp (under the Administration option for the SecureWebApp application, there is an option to edit the Security Provider) :
Press Change Security Provider:
Select the Custom Security Provider value from the Security Provider Type drowdownlist. Specify the JAAS Login Module Class – the s
ame oracle.sample.dbloginmodule.DBProcLM.DBProcOraDataSourceLoginM
odule class we used before. Then create two properties for the two parameters plsql_procedure and data_source_name with appropriate values. The data_source_name is the JNDI name (that is including jdbc/) for the DataSource we created in the previous step.
When done, press OK. The following confirmation is shown.
Restart the web application.
Once the application has been restarted, we can attempt again to gain access to the SecuredPage.jsp. Since the Application Server now can turn to the custom DBLoginModule library to perform Authentication and Authorization, this time we do get in:
Note: we had a lot of trouble with deploying an EAR file including an orion-application.xml that referenced our custom data-sources.xml and jazn-data.xml that configured the custom DBLoginModule as security provider and the data source for accessing the plsql_procedure. It should be possible defining the JAAS setup thus, but we could not get it to work and had to resort to manual setup through the Enterprise Manager console. That is primarily a pain if you frequently do a fresh deployment of the application, as an Undeploy/Deploy will remove the Security Provider configuration.
It should be possible to use the command line interface of the application server to configure the security provider, with a statement such as:
java -jar jazn.jar -addloginmodule SecureWebApp
oracle.sample.dbloginmodule.DBProcLM.DBProcOraDataSourceLoginModule
required
data_source_name="jdbc/SecurityDS"
plsql_procedure="JAAS_UTILS.GET_USER_AUTHENTICATION
I have not tried this out though.
Deploying on OPMN Managed OC4J 10.1.3.1 (Oracle Application Server 10g)
Deployment on an OPMN Managed OC4J 10.1.3.1 instance is essentially the same as deploying on a stand alone OC4J. The URLs are slightly different and the Enterprise Manager console has an extra layer (first choose the OC4J instance, then configure within that instance), but otherwise the custom JAAS LoginModule works the same.
Resources
Download the Secure Web App demonstrated in this article, along with the JAAS_UTILS package JaasSecureWebApp.zip . Note: you have to add the adf-faces-impl.jar file to the WEB-INF\lib directory yourself in order to run the SecureWebApp.
Download the DBLoginModule.jar from Duncan and Frank’s article. It is part of the jaasdatabaseloginmodule.zip you can download. (I believe that for copyright reasons I cannot make the jar available for download from our own site).
The configuration of a custom loginmodule in the embedded OC4J container for JDeveloper 10.1.3 is in Joh Stegeman – Real World ADF Faces Using a custom login module with JDev 10.1.3
Documentation Oracle Application Server 10.1.3 Custom Login Modules for more background.
i am unable to get this login screen..
can anyone telkl me where is the login.jsp file
Hi Lucas,
Thank you very much for this fantastic effort. Very very useful blog. Keep doing more. All the best.
Rajesh
I show the following preblem when trying his example of implementation,t says that the pl/sql presents errors but this well does not have an error in the
09/01/17 11:12:00 [DBProcDataSourceLoginModule] User ME not authenticated: username or password mismatch
09/01/17 11:12:00 [DBProcDataSourceLoginModule] Error: ORA-04063: package body “SCOTT.JAAS_UTILS” tiene errores
ORA-06508: PL/SQL: no se ha encontrado la unidad de programa llamada : “SCOTT.JAAS_UTILS”
ORA-06512: en lÃnea 1
09/01/17 11:12:00 [DBProcDataSourceLoginModule] Logon Successful = false
09/01/17 11:12:00 [DBProcDataSourceLoginModule] Abort called on LoginModule
That I am missing to configure or to that he is supposed to be for this givenly
Greetings and thanks
I am trying to learn this using baby steps. I have Oracle JExpress setup on my machine and I have the tables in there. I use JDeveloper. I have a million examples of file based security but not enough on how can I set up everything in my database schema tables and then using a simple jsp, login and authenticate against the passwords stored in the tables. Roles will also be stored in the database. Based on that, I want to give permissions to tables based on user roles. Is there an example or a good step by step. I want to use the basic (not custom) Login Modules first.
Thanks
Hello,
Currently, I do not have access to a database or LDAP server. Can you recommend a spoof login module?
Thanks
Chris
I have the DBLoginModule working in my JDEV application, however I am curious about the RAISE Exception in the PL/SQL package. It seems to me that this is not needed since the login module returns a binary state (i.e., it goes to your error form if bad, your app if okay). Is it possible to interrogate this exception? It looks like DBLogin just generates a LoginException if authentication fails. Thanks.
Lucas, never mind. I was able to invoke authentication after converting to a Package and refining my Data Sources. It appears to working and I will hopefully deploy to app server next week. Thanks.
Lucas, Thanks for the help with this. I am using a simple function to pass to the LM, and I am faced with the following. The function works fine from SQLPlus. Any comments are appreciated.
07/03/01 14:01:18 [DBProcDataSourceLoginModule] option debug = true
07/03/01 14:01:18 [DBProcDataSourceLoginModule] option data source name = jdbc/OracleDSSecurity
07/03/01 14:01:18 [DBProcDataSourceLoginModule] option log level = log all
07/03/01 14:01:18 [DBProcDataSourceLoginModule] option logger class = null
07/03/01 14:01:18 [DBProcDataSourceLoginModule] option plsql_procedure = my_data.get_j2ee_user_role
07/03/01 14:01:18 [DBProcDataSourceLoginModule] option application_realm = jazn.com
07/03/01 14:01:18 [DBProcDataSourceLoginModule] login called on DBTableLoginModule
07/03/01 14:01:18 [DBProcDataSourceLoginModule] Calling callbackhandler …
07/03/01 14:01:18 [DBProcDataSourceLoginModule] Username returned by callback = BLACKBURN
07/03/01 14:01:20 [DBProcDataSourceLoginModule] User BLACKBURN not authenticated: username or password mismatch
07/03/01 14:01:20 [DBProcDataSourceLoginModule] Error: User credentials doesn’t match the existing ones
07/03/01 14:01:20 [DBProcDataSourceLoginModule] Logon Successful = false
Imran,
Most steps in this article are not ADF specific and apply to web applications in general. The JAAS implementation however varies across application servers: the article discusses OC4J 10.1.3 (Oracle AS). For other Application Servers you will need to find other instructions. I am unfortunately not able to find the time to help you along with this. Surely out on the internet there are JAAS instructions for the environment you need. Good luck!
regards,
Lucas
hi lucas,
I want to implement JAAS in just one jsp file i am not awar of ADF and other things, can you give step by step guide to embed JAAS in single normal JSP, sorry for my lack of knowldge but i want to do this with baby steps.
regards
I followed your steps.
And my application is running on Embedded oc4j server successfully.
So Now I would like to run it on Standalone OC4J server.
But I got error the message was “Login Failure : all modules ignored.”
What should I do?
What should I have to check about my application for this error?
Hi Lucas,
I am happy to send you a copy of my orion-application.xml file for your comparison if that helps — just send me an email and I’ll reply.
Regards,
Todd Beets
Ginni,
That is an interesting challenge you describe. The solution I would suggest depends a little on the type of application and the way the security should be presented to the user.
Using VPD it is fairly simple to only allow update of certain columns when specific conditions are met. This can be implemented using a table policy and associated policy function for each combination of columns (or for every individual column) that is subject to the security policy. This requires the security rules to follow certain logic that can be expressed in a little PL/SQL or better still SQL logic.
However, with only VPD in place, the user can make the change in the application and will only be told that the update is not allowed when the change is committed. To make the user interface proactively deal with the security restrictions, you could query the result of a PL/SQL function that returns a bitmap (10011, 00000, 11111) for every record that specifies whether a column can be updated or not. This bitmap can be used to disable/enable field in the user interface – to prevent the user from changing a field based on a non-updateable column.
The record level security can likewise be approached with VPD (or a View with appropriate where clause calling a PL/SQL function if you do not use the Enterprise Edition and can suffer the performance degradation). Again you may want to have the user interface adapt to whether a record can be updated by the user or not (hide/display the save button, make all fields enabled/disabled).
Good luck!
Lucas
Thanks for your comment Todd and the additions you mention. I had tried those myself, but upon deploying the EAR file to OC4J, we ran into various issues. I did not succeed in deploying an EAR file with orion-application.xml file. I am glad that it worked for you though, as I had gathered from the documentation that one should be able to do that.
RE-POST to fix stripping of angle bracketed elements…
Thanks for this sample application — it was very helpful! I’m using 10.1.3.1 in a clustered configuration (an OC4J group with two instances). I made a couple additions to the sample project to further automate the deployment (especially important in a clustered configuration).
1) Added an orion-application.xml deployment descriptor to the project. In it we signal that we’re using a custom provider (jazn element with the custom.loginmodule.provider property set to true and define the relevant login module configuration in a jazn-loginconfig element. Per documentation, OracleAS takes care of the dirty work of maintaining system-jazn-data.xml with the application-specific jazn-loginconfig stanza -wierd, but serviceable.
In orion-application.xml I also import custom library “oracle.demo.dblogin” that I defined once for the target containers that brings in Oracle’s DB login demonstration jar.
2) Noticed that the sample app project was not mapping J2EE logical role “normaluser” to the physical role returned from the database “user”. Added an orion-web.xml file to the project that explicitly makes this mapping since the names are different.
Regards,
Todd
Lucas,
I am facing an issue involving the need for record level security. This is a typical requirement in our web apps: user creates a new record and can name, by name, other users who should be able to read and other users who should be able to update the record. Additionally, a group of global editors should be able to edit the record and a group of global readers should be able to see it. To further complicate things, certain columns in the tables may require specific access – sometimes granted by the user, sometimes granted by the workflow engine (probably BPEL). I read an article on dba-oracle.com called “Using VPD policy security” that stated column level security is difficult since it “can only be maintained by defining multiple views for each class of end user”. We could have hundreds of thousands of records, each with different readers and editors. Do you have any advise on how I could approach building such a security model?
Thanks for all of your great tips!
Ginni