How do I create Request-Header Authentication (Siteminder) java config class

1.6k Views Asked by At

My spring boot (2.2.4) application is using siteminder as authentication provider. Upon successful authentication siteminder sends user details (Roles) in Http header. Inside the application I need to use spring security for role based authorization. Here is reference documentation I am following - https://docs.spring.io/spring-security/site/docs/5.0.x/reference/html/preauth.html

My spring-security.xml will look this.

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/security
        https://www.springframework.org/schema/security/spring-security.xsd">

    <security:http>
        <!-- Additional http configuration omitted -->
        <security:csrf disabled="true"/>
        <security:intercept-url pattern="/**" access="hasRole('ROLE_ABC')"/>
        <security:custom-filter position="PRE_AUTH_FILTER" ref="siteminderFilter" />
    </security:http>

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

    <bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
        <property name="preAuthenticatedUserDetailsService">
            <bean id="userDetailsServiceWrapper"
                  class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
                <property name="userDetailsService" ref="userDetailsService"/>
            </bean>
        </property>
    </bean>

    <bean id="userDetailsService" class="com.mycode.service.SCPMUserDetailsService"/>

    <security:authentication-manager alias="authenticationManager">
        <security:authentication-provider ref="preauthAuthProvider" />
    </security:authentication-manager>
</beans>

How do I create a Java config file which will incorporate above security for Spring boot application.

I started something like this and need to inject all the pieces in above xml file.

@EnableWebSecurity
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
public class SecurityConfigurer extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyUserDetailsService myUserDetailsService;


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailsService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()// Disable for GraphIQL to work locally
                .antMatcher("/**")
                .authorizeRequests()
                .antMatchers("/healthcheck/**").permitAll()
                .antMatchers("/scpm/**").permitAll()
                .antMatchers("/approvers/**").hasRole("APPROVER")
                .antMatchers("/departments/**").hasRole("ADMIN")
                .anyRequest().authenticated()
                .and().httpBasic();
    }
}
1

There are 1 best solutions below

0
On

Spring Security reference document does not provide Java Code for SiteMinder integration. It just provides xml configuration for non-spring boot applications. Here is the Java code which can be used in any Spring Boot Application.

@EnableWebSecurity
@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
public class SecurityConfigurer extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyUserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
        auth.authenticationProvider(preAuthenticatedAuthenticationProvider());
    }

    @Bean
    public RequestHeaderAuthenticationFilter siteminderFilter() throws Exception {
        RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();
        filter.setPrincipalRequestHeader("SM_USER");
        filter.setAuthenticationManager(this.authenticationManager());
        return filter;
    }

    @Bean
    public PreAuthenticatedAuthenticationProvider preAuthenticatedAuthenticationProvider() {
        PreAuthenticatedAuthenticationProvider preauthAuthProvider = new PreAuthenticatedAuthenticationProvider();
        preauthAuthProvider.setPreAuthenticatedUserDetailsService(userDetailsByNameServiceWrapper());
        return preauthAuthProvider;
    }

    @Bean
    public UserDetailsByNameServiceWrapper userDetailsByNameServiceWrapper() {
        UserDetailsByNameServiceWrapper userDetailsServiceWrapper = new UserDetailsByNameServiceWrapper();
        userDetailsServiceWrapper.setUserDetailsService(userDetailsService);
        return userDetailsServiceWrapper;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .addFilter(siteminderFilter())
                .csrf().disable()// Disable for GraphIQL to work locally
                .antMatcher("/**")
                .authorizeRequests()
                .antMatchers("/healthcheck/**").permitAll()
                .antMatchers("/approvers/**").hasRole("APPROVER")
                .antMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
                .and().httpBasic();
    }
}

Here is custom UserDetailsService.

@Service
public class MyUserDetailsService implements UserDetailsService {

    private Logger logger = LoggerFactory.getLogger(SCPMUserDetailsService.class);

    @Autowired
    private HttpServletRequest request;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
        HttpServletRequest request  =
                ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                        .getRequest();
        String csgroups = "";
        Collection<GrantedAuthority> auth = new ArrayList<>();

        try {
            username = request.getHeader("SM_USER");
        } catch (Exception ex){
            logger.trace("SM_USER not found in Request Http Headers");
            ex.printStackTrace();
        }

        try {
            csgroups = request.getHeader("ROLES");;
        } catch (Exception ex){
            logger.trace("CSGROUPS not found in Request Http Headers");
            ex.printStackTrace();
        }

        //Roles
        if(csgroups != null) {
            StringTokenizer st = new StringTokenizer(csgroups, "^");

            while (st.hasMoreElements()) {
                GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_" + st.nextToken());
            }
        }
        return new User(username, "", true, true, true, true,auth);
    }


    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
}