Hi group,
I've developed a simple hack to the waffle negotiate filter in order to have a fallback to a legacy form login in case the waffle sso negotiate fails, which I wanted to share.
My scenario was the following:
- existing legacy spring web application with a custom form login page authenticating against ldap and database fallback
- sso authentication
- requirement to have no browser popups for authentication requests (e.g. basic authentication, waffle ntlm, digest,...)
Following steps where performed to ensure a proper working setup before implementing the hack:
- Create custom form login for usage of spring security
- Ensure correct setup of spring security with all resources accessable only on authenticated except the form login page
- Waffle sso login as described in the various examples with kerberos, ntlm and basic authentication setup
After I came this far, I disabled the ntlm protocoll of the NegotiateSecurityFilterProvider in supplying the protocols property via spring configuration (e.g. '<property name="protocols" value="Negotiate"/>') as well as the BasicSecurityFilterProvider with commenting out the line in the SecurityFilterProviderCollection of the waffle configuration. This helped me avoid the client-side authentication popup windows requesting the user information. This then leads to the following behaviour - after the user calls up a restricted page having no prior authentication, he is authenticated as an anonymous user by spring security and the waffle NegotiateSecurityFilterProvider sends a 401 with a WWW-Authenticate header containing the value 'Negotiate'. The browser now reacts in either of two ways. If the 'Negotiate' protocol is available, it replies to conform with the requested protocol and the user will be authenticated via waffle. In case the protocol is not available, the browser stops since it cannot supply the information for the requested authentication challenge - which leads to a blank 401 page for the user.
The hack I applied to avoid a blank 401 is simply to extend the NegotiateSecurityFilterProvider and add content for redirecting to a fallback page in case the authentication fails. This looks something like this:
public class CustomNegotiateSecurityFilterProvider extends NegotiateSecurityFilterProvider {
private String unauthorizedPage;
/**
* @see waffle.servlet.spi.NegotiateSecurityFilterProvider#NegotiateSecurityFilterProvider(waffle.windows.auth.IWindowsAuthProvider)
*/
public CustomNegotiateSecurityFilterProvider(IWindowsAuthProvider auth) {
super(auth);
}
/**
* Returns the referral page in case the user is unauthorized.
* @return The unauthorized page url.
*/
public String getUnauthorizedPage() {
return unauthorizedPage;
}
/**
* Sets the referral page for the case the user is unauthorized.
* @param unauthorizedPage The unauthorized page url.
*/
public void setUnauthorizedPage(String unauthorizedPage) {
this.unauthorizedPage = unauthorizedPage;
}
@Override
public void sendUnauthorized(HttpServletResponse response) {
super.sendUnauthorized(response);
if (getUnauthorizedPage() != null && !getUnauthorizedPage().isEmpty()) {
try {
response.setContentType("text/html; charset=UTF-8");
response.getOutputStream().print("<html><head><meta http-equiv='refresh' content='0; url=" + getUnauthorizedPage() + "' /></head><body>Redirection in progress...</body></html>");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
When replacing the NegotiateSecurityFilterProvider with the custom filter, the user will now be redirected on authentication failure and authenticated if the protocol can be fulfilled.
If any of you know of a better solution for this functionality I would be happy to hear.
Hope this helps someone...
Andreas