I'm trying to implement a loginmodule so I can perform a "remember me" feature on my web app as well as hash my password with bcrypt. To build the class I used this tutorial. However I didn't manage to connect after implementing this. The passwords in db are hashed via SHA-256 at the moment and I suspect it is the reason why.
public class TestAuthModule implements
javax.security.auth.message.module.ServerAuthModule {
@SuppressWarnings("rawtypes")
protected static final Class[] supportedMessageTypes = new Class[] {
HttpServletRequest.class, HttpServletResponse.class };
private CallbackHandler handler;
public void initialize(MessagePolicy requestPolicy,
MessagePolicy responsePolicy, CallbackHandler handler,
@SuppressWarnings("rawtypes") Map options) throws AuthException {
System.out.println("initialize called.");
this.handler = handler;
}
@SuppressWarnings("rawtypes")
public Class[] getSupportedMessageTypes() {
return supportedMessageTypes;
}
public AuthStatus validateRequest(MessageInfo messageInfo,
Subject clientSubject, Subject serverSubject) throws AuthException {
HttpServletRequest request = (HttpServletRequest) messageInfo
.getRequestMessage();
String user = request.getParameter("user");
String group = request.getParameter("group");
System.out.println("validateRequest called.");
System.out.println("User = " + user);
System.out.println("Group = " + group);
authenticateUser(user, group, clientSubject, serverSubject);
return AuthStatus.SUCCESS;
}
public AuthStatus secureResponse(MessageInfo msgInfo, Subject service)
throws AuthException {
System.out.println("secureResponse called.");
return AuthStatus.SEND_SUCCESS;
}
public void cleanSubject(MessageInfo msgInfo, Subject subject)
throws AuthException {
if (subject != null) {
subject.getPrincipals().clear();
}
}
private void authenticateUser(String user, String group,
Subject clientSubject, Subject serverSubject) {
System.out
.println("Authenticating user " + user + " in group " + group);
CallerPrincipalCallback callerPrincipalCallback = new CallerPrincipalCallback(
clientSubject, user);
GroupPrincipalCallback groupPrincipalCallback = new GroupPrincipalCallback(
clientSubject, new String[] { group });
try {
handler.handle(new Callback[] { callerPrincipalCallback,
groupPrincipalCallback });
} catch (Exception e) {
e.printStackTrace();
}
}
}
and I login like this (which did work before implementing a custom loginmodule):
private String username;
private Password password;
//....
for (int i = 0; i < x -1 ; i++) {
this.password = PasswordEncoder
.toHex(PasswordEncoder
.hash512(this.password + salt));
}
// x is the number of time I hashed the password before storing it in db.
// x-1 because glassfish authentication does it once for me.
//...
try {
request.login(username, password + salt);
} catch (ServletException e)
Also on my pages I used to have a register and a sign in button that were displayed only if the user was null if not I had the username at the top. Now that I implemented this it's like the user is connected as "ANONYMOUS" (so there is "you are connected as ANONYMOUS" at the top of the page. To prevent this I did a temporary fix:
if (username == null || username.equals("ANONYMOUS")) {
this.isUserConnected = false;
} else {
this.isUserConnected = true;
}
I tried :
isUserInGroup("ANONYMOUS");
but there is no user so I'm getting a npe. I'm not sure how to go about this as well.
There are two alternative approaches you can employ here.
The first --which I personally favor-- would be to just forget about proprietary AS-provided JAAS
LoginModule
s (LMs) altogether and implement the authentication process in its entirety yourself, within yourServerAuthModule
's (SAM's)validateRequest
method. This gives you the ultimate freedom of choice with regard to how credentials are to be persisted (hashing/salting, DB schema) by the application and how the SAM shall perform validation of client-supplied credentials. Hence, the SAM is responsible for connecting to the DB in both a thread-safe and efficient manner in that case; that would be the sole potentially tricky aspect of this approach.Alternatively, your SAM can delegate credential validation to an AS-provided LM (or to one you authored, provided that it is applicable, i.e. a vendor-specific extension). That's what you've been trying to accomplish, I suppose. The SAM must then comply with both JASPIC's Servlet Container Profile and its LoginModule Bridge Profile (see chapter 6 of the spec); the latter, in a nutshell, mandates that:
initialize
method consults the value of thejavax.security.auth.login.LoginContext
key encapsulated within the runtime-providedoptions
. It also constructs an additionalCallbackHandler
that supportsNameCallback
andPasswordValidationCallback
. Finally, it instantiates a request-specificLoginContext
(LC) with the aforementioned instances (name
andcallbackHandler
arguments, respectively).validateRequest
delegates to LC'slogin
; then communicates anyPrincipal
(s) established by the underlying LM within LC'sSubject
to the AS (viaCallerPrincipalCallback
andGroupPrincipalCallback
).cleanSubject
delegates to LC'slogout
.Obviously, unless you wrote the LM yourself, no clean way of altering the credential validation process is available.
Some closing notes:
HttpServletRequest.login
will always raise an exception when JASPIC has been configured for use with your application (see § 3.9.2 of the spec). UseHttpServletRequest.authenticate
instead.validateRequest
should return a properAuthStatus
; namelySUCCESS
when login succeeded or authentication is optional (i.e. the requested resource is unprotected), regardless of the authentication outcome;SEND_CONTINUE
when redirecting the user to a login page;SEND_FAILURE
(or throw anAuthException
) when login failed and authentication is mandatory (i.e. the requested resource is protected or the user explicitly requested to be logged in).validateRequest
to the AS by supplying theCallerPrincipalCallback
/GroupPrincipalCallback
constructor with anull
principal or groups argument, respectively. A compatible Servlet container'sHttpServletRequest.getUserPrincipal
's implementation should then returnnull
. Note thatEJBContext.getCallerPrincipal
behaves differently, returning an instance of an AS-specific type in order to represent the unauthenticated user (perhaps that's what you witnessed). In any case, your authorization-related application-level code should not depend on the non-standard name of the anonymousPrincipal
. An alternative way of addressing the is-guest question could involve setting thejavax.servlet.http.authType
callback property (see § 3.8.4 of the spec) from your SAM on successful authentication. InvokingHttpServletRequest.getAuthType
would then not only assert whether the user is authenticated, but also whether your SAM, another SAM or AS-specific LM performed the authentication (think of an app leveraging multiple authentication types). Yet another way of dealing with this could be by passing a customPrincipal
implementation to theCallerPrincipalCallback
; then doing aninstanceof
on thePrincipal
exposed viaHttpServletRequest.getUserPrincipal
(but note that there are still issues, e.g. on GlassFish an EJB is needed to achieve the same result).See also: