JaasAuthenticationProvider no longer supports the useSystemProperty setting.This is because it no longer uses the java.security.auth.login.config system property for configuring Jaas. Custom Jaas configuration needs can be implemented in a subclass that overrides the configureJaas method.
JaasAuthenticationProvider now handles logout by associating the LoginContext with a new JaasAuthenticationToken
This commit is contained in:
parent
6049e9ac65
commit
0aa4989dad
|
@ -19,40 +19,37 @@ import net.sf.acegisecurity.AcegiSecurityException;
|
|||
import net.sf.acegisecurity.Authentication;
|
||||
import net.sf.acegisecurity.AuthenticationException;
|
||||
import net.sf.acegisecurity.GrantedAuthority;
|
||||
import net.sf.acegisecurity.context.HttpSessionContextIntegrationFilter;
|
||||
import net.sf.acegisecurity.context.SecurityContext;
|
||||
import net.sf.acegisecurity.providers.AuthenticationProvider;
|
||||
import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
import net.sf.acegisecurity.providers.jaas.event.JaasAuthenticationFailedEvent;
|
||||
import net.sf.acegisecurity.providers.jaas.event.JaasAuthenticationSuccessEvent;
|
||||
|
||||
import net.sf.acegisecurity.ui.session.HttpSessionDestroyedEvent;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.security.Security;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
import javax.security.auth.login.Configuration;
|
||||
import javax.security.auth.login.LoginContext;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
import java.security.Security;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -157,15 +154,14 @@ import javax.security.auth.login.LoginException;
|
|||
* </p>
|
||||
*
|
||||
* A configuration note:
|
||||
* The JaasAuthenticationProvider configures jaas using the system property 'java.security.auth.login.config' by default.
|
||||
* If use of the java.security.auth.login.config property is not allowed by the Security property 'policy.allowSystemProperty', OR if the JaasAuthenticationProvider
|
||||
* useSystemProperty option is false, then Jaas will be configured using the 'login.config.url.x' properties.
|
||||
* The JaasAuthenticationProvider uses the security properites "e;login.config.url.X"e; to configure jaas.
|
||||
* If you would like to customize the way Jaas gets configured, create a subclass of this and override the {@link #configureJaas(Resource)} method.
|
||||
*
|
||||
* @author Ray Krueger
|
||||
* @version $Id$
|
||||
*/
|
||||
public class JaasAuthenticationProvider implements AuthenticationProvider,
|
||||
InitializingBean, ApplicationContextAware {
|
||||
InitializingBean, ApplicationContextAware, ApplicationListener {
|
||||
//~ Static fields/initializers =============================================
|
||||
|
||||
protected static final Log log = LogFactory.getLog(JaasAuthenticationProvider.class);
|
||||
|
@ -175,11 +171,10 @@ public class JaasAuthenticationProvider implements AuthenticationProvider,
|
|||
private ApplicationContext context;
|
||||
private LoginExceptionResolver loginExceptionResolver = new DefaultLoginExceptionResolver();
|
||||
private Resource loginConfig;
|
||||
private final String SYSPROP = "java.security.auth.login.config";
|
||||
|
||||
private String loginContextName = "ACEGI";
|
||||
private AuthorityGranter[] authorityGranters;
|
||||
private JaasAuthenticationCallbackHandler[] callbackHandlers;
|
||||
private boolean useSystemProperty = true;
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
|
@ -197,7 +192,6 @@ public class JaasAuthenticationProvider implements AuthenticationProvider,
|
|||
* granted to the Authentication.
|
||||
*
|
||||
* @param authorityGranters AuthorityGranter array
|
||||
*
|
||||
* @see JaasAuthenticationProvider
|
||||
*/
|
||||
public void setAuthorityGranters(AuthorityGranter[] authorityGranters) {
|
||||
|
@ -210,8 +204,7 @@ public class JaasAuthenticationProvider implements AuthenticationProvider,
|
|||
* were ever set.
|
||||
*
|
||||
* @return The AuthorityGranter array, or null
|
||||
*
|
||||
* @see #setAuthorityGranters(net.sf.acegisecurity.providers.jaas.AuthorityGranter[])
|
||||
* @see #setAuthorityGranters(AuthorityGranter[])
|
||||
*/
|
||||
public AuthorityGranter[] getAuthorityGranters() {
|
||||
return authorityGranters;
|
||||
|
@ -233,8 +226,7 @@ public class JaasAuthenticationProvider implements AuthenticationProvider,
|
|||
* none are set.
|
||||
*
|
||||
* @return the JAASAuthenticationCallbackHandlers.
|
||||
*
|
||||
* @see #setCallbackHandlers(net.sf.acegisecurity.providers.jaas.JaasAuthenticationCallbackHandler[])
|
||||
* @see #setCallbackHandlers(JaasAuthenticationCallbackHandler[])
|
||||
*/
|
||||
public JaasAuthenticationCallbackHandler[] getCallbackHandlers() {
|
||||
return callbackHandlers;
|
||||
|
@ -246,7 +238,6 @@ public class JaasAuthenticationProvider implements AuthenticationProvider,
|
|||
* @param loginConfig <a
|
||||
* href="http://www.springframework.org/docs/api/org/springframework/core/io/Resource.html">Spring
|
||||
* Resource</a>
|
||||
*
|
||||
* @see <a
|
||||
* href="http://java.sun.com/j2se/1.4.2/docs/guide/security/jaas/JAASRefGuide.html">JAAS
|
||||
* Reference</a>
|
||||
|
@ -287,47 +278,59 @@ public class JaasAuthenticationProvider implements AuthenticationProvider,
|
|||
Assert.hasLength(loginContextName,
|
||||
"loginContextName must be set on " + getClass());
|
||||
|
||||
String loginConfigStr = null;
|
||||
|
||||
try {
|
||||
loginConfigStr = loginConfig.getFile().toString();
|
||||
} catch (IOException e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Could not resolve loginConfig [" + loginConfig
|
||||
+ "] as a File, using URL");
|
||||
}
|
||||
|
||||
loginConfigStr = loginConfig.getURL().toString();
|
||||
}
|
||||
|
||||
boolean allowed = "true".equalsIgnoreCase(Security.getProperty(
|
||||
"policy.allowSystemProperty"));
|
||||
|
||||
if (useSystemProperty && allowed) {
|
||||
log.debug("Setting system property [" + SYSPROP + "] to: "
|
||||
+ loginConfigStr);
|
||||
System.setProperty(SYSPROP, loginConfigStr);
|
||||
} else {
|
||||
if (useSystemProperty && !allowed) {
|
||||
log.warn("useSystemProperty is true, but the security property 'policy.allowSystemProperty' is false. " +
|
||||
"Jaas will be configured using the login.config.url property.");
|
||||
}
|
||||
setPropertyUsingLoop(loginConfig.getURL().toString());
|
||||
}
|
||||
configureJaas(loginConfig);
|
||||
|
||||
Assert.notNull(Configuration.getConfiguration(),
|
||||
"As per http://java.sun.com/j2se/1.5.0/docs/api/javax/security/auth/login/Configuration.html \"If a Configuration object was set via the Configuration.setConfiguration method, then that object is returned. Otherwise, a default Configuration object is returned\". Your JRE returned null to Configuration.getConfiguration().");
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook method for configuring Jaas
|
||||
*
|
||||
* @param loginConfigStr URL to Jaas login configuration
|
||||
*/
|
||||
protected void configureJaas(Resource loginConfig) throws IOException {
|
||||
configureJaasUsingLoop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops through the login.config.url.1,login.config.url.2 properties
|
||||
* looking for the login configuration. If it is not set, it will be set
|
||||
* to the last available login.config.url.X property.
|
||||
*/
|
||||
private void configureJaasUsingLoop() throws IOException {
|
||||
String loginConfigUrl = loginConfig.getURL().toString();
|
||||
boolean alreadySet = false;
|
||||
|
||||
int n = 1;
|
||||
String prefix = "login.config.url.";
|
||||
String existing = null;
|
||||
|
||||
while ((existing = Security.getProperty(prefix + n)) != null) {
|
||||
alreadySet = existing.equals(loginConfigUrl);
|
||||
|
||||
if (alreadySet) {
|
||||
break;
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
if (!alreadySet) {
|
||||
String key = prefix + n;
|
||||
log.debug("Setting security property [" + key + "] to: "
|
||||
+ loginConfigUrl);
|
||||
Security.setProperty(key, loginConfigUrl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -341,11 +344,11 @@ public class JaasAuthenticationProvider implements AuthenticationProvider,
|
|||
|
||||
try {
|
||||
//Create the LoginContext object, and pass our InternallCallbackHandler
|
||||
LoginContext lc = new LoginContext(loginContextName,
|
||||
LoginContext loginContext = new LoginContext(loginContextName,
|
||||
new InternalCallbackHandler(auth));
|
||||
|
||||
//Attempt to login the user, the LoginContext will call our InternalCallbackHandler at this point.
|
||||
lc.login();
|
||||
loginContext.login();
|
||||
|
||||
//create a set to hold the authorities, and add any that have already been applied.
|
||||
Set authorities = new HashSet();
|
||||
|
@ -355,7 +358,7 @@ public class JaasAuthenticationProvider implements AuthenticationProvider,
|
|||
}
|
||||
|
||||
//get the subject principals and pass them to each of the AuthorityGranters
|
||||
Set principals = lc.getSubject().getPrincipals();
|
||||
Set principals = loginContext.getSubject().getPrincipals();
|
||||
|
||||
for (Iterator iterator = principals.iterator();
|
||||
iterator.hasNext();) {
|
||||
|
@ -378,10 +381,10 @@ public class JaasAuthenticationProvider implements AuthenticationProvider,
|
|||
}
|
||||
|
||||
//Convert the authorities set back to an array and apply it to the token.
|
||||
UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(request
|
||||
JaasAuthenticationToken result = new JaasAuthenticationToken(request
|
||||
.getPrincipal(), request.getCredentials(),
|
||||
(GrantedAuthority[]) authorities.toArray(
|
||||
new GrantedAuthority[authorities.size()]));
|
||||
new GrantedAuthority[authorities.size()]), loginContext);
|
||||
|
||||
//Publish the success event
|
||||
publishSuccessEvent(result);
|
||||
|
@ -404,6 +407,38 @@ public class JaasAuthenticationProvider implements AuthenticationProvider,
|
|||
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(aClass);
|
||||
}
|
||||
|
||||
public void onApplicationEvent(ApplicationEvent applicationEvent) {
|
||||
if (applicationEvent instanceof HttpSessionDestroyedEvent) {
|
||||
HttpSessionDestroyedEvent event = (HttpSessionDestroyedEvent) applicationEvent;
|
||||
handleLogout(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the logout by getting the SecurityContext for the session that was destroyed.
|
||||
* <b>MUST NOT use SecurityContextHolder we are logging out a session that is not related to the current user.</b>
|
||||
* @param event
|
||||
*/
|
||||
protected void handleLogout(HttpSessionDestroyedEvent event) {
|
||||
SecurityContext context = (SecurityContext) event.getSession().getAttribute(HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY);
|
||||
Authentication auth = context.getAuthentication();
|
||||
if (auth instanceof JaasAuthenticationToken) {
|
||||
JaasAuthenticationToken token = (JaasAuthenticationToken) auth;
|
||||
try {
|
||||
LoginContext loginContext = token.getLoginContext();
|
||||
if (loginContext != null) {
|
||||
log.debug("Logging principal: [" + token.getPrincipal() + "] out of LoginContext");
|
||||
loginContext.logout();
|
||||
} else {
|
||||
log.debug("Cannot logout principal: [" + token.getPrincipal() + "] from LoginContext. " +
|
||||
"The LoginContext is unavailable");
|
||||
}
|
||||
} catch (LoginException e) {
|
||||
log.warn("Error error logging out of LoginContext", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes the {@link JaasAuthenticationFailedEvent}. Can be overridden
|
||||
* by subclasses for different functionality
|
||||
|
@ -431,51 +466,6 @@ public class JaasAuthenticationProvider implements AuthenticationProvider,
|
|||
token));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops through the login.config.url.1,login.config.url.2 properties
|
||||
* looking for the login configuration. If it is not set, it will be set
|
||||
* to the last available login.config.url.X property.
|
||||
*
|
||||
* @param loginConfigStr
|
||||
*/
|
||||
private void setPropertyUsingLoop(String loginConfigStr) {
|
||||
boolean alreadySet = false;
|
||||
|
||||
int n = 1;
|
||||
String prefix = "login.config.url.";
|
||||
String existing = null;
|
||||
|
||||
while ((existing = Security.getProperty(prefix + n)) != null) {
|
||||
alreadySet = existing.equals(loginConfigStr);
|
||||
|
||||
if (alreadySet) {
|
||||
break;
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
if (!alreadySet) {
|
||||
String key = prefix + n;
|
||||
log.debug("Setting security property [" + key + "] to: "
|
||||
+ loginConfigStr);
|
||||
Security.setProperty(key, loginConfigStr);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isUseSystemProperty() {
|
||||
return useSystemProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, the JaasAuthenticationProvider will configure Jaas using the system property 'java.security.auth.login.config'.
|
||||
* If false, the JaasAuthenticationProvider will configure Jaas using the 'login.config.url.x' property.
|
||||
* <br/><b>Default:True</b>
|
||||
* @param useSystemProperty
|
||||
*/
|
||||
public void setUseSystemProperty(boolean useSystemProperty) {
|
||||
this.useSystemProperty = useSystemProperty;
|
||||
}
|
||||
//~ Inner Classes ==========================================================
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package net.sf.acegisecurity.providers.jaas;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
import net.sf.acegisecurity.GrantedAuthority;
|
||||
|
||||
import javax.security.auth.login.LoginContext;
|
||||
|
||||
/**
|
||||
* UsernamePasswordAuthenticationToken extension to carry the Jaas LoginContext that the user was logged into
|
||||
* @author Ray Krueger
|
||||
*/
|
||||
public class JaasAuthenticationToken extends UsernamePasswordAuthenticationToken {
|
||||
|
||||
private transient LoginContext loginContext = null;
|
||||
|
||||
public JaasAuthenticationToken(Object principal, Object credentials, LoginContext loginContext) {
|
||||
super(principal, credentials);
|
||||
this.loginContext = loginContext;
|
||||
}
|
||||
|
||||
public JaasAuthenticationToken(Object principal, Object credentials, GrantedAuthority[] authorities, LoginContext loginContext) {
|
||||
super(principal, credentials, authorities);
|
||||
this.loginContext = loginContext;
|
||||
}
|
||||
|
||||
public LoginContext getLoginContext() {
|
||||
return loginContext;
|
||||
}
|
||||
}
|
|
@ -17,12 +17,17 @@ package net.sf.acegisecurity.providers.jaas;
|
|||
|
||||
import junit.framework.TestCase;
|
||||
import net.sf.acegisecurity.*;
|
||||
import net.sf.acegisecurity.context.HttpSessionContextIntegrationFilter;
|
||||
import net.sf.acegisecurity.context.SecurityContextImpl;
|
||||
import net.sf.acegisecurity.ui.session.HttpSessionDestroyedEvent;
|
||||
import net.sf.acegisecurity.providers.TestingAuthenticationToken;
|
||||
import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
|
||||
import javax.security.auth.login.LoginException;
|
||||
import javax.security.auth.login.LoginContext;
|
||||
import java.net.URL;
|
||||
import java.security.Security;
|
||||
import java.util.Arrays;
|
||||
|
@ -74,7 +79,6 @@ public class JaasAuthenticationProviderTests extends TestCase {
|
|||
String resName = "/" + getClass().getName().replace('.', '/') + ".conf";
|
||||
URL url = getClass().getResource(resName);
|
||||
|
||||
Security.setProperty("policy.allowSystemProperty", "false");
|
||||
Security.setProperty("login.config.url.1", url.toString());
|
||||
|
||||
setUp();
|
||||
|
@ -211,10 +215,40 @@ public class JaasAuthenticationProviderTests extends TestCase {
|
|||
new GrantedAuthority[]{})));
|
||||
}
|
||||
|
||||
public void testLogout() throws Exception {
|
||||
|
||||
MockLoginContext loginContext = new MockLoginContext(jaasProvider.getLoginContextName());
|
||||
|
||||
JaasAuthenticationToken token = new JaasAuthenticationToken(null, null, loginContext);
|
||||
|
||||
SecurityContextImpl context = new SecurityContextImpl();
|
||||
context.setAuthentication(token);
|
||||
|
||||
MockHttpSession mockSession = new MockHttpSession();
|
||||
mockSession.setAttribute(HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY, context);
|
||||
|
||||
jaasProvider.onApplicationEvent(new HttpSessionDestroyedEvent(mockSession));
|
||||
|
||||
assertTrue(loginContext.loggedOut);
|
||||
}
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
String resName = "/" + getClass().getName().replace('.', '/') + ".xml";
|
||||
context = new ClassPathXmlApplicationContext(resName);
|
||||
eventCheck = (JaasEventCheck) context.getBean("eventCheck");
|
||||
jaasProvider = (JaasAuthenticationProvider) context.getBean("jaasAuthenticationProvider");
|
||||
}
|
||||
|
||||
private static class MockLoginContext extends LoginContext {
|
||||
|
||||
boolean loggedOut = false;
|
||||
|
||||
public MockLoginContext(String loginModule) throws LoginException {
|
||||
super(loginModule);
|
||||
}
|
||||
|
||||
public void logout() throws LoginException {
|
||||
this.loggedOut = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue