结合 Spring 安全 AUTHORIZATION 承载和 CXF

Combine Spring security AUTHORIZATION bearer and CXF

我正在为我的 restful 使用 Spring 安全 + Spring 核心并结合 CXF。

配置如下:

  1. web.xml 对于 CXF 配置:

    <!-- 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. 配置 CXF 端点(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 安全配置 - 过滤器

    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 安全配置 - 提供商

    @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 安全配置 - 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();
     }
    

但是过滤器不起作用。该请求仍然允许在没有任何 spring 安全验证的情况下进行。

这样的请求:

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

没有异常或错误。上面请求returns200(OK)。我预计会失败,因为请求中没有不记名令牌。

我们如何结合 Spring 安全性(使用不记名令牌方法)和 CXF?

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

您能否将以下内容也添加到您的 web.xml 中,看看执行是否命中 AntPathRequestMatcher matches 方法

<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>