package kuba.demo.jaas.module; import java.io.Serializable; import java.security.Principal; import java.util.logging.Logger; public class CustomPrincipal implements Principal, Serializable { private static Logger log =Logger.getLogger(CustomPrincipal.class.getName()); private static final long serialVersionUID = 1L; private String name; public CustomPrincipal(String name) { if (name == null){ log.severe("Brak principal name"); throw new NullPointerException("Principal name is null"); } this.name = name; } @Override public String getName() { return name; } public String toString() { return("CustomPrincipal-" + name); } public boolean equals(Object o) { if (o == null){ return false; } if (this == o){ return true; } if (!(o instanceof CustomPrincipal)){ return false; } CustomPrincipal that = (CustomPrincipal)o; if (this.getName().equals(that.getName())){ return true; } return false; } public int hashCode() { return name.hashCode(); } }
Kolejną wymaganą klasą jest CustomCallbackHandler - implementuje interfejs javax.security.auth.callback.CallbackHandler. Można powiedzieć, że jest ona pośrednikiem pomiędzy naszą aplikacją, a usługą logowania, przekazując jej nasze dane uwierzytelniające (credentials).
package kuba.demo.jaas.module; import java.io.IOException; import java.util.logging.Logger; 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; public class CustomCallbackHandler implements CallbackHandler { private static Logger log =Logger.getLogger(CustomCallbackHandler.class.getName()); private String user; private String password; public CustomCallbackHandler(String user, String password) { this.user = user; this.password = password; } @Override public void handle(Callback[] callbacks) throws IOException,UnsupportedCallbackException { NameCallback nameCallback = (NameCallback)callbacks[0]; PasswordCallback passwordCallback = (PasswordCallback)callbacks[1]; nameCallback.setName(user); passwordCallback.setPassword(password.toCharArray()); log.info("Inicjalizacja callback handler'a dla uzytkownika: "+user); } }
Pozostała jeszcze klasa odpowiadająca za uwierzytelnienie użytkownika czyli właściwy moduł logowania. Moduł taki - CutomLoginModule.java - musi implementować interfejs javax.security.auth.spi.LoginModule. Cała logika odpowiedzialna za uwierzytelnienie odbywa się w metodzie login(). Możemy w niej sięgnąć do bazy danych lub serwera usług katalogowych. Jeśli autentykacja nie powiedzie się, zwrócony zostanie wyjątek javax.security.auth.login.LoginException. Jeśli logowanie będzie poprawne wywoływana jest metoda commit(). Przesłaniając ją możemy przypisujemy naszemu uwierzytelnionemu obiektowi (Subject) odpowiednie właściwości. W przypadku gdy któraś z powyższych metod zwróci błąd, wywoływana jest metoda abort().
package kuba.demo.jaas.module; import java.security.Principal; import java.util.Map; import java.util.logging.Logger; import javax.security.auth.Subject; 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.login.LoginException; import javax.security.auth.spi.LoginModule; public class CustomLoginModule implements LoginModule{ private static Logger log =Logger.getLogger(CustomLoginModule.class.getName()); private Subject subject; private CallbackHandler callbackHandler; private Map sharedState; private Map options; private String userName; private String userPassword; @Override public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { this.subject = subject; this.callbackHandler = callbackHandler; this.sharedState = sharedState; this.options = options; } @Override public boolean login() throws LoginException { NameCallback nameCallback = new NameCallback("Username"); PasswordCallback passwordCallback = new PasswordCallback("Password",false); Callback[] callbacks = new Callback[]{ nameCallback, passwordCallback }; try { callbackHandler.handle(callbacks); } catch (Exception e) { e.printStackTrace(); } userName = nameCallback.getName(); userPassword = String.valueOf(passwordCallback.getPassword()); if("jakub.pawlowski".equals(userName) && "my_password".equals(userPassword)){ log.info("Poprawnie zalogowano uzytkownika [ "+userName+" ]"); return true; }else{ log.info("Logowanie nie powiodlo sie"); return false; } } @Override public boolean commit() throws LoginException { Principal principal = new CustomPrincipal(userName); subject.getPrincipals().add(principal); userPassword = null; log.info("Dodano obiekt CustomPrincipal"); return true; } @Override public boolean abort() throws LoginException { log.info("Wywolanie abort()"); userName = null; userPassword = null; return true; } @Override public boolean logout() throws LoginException { log.info("Uzytkownik [ "+userName+" ] zostal wylogowany"); userName = null; userPassword = null; return true; } }
Ostatnim krokiem jest utworzenie pliku konfiguracyjnego wskazującego na mój moduł logowania. Ja nazwałem go custom_jaas.config
MyLoginModule { kuba.demo.jaas.module.CustomLoginModule REQUIRED; };
Czas na sprawdzenie jak to wszystko działa. Poniżej prosty klient, należy go uruchomić z opcją wskazującą na plik konfiguracyjny: -Djava.security.auth.login.config=="e:/custom_jaas.config"
package kuba.demo.jaas.client; import java.security.Principal; import java.util.Iterator; import java.util.Set; import java.util.logging.Logger; import javax.security.auth.Subject; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import kuba.demo.jaas.module.CustomCallbackHandler; import kuba.demo.jaas.module.CustomPrincipal; public class CustomLoginModuleClient { private static Logger log =Logger.getLogger(CustomLoginModuleClient.class.getName()); public static void main(String[] args) { CustomCallbackHandler callbackHandler = new CustomCallbackHandler("jakub.pawlowski", "my_password"); try { LoginContext loginContext = new LoginContext("MyLoginModule",callbackHandler); loginContext.login(); Subject s = loginContext.getSubject(); Set ps = s.getPrincipals(); Iterator iter = ps.iterator(); while(iter.hasNext()){ Principal principal = (Principal)iter.next(); if(principal instanceof CustomPrincipal){ log.info("Principal [ "+principal.getName()+" ]"); } } loginContext.logout(); } catch (LoginException e) { e.printStackTrace(); } } }
Jak widać poniżej autoryzacja przebiegła poprawnie
INFO: Poprawnie zalogowano uzytkownika [ jakub.pawlowski ] 2010-10-23 19:28:09 kuba.demo.jaas.module.CustomLoginModule commit INFO: Dodano obiekt CustomPrincipal 2010-10-23 19:28:09 kuba.demo.jaas.client.CustomLoginModuleClient main INFO: Principal [ jakub.pawlowski ] 2010-10-23 19:28:09 kuba.demo.jaas.module.CustomLoginModule logout INFO: Uzytkownik [ jakub.pawlowski ] zostal wylogowany