mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-09-08 20:51:41 +00:00
SEC-923: Realm support for discovering relying parties.
A new "realmMapping" property can be configured on the OpenIDAuthenticationProcessingFilter to map the "return_to" url to a realm. If there is no mapping present the "return_to" url will be parsed and the protocol, hostname and port will be used with a trailing "/"
This commit is contained in:
parent
67e5afbb79
commit
3393ea7aaa
@ -15,6 +15,8 @@
|
|||||||
|
|
||||||
package org.springframework.security.ui.openid;
|
package org.springframework.security.ui.openid;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.security.Authentication;
|
import org.springframework.security.Authentication;
|
||||||
import org.springframework.security.AuthenticationException;
|
import org.springframework.security.AuthenticationException;
|
||||||
import org.springframework.security.AuthenticationServiceException;
|
import org.springframework.security.AuthenticationServiceException;
|
||||||
@ -24,14 +26,16 @@ import org.springframework.security.ui.AbstractProcessingFilter;
|
|||||||
import org.springframework.security.ui.FilterChainOrder;
|
import org.springframework.security.ui.FilterChainOrder;
|
||||||
import org.springframework.security.ui.openid.consumers.OpenID4JavaConsumer;
|
import org.springframework.security.ui.openid.consumers.OpenID4JavaConsumer;
|
||||||
import org.springframework.security.ui.webapp.AuthenticationProcessingFilter;
|
import org.springframework.security.ui.webapp.AuthenticationProcessingFilter;
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import javax.servlet.http.HttpSession;
|
import javax.servlet.http.HttpSession;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,6 +54,7 @@ public class OpenIDAuthenticationProcessingFilter extends AbstractProcessingFilt
|
|||||||
|
|
||||||
private OpenIDConsumer consumer;
|
private OpenIDConsumer consumer;
|
||||||
private String claimedIdentityFieldName = DEFAULT_CLAIMED_IDENTITY_FIELD;
|
private String claimedIdentityFieldName = DEFAULT_CLAIMED_IDENTITY_FIELD;
|
||||||
|
private Map realmMapping = new HashMap();
|
||||||
|
|
||||||
//~ Methods ========================================================================================================
|
//~ Methods ========================================================================================================
|
||||||
|
|
||||||
@ -106,7 +111,8 @@ public class OpenIDAuthenticationProcessingFilter extends AbstractProcessingFilt
|
|||||||
if (StringUtils.hasText(claimedIdentity)) {
|
if (StringUtils.hasText(claimedIdentity)) {
|
||||||
try {
|
try {
|
||||||
String returnToUrl = buildReturnToUrl(request);
|
String returnToUrl = buildReturnToUrl(request);
|
||||||
return consumer.beginConsumption(request, claimedIdentity, returnToUrl);
|
String realm = lookupRealm(returnToUrl);
|
||||||
|
return consumer.beginConsumption(request, claimedIdentity, returnToUrl, realm);
|
||||||
} catch (OpenIDConsumerException e) {
|
} catch (OpenIDConsumerException e) {
|
||||||
log.error("Unable to consume claimedIdentity [" + claimedIdentity + "]", e);
|
log.error("Unable to consume claimedIdentity [" + claimedIdentity + "]", e);
|
||||||
}
|
}
|
||||||
@ -116,6 +122,30 @@ public class OpenIDAuthenticationProcessingFilter extends AbstractProcessingFilt
|
|||||||
return super.determineFailureUrl(request, failed);
|
return super.determineFailureUrl(request, failed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String lookupRealm(String returnToUrl) {
|
||||||
|
|
||||||
|
String mapping = (String) realmMapping.get(returnToUrl);
|
||||||
|
|
||||||
|
if (mapping == null) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
URL url = new URL(returnToUrl);
|
||||||
|
int port = (url.getPort() == -1) ? 80 : url.getPort();
|
||||||
|
StringBuffer realmBuffer = new StringBuffer(returnToUrl.length())
|
||||||
|
.append(url.getProtocol())
|
||||||
|
.append("://")
|
||||||
|
.append(url.getHost())
|
||||||
|
.append(":").append(port)
|
||||||
|
.append("/");
|
||||||
|
mapping = realmBuffer.toString();
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
log.warn("returnToUrl was not a valid URL: [" + returnToUrl + "]", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
|
||||||
protected String buildReturnToUrl(HttpServletRequest request) {
|
protected String buildReturnToUrl(HttpServletRequest request) {
|
||||||
return request.getRequestURL().toString();
|
return request.getRequestURL().toString();
|
||||||
}
|
}
|
||||||
@ -201,4 +231,32 @@ public class OpenIDAuthenticationProcessingFilter extends AbstractProcessingFilt
|
|||||||
public int getOrder() {
|
public int getOrder() {
|
||||||
return FilterChainOrder.OPENID_PROCESSING_FILTER;
|
return FilterChainOrder.OPENID_PROCESSING_FILTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the return_to url to a realm.<br/>
|
||||||
|
* For example http://www.example.com/j_spring_openid_security_check -> http://www.example.com/realm<br/>
|
||||||
|
* If no mapping is provided then the returnToUrl will be parsed to extract the protocol, hostname and port followed
|
||||||
|
* by a trailing slash.<br/>
|
||||||
|
* This means that http://www.example.com/j_spring_openid_security_check will automatically
|
||||||
|
* become http://www.example.com:80/
|
||||||
|
*
|
||||||
|
* @return Map containing returnToUrl -> realm mappings
|
||||||
|
*/
|
||||||
|
public Map getRealmMapping() {
|
||||||
|
return realmMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the return_to url to a realm.<br/>
|
||||||
|
* For example http://www.example.com/j_spring_openid_security_check -> http://www.example.com/realm<br/>
|
||||||
|
* If no mapping is provided then the returnToUrl will be parsed to extract the protocol, hostname and port followed
|
||||||
|
* by a trailing slash.<br/>
|
||||||
|
* This means that http://www.example.com/j_spring_openid_security_check will automatically
|
||||||
|
* become http://www.example.com:80/
|
||||||
|
*
|
||||||
|
* @param realmMapping containing returnToUrl -> realm mappings
|
||||||
|
*/
|
||||||
|
public void setRealmMapping(Map realmMapping) {
|
||||||
|
this.realmMapping = realmMapping;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,9 +27,26 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
*/
|
*/
|
||||||
public interface OpenIDConsumer {
|
public interface OpenIDConsumer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link #beginConsumption(javax.servlet.http.HttpServletRequest, String, String, String)}
|
||||||
|
*/
|
||||||
public String beginConsumption(HttpServletRequest req, String identityUrl, String returnToUrl)
|
public String beginConsumption(HttpServletRequest req, String identityUrl, String returnToUrl)
|
||||||
throws OpenIDConsumerException;
|
throws OpenIDConsumerException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given the request, the claimedIdentity, the return to url, and a realm, lookup the openId authentication
|
||||||
|
* page the user should be redirected to.
|
||||||
|
*
|
||||||
|
* @param req HttpServletRequest
|
||||||
|
* @param claimedIdentity String URI the user presented during authentication
|
||||||
|
* @param returnToUrl String URI of the URL we want the user sent back to by the OP
|
||||||
|
* @param realm URI pattern matching the realm we want the user to see
|
||||||
|
* @return String URI to redirect user to for authentication
|
||||||
|
* @throws OpenIDConsumerException if anything bad happens
|
||||||
|
*/
|
||||||
|
public String beginConsumption(HttpServletRequest req, String claimedIdentity, String returnToUrl, String realm)
|
||||||
|
throws OpenIDConsumerException;
|
||||||
|
|
||||||
public OpenIDAuthenticationToken endConsumption(HttpServletRequest req)
|
public OpenIDAuthenticationToken endConsumption(HttpServletRequest req)
|
||||||
throws OpenIDConsumerException;
|
throws OpenIDConsumerException;
|
||||||
|
|
||||||
|
@ -61,7 +61,11 @@ public class OpenID4JavaConsumer implements OpenIDConsumer {
|
|||||||
|
|
||||||
//~ Methods ========================================================================================================
|
//~ Methods ========================================================================================================
|
||||||
|
|
||||||
public String beginConsumption(HttpServletRequest req, String identityUrl, String returnToUrl)
|
public String beginConsumption(HttpServletRequest req, String identityUrl, String returnToUrl) throws OpenIDConsumerException {
|
||||||
|
return beginConsumption(req, identityUrl, returnToUrl, returnToUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String beginConsumption(HttpServletRequest req, String identityUrl, String returnToUrl, String realm)
|
||||||
throws OpenIDConsumerException {
|
throws OpenIDConsumerException {
|
||||||
List discoveries;
|
List discoveries;
|
||||||
|
|
||||||
@ -78,7 +82,7 @@ public class OpenID4JavaConsumer implements OpenIDConsumer {
|
|||||||
AuthRequest authReq;
|
AuthRequest authReq;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
authReq = consumerManager.authenticate(information, returnToUrl);
|
authReq = consumerManager.authenticate(information, returnToUrl, realm);
|
||||||
} catch (MessageException e) {
|
} catch (MessageException e) {
|
||||||
throw new OpenIDConsumerException("Error processing ConumerManager authentication", e);
|
throw new OpenIDConsumerException("Error processing ConumerManager authentication", e);
|
||||||
} catch (ConsumerException e) {
|
} catch (ConsumerException e) {
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
package org.springframework.security.ui.openid;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
|
import org.springframework.security.MockAuthenticationManager;
|
||||||
|
import org.springframework.security.ui.openid.consumers.MockOpenIDConsumer;
|
||||||
|
import org.springframework.security.util.MockFilterChain;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
public class OpenIDAuthenticationProcessingFilterTests extends TestCase {
|
||||||
|
|
||||||
|
OpenIDAuthenticationProcessingFilter filter;
|
||||||
|
private static final String REDIRECT_URL = "http://www.example.com/redirect";
|
||||||
|
private static final String CLAIMED_IDENTITY_URL = "http://www.example.com/identity";
|
||||||
|
private static final String REQUEST_PATH = "/j_spring_openid_security_check";
|
||||||
|
private static final String FILTER_PROCESS_URL = "http://localhost:80" + REQUEST_PATH;
|
||||||
|
private static final String DEFAULT_TARGET_URL = FILTER_PROCESS_URL;
|
||||||
|
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
filter = new OpenIDAuthenticationProcessingFilter();
|
||||||
|
filter.setConsumer(new MockOpenIDConsumer(REDIRECT_URL));
|
||||||
|
filter.setDefaultTargetUrl(DEFAULT_TARGET_URL);
|
||||||
|
filter.setAuthenticationManager(new MockAuthenticationManager());
|
||||||
|
filter.afterPropertiesSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNoIdentityCausesException() throws Exception {
|
||||||
|
try {
|
||||||
|
MockHttpServletRequest req = new MockHttpServletRequest();
|
||||||
|
filter.attemptAuthentication(req);
|
||||||
|
fail("OpenIDAuthenticationRequiredException expected, no openid.identity parameter");
|
||||||
|
} catch (OpenIDAuthenticationRequiredException e) {
|
||||||
|
//cool
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testFilterOperation() throws Exception {
|
||||||
|
MockHttpServletRequest req = new MockHttpServletRequest("GET", REQUEST_PATH);
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
||||||
|
req.setParameter("j_username", CLAIMED_IDENTITY_URL);
|
||||||
|
req.setRemoteHost("www.example.com");
|
||||||
|
|
||||||
|
filter.setConsumer(new MockOpenIDConsumer() {
|
||||||
|
public String beginConsumption(HttpServletRequest req, String claimedIdentity, String returnToUrl, String realm) throws OpenIDConsumerException {
|
||||||
|
assertEquals(CLAIMED_IDENTITY_URL, claimedIdentity);
|
||||||
|
assertEquals(DEFAULT_TARGET_URL, returnToUrl);
|
||||||
|
assertEquals("http://localhost:80/", realm);
|
||||||
|
return REDIRECT_URL;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
filter.doFilter(req, response, new MockFilterChain(false));
|
||||||
|
assertEquals(REDIRECT_URL, response.getRedirectedUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -15,7 +15,6 @@
|
|||||||
package org.springframework.security.ui.openid.consumers;
|
package org.springframework.security.ui.openid.consumers;
|
||||||
|
|
||||||
import org.springframework.security.providers.openid.OpenIDAuthenticationToken;
|
import org.springframework.security.providers.openid.OpenIDAuthenticationToken;
|
||||||
|
|
||||||
import org.springframework.security.ui.openid.OpenIDConsumer;
|
import org.springframework.security.ui.openid.OpenIDConsumer;
|
||||||
import org.springframework.security.ui.openid.OpenIDConsumerException;
|
import org.springframework.security.ui.openid.OpenIDConsumerException;
|
||||||
|
|
||||||
@ -33,14 +32,34 @@ public class MockOpenIDConsumer implements OpenIDConsumer {
|
|||||||
private OpenIDAuthenticationToken token;
|
private OpenIDAuthenticationToken token;
|
||||||
private String redirectUrl;
|
private String redirectUrl;
|
||||||
|
|
||||||
|
public MockOpenIDConsumer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public MockOpenIDConsumer(String redirectUrl, OpenIDAuthenticationToken token) {
|
||||||
|
this.redirectUrl = redirectUrl;
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MockOpenIDConsumer(String redirectUrl) {
|
||||||
|
this.redirectUrl = redirectUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MockOpenIDConsumer(OpenIDAuthenticationToken token) {
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
//~ Methods ========================================================================================================
|
//~ Methods ========================================================================================================
|
||||||
|
|
||||||
|
public String beginConsumption(HttpServletRequest req, String claimedIdentity, String returnToUrl, String realm) throws OpenIDConsumerException {
|
||||||
|
return redirectUrl;
|
||||||
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see org.springframework.security.ui.openid.OpenIDConsumer#beginConsumption(javax.servlet.http.HttpServletRequest, java.lang.String)
|
* @see org.springframework.security.ui.openid.OpenIDConsumer#beginConsumption(javax.servlet.http.HttpServletRequest, java.lang.String)
|
||||||
*/
|
*/
|
||||||
public String beginConsumption(HttpServletRequest req, String identityUrl, String returnToUrl)
|
public String beginConsumption(HttpServletRequest req, String identityUrl, String returnToUrl)
|
||||||
throws OpenIDConsumerException {
|
throws OpenIDConsumerException {
|
||||||
return redirectUrl;
|
throw new UnsupportedOperationException("This method is deprecated, stop using it");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user