For a long time I’ve been using FORM based authentication, logging in with the j_security_check, and then using the getRemoteUser() as a starting point for user-related functionality. But I got a bit tired of letting all the control of my login to the j_security_check, as this one is very difficult to customize. I wanted to do a login to the application programmatically, and started to investegate.
My first try was to use the LoginContext (javax.security.auth.login.LoginContext) and do a JAAS login, as I found an article here that describes this in a good way. It seems plain and simple:
In the login-config.xml you create your LoginModule configuration, something like this:
<application-policy name = "myLoginConfiguration"> <authentication> <login-module code = "org.jboss.security.auth.spi.DatabaseServerLoginModule" flag = "required"> <module-option name = "unauthenticatedIdentity">guest</module-option> <module-option name = "dsJndiName">java:/MyDataSourceConfig</module-option> <module-option name = "principalsQuery">Select passord from users where userid=?</module-option> <module-option name = "rolesQuery">Select role, 'Roles' from user_roles where userid=?</module-option> </login-module> </authentication> </application-policy>
Imagine when doing a login, we need some way of passing the username and the password to the login module, and this can be done by using a callback handler. In this example I’ve been using a PassiveCallbackHandler, which basically have a constructor for username and password, so that it doesn’t really care where you get the username and password from (could be from the request, a bean etc). For reference I display the PassiveCallbackHandler here:
import java.io.*; /* JAAS imports */ import javax.security.auth.callback.*; /** * * PassiveCallbackHandler has constructor that takes * a username and password so its handle() method does * not have to prompt the user for input. * Useful for server-side applications. * * @author Paul Feuer and John Musser * @version 1.0 */ public class PassiveCallbackHandler implements CallbackHandler { private String username; char[] password; /** * Creates a callback handler with the give username * and password. */ public PassiveCallbackHandler(String user, String pass) { this.username = user; this.password = pass.toCharArray(); } /** * Handles the specified set of Callbacks. Uses the * username and password that were supplied to our * constructor to popluate the Callbacks. * * This class supports NameCallback and PasswordCallback. * * @param callbacks the callbacks to handle * @throws IOException if an input or output error occurs. * @throws UnsupportedCallbackException if the callback is not an * instance of NameCallback or PasswordCallback */ public void handle(Callback[] callbacks) throws java.io.IOException, UnsupportedCallbackException { for (int i = 0; i < callbacks.length; i++) { if (callbacks[i] instanceof NameCallback) { ((NameCallback)callbacks[i]).setName(username); } else if (callbacks[i] instanceof PasswordCallback) { ((PasswordCallback)callbacks[i]).setPassword(password); } else { throw new UnsupportedCallbackException( callbacks[i], "Callback class not supported"); } } } /** * Clears out password state. */ public void clearPassword() { if (password != null) { for (int i = 0; i < password.length; i++) password[i] = ' '; password = null; } } } [/sourcecode] And then from java side in your login-method you are told simply to do: [sourcecode language='java'] LoginContext lc = new LoginContext("myLoginConfiguration", new PassiveCallbackHandler(user,pass)); try { lc.login(); } catch (LoginException) { // Authentication failed. }[/sourcecode] Fine, you think. If you try to get the user from lc.getSubject() it works. ๐ But still, if you try getUserPrincipal() or getRemoteUser(), those are null. This is because even if you have successfully logged in using jaas, the web container in jboss (tomcat) doesn't know. And there is no way of "feeding" this knowledge to the web tier, you cannot do a set on the remote user, and there are no magic calls you have forgotten. What to do then??Depending on which jboss version you are running, there are different solutions. If you use 4.2.2 or higher, you are the lucky one. If you use an older version, there's a longer path to walk.For my part I have upgraded to 4.2.2 (which seems to be running nice), and then you have the luxury of a new class called WebAuthentication (org.jboss.web.tomcat.security.login.WebAuthentication). This is a class that has been done to solve the above issue, namely to do a proper login to the web container. It is found inside the jbossweb-service.jar, so you need to have this one in your classpath. It is VERY simple to use it, just do like this: [sourcecode language='java'] WebAuthentication webA = new WebAuthentication(); webA.login(username, password);[/sourcecode] And you're on ๐ Both getUserPrincipal() and getRemoteUser() works. This was something I've been waiting for at least, now you can finally have control of the login procedure, not to mention you can go wherever you want after doing a successful login. (A small pinch in the side to j_security_check ๐ ) The only downside I've found (this far) is that the 4.2.2 doesn't support Single-Sign-On using the valve yet( in server.xml: <Valve className="org.apache.catalina.authenticator.SingleSignOn" /> ). But according to <a href="http://wiki.jboss.org/wiki/Wiki.jsp?page=WebAuthentication">this</a> page this is to be resolved when jboss 5 is released.But then, what about those of you that uses older versions of jboss? Unfortunately, there are no ways (that I have found) which gives you the same effect as with the WebAuthentication. I believe that what you need to do here is to forget about remoteUser and userPrincipal, and play against the loginContext instead. Let's take an example from jsf, and say that you have a LoginHandler with a method getLoggedOnUsername. Instead of getting the username from externalContext.getRemoteUser(), get it from the loginContext:Old way public String getLoggedOnUserName() { FacesContext context = FacesContext.getCurrentInstance(); ExternalContext externalContext = context.getExternalContext(); String remoteUser = externalContext.getRemoteUser(); if(remoteUser != null) return remoteUser; else return "guest"; }
New way
public String getLoggedOnUserName() { String username = lc.getSubject() if(username != null) return username; else return "guest"; }
This of course assumes that the LoginHandler holds the LoginContext object.Now, for both versions you would need to decide when to login to you application. Are all the pages protected? Or just a subset of them? Are all pages open but there’s additional functionality available when a user is logged in and not a guest? Depending on these answers there are different ways of handling the authentication mechanism. If you use WebAuthentication you can actually let JAAS trigger the login for you, just as we did with j_security_check, only that you can set the action of your form to where you want to go and control the login yourself. Another alternative could be to create a security filter with the url-pattern of your protected resources.
<filter-mapping> <filter-name>SecurityFilter</filter-name> <url-pattern>/protected/*</url-pattern> </filter-mapping>
I haven’t done this myself, but there is a project here that could be a good starting point for this. ๐
19.02.2008 at 17:02:25
Glad that you found our “Programmatic Web Login” feature useful.
20.02.2008 at 01:02:19
Anil,
Yes, indeed! I really welcome this feature ๐ I left a message for you at your blog, but thanks anyway!
13.03.2008 at 22:03:54
hey man,
sorry to bother you, but i am quite desperate with the following:
http://www.jboss.com/index.html?module=bb&op=viewtopic&t=131498
as you seem to be an expert on JBoss JAAS i will really appreciate some help here.
thansk,
Rami
15.03.2008 at 02:03:20
Rami, I’ve posted a reply on the thread you were pointing to, hopefully this will be able to solve your problems. As soon as I have time I will also do a post here using the same examples.
Regards, Eivind
15.03.2008 at 02:03:36
[…] Perform a JAAS programmatic login in Jboss – try to solve the “empty” remote user p… Blog at WordPress.com. • Theme: Garland by Steven Wittens and Stefan […]
17.03.2008 at 11:03:34
hey Eivind,
i’m not the lucky one because i’m using jboss 4.2.0. Have you any idea where i could find some more information about the “longer path to walk”… to get the login information to the web tier ?
thanks,
alex
24.03.2008 at 09:03:41
thanks main, i got up and running…
see:
http://www.jboss.com/index.html?module=bb&op=viewtopic&t=131498
28.03.2008 at 01:03:48
Alex,
Sorry I haven’t replied to your comment before now, for some reasons it was picked up by Akismet and put inside the spam folder.
But I would have to dissappoint you when it comes to your question, I spent a lot of time and tried a lot of stuff to do a proper programmatic logon with jboss versions prior to 4.2.2, but didn’t succeed..
I guess if you want to dig into it a place to start could be the source code for jboss and see what the j_security_check actually does, and then figure out if you’re able to redo this programmatically.
But for me digging this far was not worth it as I didn’t have any negative consequences/problems upgrading to 4.2.2, and then there was a solution ready to use..
Sorry I couldn’t be of more help!
25.04.2008 at 10:04:16
This is great, a big improvement.
However, how do we get the exceptions thrown from the login module?
For instance, my custom login module may throw a credential expired exception or fail when logging into an external host system. How do I get that exception when using WebAuthentication so I can act accordingly?
A bool flag just doesn’t cut it.
Thanks for the article.
25.04.2008 at 11:04:04
Hi MakkaPakka,
It is a very good question you ask, I struggled with the same, and unfortunately I didn’t find any good answers. I had a look at the source of WebAuthentication, hoping that I could override the login() method and perhaps return some status codes depending on login-module, but I didn’t manage and didn’t have too much available time to look into this.
Still I felt like using the programmatic login insted of old j_security_check, so I did some sort of a “workaround” by using a class I named LoginManager. The LoginManager has methods like doesExist(), isActive(), hasExpired() and so on. So I basically do some testing myself before passing the username/credentials on to the WebAuthentication login-method. The big downside by doing something like this is that you in many ways need to duplicate the work done by the login-module, and your LoginServlet also needs to have “knowledge” about what type of authentication store you use (db,ldap,properties-files etc).
Hopefully there will be some more possibilities handling this with jboss 5 ๐
Regards,
Eivind
25.04.2008 at 11:04:59
Thanks for the reply Eivind.
This has been a source of frustration for some time now. It’s a massive problem with the environment.
I tried using a LoginContext directly and making a bean call to force the authentication, but that only partially works as you’re still not authenticated in tomcat.
Not being able to get the precise authentication errors in this day and age is ridiculous.
If I have any joy I will share it here.
25.04.2008 at 16:04:40
Take a look at org.jboss.web.tomcat.security.HttpServletRequestLoginModule in jboss. Think you can stash the failure in the session and then get it back in your bean when WebAuthentication.login fails.
Hacky but may well work.
I will try it and let you know.
15.05.2008 at 09:05:04
This is a very good post. WebAuthentication class was a very handy class. It solves many of my problems in using j_security_check. In my experiance its better to do programmatic web authentication (ActiveAuthentication) than relying on the j_security_check (not a flexible implementation at all).
15.05.2008 at 09:05:49
Hi Eivind
I got a question. Is there any standard way to pass some thing else other than the status of the authentication,authorisation form loginmodules.
In my scenario i have to login to a thrid party system and when i login they give a session id (of that system ) and all subsequent request to that system requires this id. So basically when the user access my app there are 2 login modules. These are chained. One is the sites own login and other is the loginmodule for the 3rd party system. My current solution is to insert the session id got from the 3rd party system to a database from the loginModule and pick it from the action class from the web tier. Is this the correct method or any other standard method exists for the same ?
16.05.2008 at 09:05:01
Prem,
Thank you for your interest in my post. ๐
Unfortunately I don’t know of any standard way of handling the scenario you described. Do you pass the session id to the 3rd party system as a url-parameter?
The only idea I can think of, If you don’t want to handle this in your action class, is to use a filter that on all requests set this parameter for you. Or, I guess there could be possibilities to use something like the org.jboss.web.tomcat.security.HttpServletRequestLoginModule, during login set this id in the session and retrieve it from there? I’ve never tried this myself, but I’ve heard others speaking of such possibilites.
Sorry I couldn’t help you out more on this. ๐
19.05.2008 at 13:05:19
Hi Eivind,
Thanks for the reply. I think the HttpServletRequestLoginModule sounds a good option.
I can add this module also to the JAAS chain and from this module i can set the value in session.
19.06.2008 at 15:06:17
Hi guys,
Unfortunatelly the LoginContext.login() method did not work for me! Login is performed accordingly to my DatabaseLoginModule but when the pages does not authenticate and tomcat remains unauthenticated leaving the login page…
I even placed a filter making a login but tomcat still remains unauthenticated!
Any suggestions?
20.06.2008 at 09:06:07
George,
To be able to help you I need a bit more information.. ๐
I donโt know why your LoginContext.login() doesnโt work, but the best way to start is to get some more debug about whatโs going on. Have a look at this page (http://www.jboss.org/community/docs/DOC-12198) and add the debugging statements for the sequrity layer. Then see if you get some more information.
Also, I can say that for jboss the role ‘Roles’ are required for all users to be able to log on. ๐
05.07.2008 at 22:07:43
Hello everyone
I am am struggling to implement this solution. I am using jboss-4.2.2.
loginContext.login() completes successfully ( I am using DatabaseServerLoginModule) but webAuthentication.login(name,pass) throws exception.
llegalStateException: request is null at org.jboss.web.tomcat.security.login.WebAuthentication.login(WebAuthentication.java:80)
What am I missing? Can somebody give a hint please?
Thank you
08.07.2008 at 01:07:39
Andrei,
If you have the time you could perhaps test the scenarios described in this post? https://roneiv.wordpress.com/2008/03/15/using-webauthentication-in-jboss/
Just to be able to discover where the error might be. ๐
The source of your problem could be different things, the login-config.xml, the web.xml etc.
In login-config, have you remembered to include the “Roles” role in your roles-query? It is a mandatory group in jboss, without it authentication fails.
Also, in your web.xml you need to have at least ONE role defined in auth-constraint that is a valid role for the user you try to log on with. ๐
Possibly you could also add debugging statements of the security layer to see if something strange is going on. Follow the explanations here: http://www.jboss.org/community/docs/DOC-12198)
Good luck!
03.09.2008 at 15:09:15
Andrei.
Im running into the same problem with JBOSS 4.2.2. Has anyone found a solution.
Thanks.
Micah
17.10.2008 at 12:10:34
Hi,
I have tried Programmic WebAuthentication in JBoss portal 2.6.6..
i m calling LoginServelt from login.jsp and itโs working fine..
in LoginServlet.java
WebAuthentication webAuthentication = new WebAuthentication();
if(webAuthentication.login(user, pass))
{
System.out.println(โIn Web Authentication โ);
System.out.println(โredirectUrl : โ+redirectUrl);
String referer = req.getHeader(โRefererโ);
resp.sendRedirect(โhttp://localhost:8080/portal/auth/dashboardโ);
}
This is working fine if i login from login link on Portal home page..but, in my real scenario i ve to login it from outside..so i m running one simple html throgh apache
http://localhost/scripts/login.html..in this login.html i m calling LoginServlet of Portal..
it is doing Authentication but not doing authorizationโฆand so if do do re-login..it worksโฆ
Am i missing something, please help me..
thanx in advance
27.11.2008 at 14:11:11
Fantastic tutorial m8. Thanks a million. This one really saved my day. ๐
20.02.2009 at 09:02:33
Hi Jigna,
resp.sendRedirect(โhttp://localhost:8080/portal/auth/dashboardโ);
instead of sendredirect()
plz use a dispathcer .
02.04.2009 at 11:04:28
Hello,
I’ve found the maven depencency for JBoss 5.0.0. Do be able to import the WebAuthentication add the following maven depencency to your pom:
org.jboss.jbossas
jboss-as-tomcat
provided
5.0.0.GA
After that you can import the class:
import org.jboss.web.tomcat.security.login.WebAuthentication;
15.04.2009 at 00:04:55
Thanks for sharing!
07.04.2011 at 16:04:54
Thanks a lot!!!! Was searching for that info!
09.04.2009 at 21:04:23
thanks for the nice article…
15.08.2009 at 02:08:56
Roneiv,
I’ve been trying to do JAAS form based authentication on jboss but its failing all the time…
please take a look at my posting, it has not been answered since I post it on the jobss community forum.
http://www.jboss.org/index.html?module=bb&op=viewtopic&t=158664
15.08.2009 at 16:08:13
Mgibson,
I tried to follow your link but I ended up on the forum front page.. ๐
– E –
18.10.2009 at 20:10:33
[…] between JBoss 4 and JBoss 5 (if you want to fight this problem, good luck: you can start from here and here. Please share the results if you […]
30.04.2010 at 14:04:45
Dear roniev,
You just saved me lots of effort with you post.
I really thank you.
Dom
08.11.2010 at 00:11:06
hey man, you totally saved the day! thank you so much, you rock!
29.07.2011 at 21:07:31
Hi.
Did you try it in JBoss 7?
26.01.2012 at 05:01:51
Can anyone point me in direction for finding reference to configure jboss seam project to use oracle access manager for authentication and authorizaion?
25.01.2013 at 10:01:21
Is there an update in place for JBoss 5.1.0??
25.01.2013 at 10:01:12
Unfortunately not, I don’t have time to manage this blog very well.. ๐