Thang Le
Thang Le

Reputation: 1449

Combine Spring security AUTHORIZATION bearer and CXF

I am using Spring security + Spring core and combination with CXF for my restful.

Below are configs:

  1. web.xml for CXF config:

    <!-- Spring configuration for ContextLoaderListener -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    <!-- CXF configuration for resful webservices -->
    <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/services/*</url-pattern>
    </servlet-mapping>
    
  2. config CXF endpoint (context.xml)

    <!-- configure for restful endpoint for application services as web authentication... -->
    <jaxrs:server id="ApplicationServices"
        address="/Application">
        <jaxrs:serviceBeans>
            <ref bean="ControllerImpl" />
        </jaxrs:serviceBeans>
        <jaxrs:providers>
            <ref bean="jsonProvider" />
        </jaxrs:providers>
        <jaxrs:features>
            <bean id="loggingFeature"
                class="org.apache.cxf.feature.LoggingFeature">
                <property name="prettyLogging" value="true" />
            </bean>
            <ref bean="swagger2Feature" />
        </jaxrs:features>
    </jaxrs:server>
    
  3. spring security config - filter

    public class AuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    
    
    AuthenticationFilter(final RequestMatcher requiresAuth) {
        super(requiresAuth);
    }
    
    @Override
    public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
    
        //Optional<String> tokenParam = Optional.ofNullable(httpServletRequest.getHeader(AUTHORIZATION)); //Authorization: Bearer TOKEN
        String token= StringUtils.isNotEmpty(httpServletRequest.getHeader(AUTHORIZATION))? httpServletRequest.getHeader(AUTHORIZATION) : "";
        token= StringUtils.removeStart(token, "Bearer").trim();
        Authentication requestAuthentication = new UsernamePasswordAuthenticationToken(token, token);
        return getAuthenticationManager().authenticate(requestAuthentication);
    
    }
    
    @Override
    protected void successfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain, final Authentication authResult) throws IOException, ServletException  {
        SecurityContextHolder.getContext().setAuthentication(authResult);
        chain.doFilter(request, response);
     }
    }
    
  4. spring security config - provider

    @Component
    public class AuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
    
    @Autowired
    UserTokenService userTokenService;
    
    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) throws AuthenticationException {
        //
    }
    
    @Override
    protected UserDetails retrieveUser(String userName, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) throws AuthenticationException {
    
        Object token= usernamePasswordAuthenticationToken.getCredentials();
        return Optional
                .ofNullable(token)
                .map(String::valueOf)
                .flatMap(userTokenService::findByToken)
                .orElseThrow(() -> new UsernameNotFoundException("Cannot find user with authentication token=" + token));
      }
    }
    
    
  5. spring security config - SecurityConfiguration

    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
    private static final RequestMatcher PROTECTED_URLS = new OrRequestMatcher(
            new AntPathRequestMatcher("/services/**"));
    
    AuthenticationProvider provider;
    
    public SecurityConfiguration(final AuthenticationProvider authenticationProvider) {
        super();
        this.provider = authenticationProvider;
    }
    
    @Override
    protected void configure(final AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(provider);
    }
    
    /**
     * we don't need provide this service for now because we are using Vaadin
     */
    @Override
    public void configure(final WebSecurity webSecurity) {
        webSecurity.ignoring().antMatchers("/token/**");
    }
    
    @Override
    public void configure(HttpSecurity http) throws Exception {
         http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().exceptionHandling().and()
                .authenticationProvider(provider)
                .addFilterBefore(authenticationFilter(), AnonymousAuthenticationFilter.class).authorizeRequests()
                .requestMatchers(PROTECTED_URLS).authenticated().and().csrf().disable().formLogin().disable()
                .httpBasic().disable().logout().disable();
    }
    
    @Bean
    AuthenticationFilter authenticationFilter() throws Exception {
        final AuthenticationFilter filter = new AuthenticationFilter(PROTECTED_URLS);
        filter.setAuthenticationManager(authenticationManager());
        // filter.setAuthenticationSuccessHandler(successHandler());
        return filter;
    }
    
    @Bean
    AuthenticationEntryPoint forbiddenEntryPoint() {
        return new HttpStatusEntryPoint(HttpStatus.FORBIDDEN);
    }
    }
    
  6. findByToken

     @Override
     public Optional<User> findByToken(String token) {
         UserToken userToken = userTokenDAO.findByToken(token);
         if (userToken != null) {
    
         User user = new User(userToken.getUserId(), userToken.getUserPassword(), true, true, true, true,
                 AuthorityUtils.createAuthorityList("USER"));
    
         return Optional.of(user);
     }
     return Optional.empty();
     }
    

However filter does not work. The request still allows comming without any validation by spring security.

The request like:

curl -X POST "http://localhost:8080/my-app/services/Application/ControllerImpl/myservice1" -H "accept: application/json" -H "Content-Type: application/json" -d "string"

There is no exception or error. The above request returns 200 (OK). I expected to fail because of no bearer token on the request.

How can we combine Spring security (using bearer token method) and CXF ?

Upvotes: 1

Views: 619

Answers (1)

Based on discussion in the comments, it is clear spring security filter chain is not getting configured.

Can you please add the following to your web.xml as well and see if the execution is hitting AntPathRequestMatcher matches method

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Upvotes: 1

Related Questions