This is the sample to integrate SSO to Java web app with spring security, typical authentication process:
[b]In this case, Web app sever is not proxyed, and recieve request directly, so the user's request arrive to Web app server firstly[/b]
[img]http://dl2.iteye.com/upload/attachment/0118/8617/b986decd-5d80-3e48-bc95-2a924ab06067.jpg[/img]
Core Spring security config:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:sec="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<sec:http pattern="/assets/**" security="none" />
<sec:http pattern="/views/logout**" security="none" />
<bean id="userDetailsService" class="com.wilson.security.CustomUserDetailsService" />
<sec:http entry-point-ref="loginHandler" use-expressions="true" auto-config="false"
request-matcher="regex">
<sec:intercept-url pattern="/**" access="isAuthenticated()" />
<sec:logout logout-url="/logout" invalidate-session="true" success-handler-ref="logoutHandler" />
<sec:custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER" />
</sec:http>
<bean id="loginHandler" class="com.wilson.security.SSOLoginHandler">
<property name="loginFormUrl" value="${sso.proxy}" />
<property name="authProcessingURL" value="http://${fqdn}:${port}${context}/authenticate" />
</bean>
<bean id="logoutHandler" class="com.wilson.security.SSOLogoutHandler">
<property name="defaultTargetUrl" value="/views/logout.jsp" />
<property name="alwaysUseDefaultTargetUrl" value="true" />
</bean>
<bean id="authenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<constructor-arg value="/views/logout-failure.jsp"></constructor-arg>
</bean>
<bean id="authenticationFilter" class="com.wilson.security.SSOAuthenticationFilter">
<property name="filterProcessesUrl" value="/authenticate" />
<property name="authenticationManager" ref="authenticationManager" />
<property name="AuthenticationFailureHandler" ref="authenticationFailureHandler" />
<property name="publicKey" value="${sso.publickey}" />
</bean>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider user-service-ref="userDetailsService" />
</sec:authentication-manager>
</beans>
So, first, we need to add a custom LoginUrlAuthenticationEntryPoint to redirect request to SSO when user open home page without login
public class SSOLoginHandler extends LoginUrlAuthenticationEntryPoint
{
private final Logger logger = LoggerFactory.getLogger(SSOLoginHandler.class);
private String authProcessingURL;
@Override
public void commence(final HttpServletRequest request, final HttpServletResponse response,
final AuthenticationException authenticationException) throws IOException, ServletException
{
logger.debug("Preparing redirectiion to SSO PROXY...");
// new DefaultRedirectStrategy().sendRedirect(request, response, this.getLoginFormUrl() + "?ref="
// + authProcessingURL);
String SSO_LOGIN_URL= "https://ssoserver.com/sso.jsp";
new DefaultRedirectStrategy().sendRedirect(request, response, SSO_LOGIN_URL + "?ref="
+ authProcessingURL);
}
public String getAuthProcessingURL()
{
return authProcessingURL;
}
public void setAuthProcessingURL(final String authProcessingURL)
{
this.authProcessingURL = authProcessingURL;
}
Simplete logout which do some logging items..
public class SSOLogoutHandler extends SimpleUrlLogoutSuccessHandler
{
private final Logger logger = LoggerFactory.getLogger(SSOLogoutHandler.class);
@Override
public void onLogoutSuccess(final HttpServletRequest request, final HttpServletResponse response,
final Authentication authentication) throws IOException, ServletException
{
super.onLogoutSuccess(request, response, authentication);
logger.debug("Performing an SSO logout at: {}", this.getDefaultTargetUrl());
}
}
Custom UserDeatailsService to load role and Grant Authority to user
public class CustomUserDetailsService implements UserDetailsService
{
public static final String DEFAULT_AUTH_PASSWORD = "password";
@Override
public UserDetails loadUserByUsername(String soeid) throws UsernameNotFoundException
{
List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>();
grantedAuths.add(new SimpleGrantedAuthority(***Service.queryUserRoleFromDatabase(soeid).toString()));
UserDetails user = new User(soeid, DEFAULT_AUTH_PASSWORD, true, true, true, true, grantedAuths);
return user;
}
}
Custom authetication filter to processe the response form SSO server after logicn
public class SSOAuthenticationFilter extends UsernamePasswordAuthenticationFilter
{
public static final String DEFAULT_AUTH_PASSWORD = "password";
private final Logger logger = LoggerFactory.getLogger(SSOAuthenticationFilter.class);
private Cipher cipher;
public SSOAuthenticationFilter()
{
super.setPostOnly(false); // allow a GET request from SSO PROXY
}
@Override
public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response) throws AuthenticationException
{
String[] sid = decodeSID(request);
String soeid = sid[0];
// token is expired if currentTimeMillis is greater then TIMESTAMP
if (System.currentTimeMillis() > Long.parseLong(sid[1]))
{
logger.error("Authentication rejected for: {}", soeid);
throw new NonceExpiredException("Authentication token is expired");
}
// saving decoded SOEID in a REQUEST to reuse it by obtainUsername()
request.setAttribute("SSO_USER_SOEID", soeid);
return super.attemptAuthentication(request, response);
}
@Override
protected String obtainPassword(final HttpServletRequest request)
{
return DEFAULT_AUTH_PASSWORD;
}
@Override
protected String obtainUsername(final HttpServletRequest request)
{
//SM_USER is coming from SSO after login
return (String) request.getAttribute("SM_USER");
}
private String[] decodeSID(final HttpServletRequest request)
{
.............add SSO server decode strtegy
}
}
[b]
You may say above sample was not my case, what happens we have SSO setup in the proxy server as below?[/b]
[img]http://dl2.iteye.com/upload/attachment/0118/8615/2525dae2-ee24-3ee0-98fe-e58799aa33b7.jpg[/img]
only difference is in the login entry filter, we redirect to the web app authentication filter("/authenticate") as it's pre-logged in
public class SSOLoginHandler extends LoginUrlAuthenticationEntryPoint
{
private final Logger logger = LoggerFactory.getLogger(SSOLoginHandler.class);
private String authProcessingURL;
@Override
public void commence(final HttpServletRequest request, final HttpServletResponse response,
final AuthenticationException authenticationException) throws IOException, ServletException
{
logger.debug("Preparing redirectiion to SSO PROXY...");
new DefaultRedirectStrategy().sendRedirect(request, response, "/authenticate");
}
}