Spring Security Preauth "filters=none" not working

4.5k Views Asked by At

strange one,

I am using spring security with siteminder and it works fine. However I want to have one url which isn't protected - our loadBalancer needs a "healthCheck" url within the app itself. This url isn't intercepted by siteminder, but spring security seems to apply the preauth to it anyhow..

if I run it locally using a simple forms-based security config the following works (excluding the filters):

<http auto-config="true" use-expressions="true">
    <intercept-url pattern="/html/healthCheck.html" filters="none" />   
    <intercept-url pattern="/css/**" filters="none" />
    <intercept-url pattern="/images/**" filters="none" />
    <intercept-url pattern="/js/**" filters="none" />
    <intercept-url pattern="/login" filters="none" />
    <intercept-url pattern="/favicon.ico" filters="none" />
    <intercept-url pattern="/*" access="hasAnyRole('ROLE_USER')" />
    <form-login login-page="/login" default-target-url="/" authentication-failure-url="/loginfailed" />
    <logout logout-success-url="/logout" />
</http>

In this case, I can browse to localhost/myApp/resources/html/healthCheck.html without hitting an authorization issue, but any other url will display the login form. All looking good so far!

However when I deploy to the server I am using the following config:

<http auto-config="true" use-expressions="true">
    <intercept-url pattern="/html/healthCheck.html" filters="none" />   
    <intercept-url pattern="/css/**" filters="none" />
    <intercept-url pattern="/images/**" filters="none" />
    <intercept-url pattern="/js/**" filters="none" />
    <intercept-url pattern="/login" filters="none" />
    <intercept-url pattern="/favicon.ico" filters="none" />
    <intercept-url pattern="/*" access="hasAnyRole('ROLE_USER')" />
    <custom-filter position="PRE_AUTH_FILTER" ref="siteminderFilter" />
</http>

When I browse to: server/myapp/resources/html/healthCheck.html I get the following error:

java.lang.IllegalArgumentException: Cannot pass null or empty values to constructor
    org.springframework.security.core.userdetails.User.<init>(User.java:94)
    com.myApp.security.SecuritySBSUserDetailsService.loadUserByUsername(SecuritySBSUserDetailsService.java:119)
    org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper.loadUserDetails(UserDetailsByNameServiceWrapper.java:53)

I think this is caused by the UserDetailsService getting instantiated without any SM_USER. Yet the filters=none is in place.. and works when using forms authentication..Any idea what might be causing this, or better - of a workaround?

By the way, my userdetails service is configured as follows:

<beans:bean id="siteminderFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
    <beans:property name="principalRequestHeader" value="SM_USER" />
    <beans:property name="exceptionIfHeaderMissing" value="false" />
    <beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>

i.e. I've set exceptionIfHeaderMissing to false, if that helps..

2

There are 2 best solutions below

0
On

The most obvious thing I can see is that /resources/html/healthCheck.html won't be matched by /html/healthCheck.html. If you are rewriting the URLs somewhere you should probably explain that.

If you enable debug logging, it should explain in detail what is matched against what.

I'd also leave out the auto-config. It causes more confusion than it is worth. And you should use /** rather than /* for a universal ant pattern match.

It's probably also worth mentioning here that Spring Security 3.1 has a better approach for defining empty filter chains, and also allows you to define more than one filter chain using the <http> syntax.

1
On

Okay, it seems to be a bug in spring security as far as I can see. I got around it by adding a dummy return to the start of the loadUserByName method in the UserDetailsService..

@Override
public UserDetails loadUserByUsername(String userName)
        throws UsernameNotFoundException, DataAccessException {
    logger.trace(">> loadUserByUsername()");
    logger.info("-- loadUserByUsername(): username : {}", userName);

    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();             
    if(userName==null || userName.trim().equals("")) {
        return(new User("ANONYMOUS", "", true, true, true, true, authorities));
        }
// rest of auth checks

It would seem like with the config I have, the UserDetails check shouldn't be getting triggered at all (as it is with the forms..). If anyone has a configuration based workaround I'll give you a plus :-)