Just playing with JAAS the other day. Basically its a general set of interfaces and classes (framework) provided by Java to perform authorization and authentication.
- Here is the top level code used for authorization using JAAS.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
package com.jassdemo; import java.security.Principal; import java.util.Set; import javax.security.auth.Subject; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import com.handler.DummyCallbackHandler; public class JaasTest { public static void main(String args[]) { System.setProperty("java.security.auth.login.config", "jaas.config"); //config file! LoginContext lc = null; try { lc = new LoginContext("Example", new DummyCallbackHandler("username","password")); lc.login(); Subject sub=lc.getSubject(); Set<Principal> principals=sub.getPrincipals(); Set<Object> credentials=sub.getPublicCredentials(); System.out.println(principals); System.out.println(credentials); } catch (LoginException e) { // Authentication failed. e.printStackTrace(); } // Authentication successful, we can now continue. // We can use the returned Subject if we like. Subject sub = lc.getSubject(); } }
- Create a jaas.config file that contains names of modules that are responsible for authentication. “Example” represents a set of LoginModules used to authenticate someone (called a Subject). jaas.config should be in the classpath. We use this name when creating a new LoginContext(String name, CallbackHandler callbackHandler).
Example { com.module.DummyLoginModule optional username="username" password="password"; com.module.AlwaysLoginModule required; };
- Every source that you wish to authenticate against will have its own LoginModule. Your login module implements the LoginModule interface. You have some built in ones, make sure you check if any of them work for you. However, if you want to authenticate against some custom data source (webservice? DB? etc) you would need to create your own modules. In case of my example i have a AlwaysLoginModule (which always logs in irrespective of username/pass) and a DummyLoginModule which authenticates against a hardcoded user/pass.
Here is a brief description of methods in a module.
- initialize() method initialize’s objects for the LoginModule. Parameters passed in the Example config can be read here.
- login() method is where authentication happens. Principal and Credential objects are initialized.
- commit(). One config can have multiple LoginModules. commit is called if LoginContext’s overall authentication succeeded. In the example above, since DummyLoginModule is optional, even if it doesn’t succeed the overall authentication will succeed if AlwaysLoginModule succeeds. So commit will be called.
- abort() One config can have multiple LoginModules. abort is called if LoginContext’s overall authentication failed. In the example above, since AlwaysLoginModule is required, if it doesn’t succeed the overall authentication will fail and abort will be called.
- logout() method uninitialize’s objects within the LoginModule.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
package com.module; import java.util.Map; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; import com.credentials.MyUserCredentials; import com.principal.MyUserPrincipal; public class AlwaysLoginModule implements LoginModule { private Subject subject; private CallbackHandler callbackHandler; private Map<String, ?> sharedState; private Map<String, ?> options; private MyUserPrincipal principal; private MyUserCredentials credentials; private boolean success; @Override public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) { this.subject = subject; this.callbackHandler = callbackHandler; this.sharedState = sharedState; this.options = options; success = false; } @Override public boolean login() throws LoginException { // always login success! // add these if commit was called! principal=new MyUserPrincipal("alwaysloggedinusername"); credentials=new MyUserCredentials(); credentials.put("guestpermission", true); success = true; return true; } @Override public boolean commit() throws LoginException { if ( success ) { subject.getPrincipals().add(principal); subject.getPublicCredentials().add(credentials); return true; } return false; } @Override public boolean abort() throws LoginException { if ( success ) { callbackHandler = null; sharedState = null; options = null; subject.getPrincipals().remove(principal); subject.getPublicCredentials().remove(credentials); principal = null; credentials = null; // TODO Auto-generated method stub return true; } return false; } @Override public boolean logout() throws LoginException { callbackHandler = null; sharedState = null; options = null; success = false; subject.getPrincipals().remove(principal); subject.getPublicCredentials().remove(credentials); principal = null; credentials = null; return true; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
package com.module; import java.io.IOException; import java.util.Map; import javax.security.auth.Subject; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; import com.callback.NamePassCallback; import com.credentials.MyUserCredentials; import com.principal.MyUserPrincipal; import com.principal.MyUserRole; public class DummyLoginModule implements LoginModule { private Subject subject; private CallbackHandler callbackHandler; private Map<String, ?> sharedState; private Map<String, ?> options; private MyUserPrincipal principal; private MyUserCredentials credentials; private boolean success; @Override public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) { this.subject = subject; this.callbackHandler = callbackHandler; this.sharedState = sharedState; this.options = options; success = false; } @Override public boolean login() throws LoginException { if ( callbackHandler == null ) { throw new LoginException("no handler"); } // Callback is just like a VO object, that the handler will populate! NamePassCallback namePassCallback=new NamePassCallback("NamePassCallback Prompt!!"); Callback[] callbacks = new Callback[] { namePassCallback }; try { callbackHandler.handle(callbacks); } catch (IOException e) { throw new LoginException("handler io error"); } catch (UnsupportedCallbackException e) { throw new LoginException("unsupported callback error"); } String username = namePassCallback.getUser(); String password = new String(namePassCallback.getPass()); if ( options.get("username").equals(username) && options.get("password").equals(password) ) { // add these if commit was called! principal=new MyUserPrincipal(username); credentials=new MyUserCredentials(); credentials.put("allpermission", true); credentials.put("grantpermission", true); success = true; } else { throw new LoginException("incorrect username/password"); } return true; } @Override public boolean commit() throws LoginException { if ( success ) { subject.getPrincipals().add(principal); subject.getPublicCredentials().add(credentials); return true; } return false; } @Override public boolean abort() throws LoginException { if ( success ) { callbackHandler = null; sharedState = null; options = null; principal = null; credentials = null; return true; } return false; } @Override public boolean logout() throws LoginException { callbackHandler = null; sharedState = null; options = null; success = false; subject.getPrincipals().remove(principal); subject.getPublicCredentials().remove(credentials); principal = null; credentials = null; return true; } }
- LoginModules use CallbackHandlers to get authentication data from users. We use Callback objects to get data out of handlers. Your handlers need to know what callbacks need to be handled, put data into them and send them back. Basically, think of Callback as data transfer objects that the LoginModule sends to the CallbackHandler, which are populated by the handler and sent back to LoginModule for use.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
package com.handler; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; import com.callback.NamePassCallback; public class DummyCallbackHandler implements CallbackHandler { String user; String pass; public DummyCallbackHandler(String user, String pass) { this.user = user; this.pass = pass; } @Override public void handle(Callback[] callbacks) throws UnsupportedCallbackException { for ( int i = 0; i < callbacks.length; i++ ) { if (callbacks[i] instanceof NamePassCallback) { NamePassCallback namepassCallback = (NamePassCallback)callbacks[i]; System.out.println("No need for prompt, we'll just use values in: " + namepassCallback.getPrompt()); namepassCallback.setUser(user); namepassCallback.setPass(pass); } else if (callbacks[i] instanceof NameCallback) { NameCallback nameCallback = (NameCallback)callbacks[i]; nameCallback.setName(user); } else if (callbacks[i] instanceof PasswordCallback) { PasswordCallback passCallback = (PasswordCallback)callbacks[i]; passCallback.setPassword(pass.toCharArray()); } else { throw new UnsupportedCallbackException(callbacks[i], "UnsupportedCallbackException"); } } } }
You can download my JAAS demo code here. There is this great post on Jaas in java world titled “All that JAAS” that was very helpful.