Reputation: 1
Implementation of SingleSign on using Spring Security.
We ran into a weird situation after we replaced the cookie based authentication with SAML based authentication using spring security.
adding the SAMLContextProviderImpl in securityContext.xml, I see that request redirects indefinitely, i.e context gets appended n number of times.
bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderImpl"/>
Hence I replaced it with
<bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderLB">
<property name="scheme" value="https"/>
<property name="serverName" value="ServerName"/>
<property name="serverPort" value="43"/>
<property name="includeServerPortInRequestURL" value="false"/>
<property name="contextPath" value="/appcontext"/>
</bean>
After adding ContextProvider with reverse proxy, I do not see the multiple redirection. The context is loaded correctly. However in chrome and firefox I see that the application is not loaded properly and from the developer tool I see that the error: Mixed Content: The page at 'https://xportal/tools' was loaded over HTTPS,but requested an insecure form action 'http://xportal/tools'. This request has been blocked; the content must be served over HTTPS.
If we refresh the page, the app loads fine. But it is blocked at first time only.
Any help on this would be appreciated.
Upvotes: 0
Views: 1443
Reputation: 11
I came across your issue during my googling as I was experiencing the same issue. The reason this occurs is because the SAMLContextProviderLB does part of the work but it doesn't cover the situation where your app is inside an iframe.
What happens in this situation is that the original requested url that is protected by spring security saml is stored in the user's session (assuming that you're using the SavedRequestAwareAuthenticationSuccessHandler). Now because your app is running behind a load balancer which is presumably handling the ssl offloading your app actually sees the url as plain http which is what gets stored.
After successful sso at the idp the assertion is posted to the acs url at /saml/SSO/alias/{sp-entity-id}. This is where SAMLContextProviderLB comes into play and it handles it nicely.
Then spring security (via the SavedRequestAwareAuthenticationSuccessHandler) will retrieve the original requested url which it previously stored in the session and will redirect to that location. This is where the problem occurs. It stored the url as plain http because that's how your app saw the request since it's behind the load balancer.
So as far as chrome is concerned, it witnessed some non-https activity and therefore says no way.
So, the solution is to do the same thing that SAMLContextProviderLB does - basically just rewrite http to https in the url. Unfortunately it wasn't possible to simply extend SavedRequestAwareAuthenticationSuccessHandler due to the requestCache variable being private. Instead just copy and paste this class - I've just added a single line to replace http with https.
package com.blah;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.util.StringUtils;
public class SavedRequestAwareAuthenticationSuccessHandlerLB extends SimpleUrlAuthenticationSuccessHandler {
protected final Log logger = LogFactory.getLog(this.getClass());
private RequestCache requestCache = new HttpSessionRequestCache();
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws ServletException, IOException {
SavedRequest savedRequest = requestCache.getRequest(request, response);
if (savedRequest == null) {
super.onAuthenticationSuccess(request, response, authentication);
return;
}
String targetUrlParameter = getTargetUrlParameter();
if (isAlwaysUseDefaultTargetUrl()
|| (targetUrlParameter != null && StringUtils.hasText(request
.getParameter(targetUrlParameter)))) {
requestCache.removeRequest(request, response);
super.onAuthenticationSuccess(request, response, authentication);
return;
}
clearAuthenticationAttributes(request);
// Use the DefaultSavedRequest URL
String targetUrl = savedRequest.getRedirectUrl();
targetUrl = StringUtils.replace(targetUrl, "http://", "https://");
logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);
getRedirectStrategy().sendRedirect(request, response, targetUrl);
}
}
Hope that helps.
Upvotes: 1