Spring Security-身份验证在安全上下文中不可用

时间:2018-10-16 14:58:10

标签: java spring-boot authentication spring-security-oauth2

我有一个与Spring Boot API层进行通信的Angular2 SPI,使用OAuth隐式授权流并将Auth0作为我的IDP进行保护。

API层是Spring Boot 2.1.0.M2,还使用Spring Security 5.1.0.RC1和Spring Security OAuth2 2.0.5.RELEASE

除了ResourceServer之外,API方面的配置很少:

@Configuration
@EnableResourceServer
@EnableWebSecurity(debug = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    private final CorsFilter corsFilter;
    private final CustomAccessTokenConverter accessTokenConverter;

    @Value("${oauth2.resource.id}")
    private String resourceId;

    @Value("${security.oauth2.resource.jwk.key-set-uri}")
    private String jwks;

    @Autowired
    public ResourceServerConfiguration(CorsFilter corsFilter, CustomAccessTokenConverter accessTokenConverter) {
        this.corsFilter = corsFilter;
        this.accessTokenConverter = accessTokenConverter;
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer config) throws Exception {
        config.resourceId(resourceId);
        config.tokenServices(tokenServices()).resourceId(resourceId);
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() throws Exception {
        final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(jwkTokenStore());
        return defaultTokenServices;
    }

    @Bean
    public TokenStore jwkTokenStore() throws Exception {
        return new JwkTokenStore(jwks, accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setAccessTokenConverter(accessTokenConverter);
        return converter;
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .anonymous().disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.GET, "/secure-endpoint").authenticated()
                .and()
                .addFilterBefore(corsFilter, ChannelProcessingFilter.class)
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
}

我的CorsFilter看起来像这样:

@Service
public class CorsFilter extends GenericFilterBean {

    private final AllowedOriginsAccessor allowedOriginsAccessor;

    @Autowired
    public CorsFilter(AllowedOriginsAccessor allowedOriginsAccessor) {
        this.allowedOriginsAccessor = allowedOriginsAccessor;
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        HttpServletRequest request = (HttpServletRequest) req;

        String allowedOrigins = allowedOriginsAccessor.getAllowedOrigins();
        response.setHeader("Access-Control-Allow-Origin", allowedOrigins);
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers",
                "x-requested-with, authorization, content-type, pragma, cache-control, expires");
        response.setHeader("Access-Control-Allow-Credentials", "true");

        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
        } else {
            chain.doFilter(req, res);
        }
    }
}

我的JwtAccessTokenConverter看起来像这样:

@Component
public class CustomAccessTokenConverter extends DefaultAccessTokenConverter {

        @Override
        public OAuth2Authentication extractAuthentication(Map<String, ?> claims) {
        OAuth2Authentication authentication = super.extractAuthentication(claims);
        authentication.setDetails(claims);
        return authentication;
    }
}

这全部有效,因为只允许来自前端的经过身份验证的请求访问/secure-endpoint(我还用/secure-endpoint保护了.access("#oauth2.hasScope('examplescope')"),并验证了这仅允许经过身份验证包含examplescope的请求。前端正在按预期方式发送Bearer令牌。

问题是我已经向访问令牌添加了一些自定义数据,我需要在API层中使用这些数据。我正在尝试使用secure-endpointSecurityContextHolder.getContext().getAuthentication()控制器中提取自定义数据,但是尽管有安全上下文,但其中的Authentication对象为null。

逐步执行代码,我可以看到在FilterChainProxy处理附加过滤器链时,安全性上下文中存在Authentication对象(并包含我期望的其他数据)。处理原始链时(即,在FilterChainProxy.doFilter中调用originalChain.doFilter(request, response)时,它消失了,在随后的任何时候都没有。 SecurityContextImpl对象ID不变,因此看起来像相同的上下文,减去Authentication对象。

我无法弄清为什么,在何处或如何清除Authentication对象,或者如何使其保持粘贴状态,以便控制器可以使用它。如有任何帮助或指示,我们将不胜感激。

相关的日志输出(如前所述,身份验证对象可用并按预期填充,直到底部的第三条日志消息为止):

o.s.security.web.FilterChainProxy        : /secure-endpoint?orderByLastModifiedAsc=false at position 1 of 11 in additional filter chain; firing Filter: 'CorsFilter'
o.s.security.web.FilterChainProxy        : /secure-endpoint?orderByLastModifiedAsc=false at position 1 of 11 in additional filter chain; firing Filter: 'CorsFilter'
o.s.security.web.FilterChainProxy        : /secure-endpoint?orderByLastModifiedAsc=false at position 2 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
o.s.security.web.FilterChainProxy        : /secure-endpoint?orderByLastModifiedAsc=false at position 3 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
o.s.security.web.FilterChainProxy        : /secure-endpoint?orderByLastModifiedAsc=false at position 4 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
o.s.security.web.FilterChainProxy        : /secure-endpoint?orderByLastModifiedAsc=false at position 5 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/logout', GET]
o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/secure-endpoint'; against '/logout'
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/logout', POST]
o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'GET /secure-endpoint' doesn't match 'POST /logout'
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/logout', PUT]
o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'GET /secure-endpoint' doesn't match 'PUT /logout'
o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/logout', DELETE]
o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'GET /secure-endpoint' doesn't match 'DELETE /logout'
o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
o.s.security.web.FilterChainProxy        : /secure-endpoint?orderByLastModifiedAsc=false at position 6 of 11 in additional filter chain; firing Filter: 'OAuth2AuthenticationProcessingFilter'
p.a.OAuth2AuthenticationProcessingFilter : Authentication success: org.springframework.security.oauth2.provider.OAuth2Authentication@2fe14c40: Principal: null; Credentials: [PROTECTED]; Authenticated: true; Details: remoteAddress=0:0:0:0:0:0:0:1, tokenType=BearertokenValue=<TOKEN>; Not granted any authorities
o.s.security.web.FilterChainProxy        : /secure-endpoint?orderByLastModifiedAsc=false at position 7 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
o.s.security.web.FilterChainProxy        : /secure-endpoint?orderByLastModifiedAsc=false at position 8 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
o.s.security.web.FilterChainProxy        : /secure-endpoint?orderByLastModifiedAsc=false at position 9 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter'
s.CompositeSessionAuthenticationStrategy : Delegating to org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy@720fbae6
o.s.security.web.FilterChainProxy        : /secure-endpoint?orderByLastModifiedAsc=false at position 10 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
o.s.security.web.FilterChainProxy        : /secure-endpoint?orderByLastModifiedAsc=false at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/secure-endpoint'; against '/secure-endpoint'
o.s.s.w.a.i.FilterSecurityInterceptor    : Secure object: FilterInvocation: URL: /secure-endpoint?orderByLastModifiedAsc=false; Attributes: [#oauth2.throwOnError(authenticated)]
o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated: org.springframework.security.oauth2.provider.OAuth2Authentication@2fe14c40: Principal: null; Credentials: [PROTECTED]; Authenticated: true; Details: remoteAddress=0:0:0:0:0:0:0:1, tokenType=BearertokenValue=<TOKEN>; Not granted any authorities
o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@110090b7, returned: 1
o.s.s.w.a.i.FilterSecurityInterceptor    : Authorization successful
o.s.s.w.a.i.FilterSecurityInterceptor    : RunAsManager did not change Authentication object
o.s.security.web.FilterChainProxy        : /secure-endpoint?orderByLastModifiedAsc=false reached end of additional filter chain; proceeding with original chain
o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@2d219451
s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed

0 个答案:

没有答案