From 3360e2d51af193a13b9d2e33c6e96d3567835e16 Mon Sep 17 00:00:00 2001
From: Ray Krueger
- * User: raykrueger@users.sourceforge.net
- * Date: Jul 15, 2004
+ * An {@link AuthenticationProvider} implementation that retrieves user details
+ * from a JAAS login configuration.
+ *
+ * This AuthenticationProvider
is capable of validating {@link
+ * net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken} requests contain the correct username and password.
+ *
+ * <property name="loginConfig"> + * <value>/WEB-INF/login.conf</value> + * </property> + *+ * + * + * + * The loginContextName should coincide with a given index in the loginConfig specifed. + * The loginConfig file used in the JUnit tests appears as the following... + *
+ * JAASTest { + * net.sf.acegisecurity.providers.jaas.TestLoginModule required; + * }; + *+ * Using the example login configuration above, the loginContextName property would be set as JAASTest... + *
+ * <property name="loginContextName"> + * <value>JAASTest</value> + * </property> + *+ * + * + * + * When using JAAS login modules as the authentication source, sometimes the + * LoginContext + * will require CallbackHandlers. + * The JAASAuthenticationProvider uses an internal CallbackHandler to + * wrap the {@link JAASAuthenticationCallbackHandler}s configured in the ApplicationContext. When the LoginContext calls + * the internal CallbackHandler, control is passed to each {@link JAASAuthenticationCallbackHandler} for each Callback passed. + * + * + * {@link JAASAuthenticationCallbackHandler}s are passed to the JAASAuthenticationProvider through the + * {@link #setCallbackHandlers(net.sf.acegisecurity.providers.jaas.JAASAuthenticationCallbackHandler[]) callbackHandlers} property. + *
+ * <property name="callbackHandlers"> + * <list> + * <bean class="net.sf.acegisecurity.providers.jaas.TestCallbackHandler"/> + * <bean class="{@link JAASNameCallbackHandler net.sf.acegisecurity.providers.jaas.JAASNameCallbackHandler}"/> + * <bean class="{@link JAASPasswordCallbackHandler net.sf.acegisecurity.providers.jaas.JAASPasswordCallbackHandler}"/> + * </list> + * </property> + *+ * + * + * + * After calling LoginContext.login(), the JAASAuthenticationProvider will retrieve the returned Principals from the Subject (LoginContext.getSubject().getPrincipals). + * Each returned principal is then passed to the configured {@link AuthorityGranter}s. An AuthorityGranter is a mapping between a returned Principal, and a role name. + * If an AuthorityGranter wishes to grant an Authorization a role, it returns that role name from it's {@link AuthorityGranter#grant(java.security.Principal)} method. + * The returned role will be applied to the Authorization object as a {@link GrantedAuthority}. + * + * AuthorityGranters are configured in spring xml as follows... + *
+ * <property name="authorityGranters"> + * <list> + * <bean class="net.sf.acegisecurity.providers.jaas.TestAuthorityGranter"/> + * </list> + * </property> + * + *+ * + * + * @author Ray Krueger + * @version $Id$ */ public class JAASAuthenticationProvider implements AuthenticationProvider, InitializingBean, ApplicationContextAware { @@ -45,44 +116,62 @@ public class JAASAuthenticationProvider implements AuthenticationProvider, Initi private JAASAuthenticationCallbackHandler[] callbackHandlers; private AuthorityGranter[] authorityGranters; + /** + * Attempts to login the user given the Authentication objects principal and credential + * + * @param auth The Authentication object to be authenticated. + * @return The authenticated Authentication object, with it's grantedAuthorities set. + * @throws AuthenticationException This implementation does not handle 'locked' or 'disabled' accounts. + * This method only throws a AuthenticationServiceException, with the message of the LoginException that will be thrown, + * should the loginContext.login() method fail. + */ public Authentication authenticate(Authentication auth) throws AuthenticationException { if (auth instanceof UsernamePasswordAuthenticationToken) { UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) auth; try { + //Create the LoginContext object, and pass our InternallCallbackHandler LoginContext lc = new LoginContext(loginContextName, new InternalCallbackHandler(auth)); + + //Attempt to login the user, the LoginContext will call our InternalCallbackHandler at this point. lc.login(); + + //create a set to hold the authorities, and add any that have already been applied. Set authorities = new HashSet(); if (token.getAuthorities() != null) { authorities.addAll(Arrays.asList(token.getAuthorities())); } - Subject subject = lc.getSubject(); - - - Set principals = subject.getPrincipals(); + //get the subject principals and pass them to each of the AuthorityGranters + Set principals = lc.getSubject().getPrincipals(); for (Iterator iterator = principals.iterator(); iterator.hasNext();) { Principal principal = (Principal) iterator.next(); for (int i = 0; i < authorityGranters.length; i++) { AuthorityGranter granter = authorityGranters[i]; String role = granter.grant(principal); + //If the granter doesn't wish to grant any authority, it should return null. if (role != null) { authorities.add(new JAASGrantedAuthority(role, principal)); } } } + //Convert the authorities set back to an array and apply it to the token. token.setAuthorities((GrantedAuthority[]) authorities.toArray(new GrantedAuthority[authorities.size()])); + //Publish the success event context.publishEvent(new JAASAuthenticationSuccessEvent(token)); + //we're done, return the token. return token; } catch (LoginException e) { context.publishEvent(new JAASAuthenticationFailedEvent(auth, e)); + //We have no way of knowing what caused the exception, so we cannot throw BadCredentialsException, DisabledException, or LockedException. + //So we'll just throw an AuthenticationServiceException throw new AuthenticationServiceException(e.toString()); } } @@ -101,6 +190,11 @@ public class JAASAuthenticationProvider implements AuthenticationProvider, Initi return loginContextName; } + /** + * Set the loginContextName, this name is used as the index to the configuration specified in the loginConfig property. + * + * @param loginContextName + */ public void setLoginContextName(String loginContextName) { this.loginContextName = loginContextName; } @@ -109,7 +203,13 @@ public class JAASAuthenticationProvider implements AuthenticationProvider, Initi return loginConfig; } - public void setLoginConfig(Resource loginConfig) throws IOException { + /** + * Set the JAAS login configuration file. + * + * @param loginConfig Spring Resource + * @see JAAS Reference + */ + public void setLoginConfig(Resource loginConfig) { this.loginConfig = loginConfig; } @@ -127,22 +227,46 @@ public class JAASAuthenticationProvider implements AuthenticationProvider, Initi Security.setProperty("login.config.url." + n, loginConfig.getURL().toString()); } + /** + * @return the JAASAuthenticationCallbackHandlers. + * @see #setCallbackHandlers(net.sf.acegisecurity.providers.jaas.JAASAuthenticationCallbackHandler[]) + */ public JAASAuthenticationCallbackHandler[] getCallbackHandlers() { return callbackHandlers; } + /** + * Set the JAASAuthentcationCallbackHandler array to handle callback objects generated by the + * LoginContext.login method. + * + * @param callbackHandlers Array of JAASAuthenticationCallbackHandlers + */ public void setCallbackHandlers(JAASAuthenticationCallbackHandler[] callbackHandlers) { this.callbackHandlers = callbackHandlers; } + /** + * @return The AuthorityGranter array + * @see #setAuthorityGranters(net.sf.acegisecurity.providers.jaas.AuthorityGranter[]) + */ public AuthorityGranter[] getAuthorityGranters() { return authorityGranters; } + /** + * Set the AuthorityGranters that should be consulted for role names to be granted to the Authentication. + * + * @param authorityGranters AuthorityGranter array + * @see JAASAuthenticationProvider + */ public void setAuthorityGranters(AuthorityGranter[] authorityGranters) { this.authorityGranters = authorityGranters; } + + /** + * Wrapper class for JAASAuthenticationCallbackHandlers + */ private class InternalCallbackHandler implements CallbackHandler { private Authentication authentication;