Spring Security - аутентификация недоступна в контексте безопасности - PullRequest
0 голосов
/ 16 октября 2018

У меня есть Angular2 SPI, разговаривающий со слоем API Spring Boot, защищенный с помощью OAuth-неявного потока предоставления и использующий Auth0 в качестве моего IDP.

Уровень API - это Spring Boot 2.1.0.M2, а такжеиспользование Spring Security 5.1.0.RC1 и Spring Security OAuth2 2.0.5.RELEASE

На стороне API очень мало настроек, кроме ResourceServer:

@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 (I 'мы также защитили /secure-endpoint с помощью .access("#oauth2.hasScope('examplescope')") и убедились, что это разрешает только аутентифицированные запросы, содержащие examplescope through).Внешний интерфейс отправляет токен Bearer, как и ожидалось.

Проблема в том, что я добавил некоторые пользовательские данные в токен доступа, которые мне нужно использовать на уровне API.Я пытаюсь извлечь пользовательские данные в моем secure-endpoint контроллере, используя SecurityContextHolder.getContext().getAuthentication(), но, несмотря на наличие контекста безопасности, объект аутентификации внутри него является нулевым.

Пошаговый код, который я вижучто объект Authentication существует в контексте безопасности (и содержит ожидаемые мной дополнительные данные), пока FilterChainProxy обрабатывает цепочку дополнительных фильтров.Он исчезает при обработке исходной цепочки (т. Е. Когда * вызывается originalChain.doFilter(request, response) в FilterChainProxy.doFilter), и отсутствует в любой последующей точке.Идентификатор объекта SecurityContextImpl не изменяется, поэтому он выглядит как один и тот же контекст, за исключением объекта аутентификации.

Я не могу понять, почему, где и как очищается объект аутентификации, или как заставить его закрепиться, чтобы он был доступен для контроллера.Любая помощь или указание по этому вопросу будут с благодарностью приняты.

Соответствующие выходные данные журнала (и, как уже упоминалось, объект аутентификации доступен и заполняется, как ожидается, до 3-го сообщения журнала снизу):

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