繁体   English   中英

Spring 使用 HTTP 基本和 OIDC 承载令牌进行安全认证

[英]Spring Security authentication with HTTP Basic and OIDC Bearer Token

I'm working on an web application that uses Spring Boot 2.4.1 and Spring Security 5.4.2, and I need to provide both HTTP Basic Authentication and Bearer Token Authentication (JWT access token is sent from a SPA for every API call). 所有 API URL 都以路径/api开头,并且必须使用承载令牌进行身份验证,但使用 HTTP Basic 所需的两个 URL( /api/func1/api/func2 )除外。

问题是,如果我为 HTTP Basic 激活扩展 WebSecurityConfigurerAdapter 的 class,则会跳过不记名令牌身份验证。

@Configuration
@EnableWebSecurity
@ConditionalOnProperty(name = "auth.enable", matchIfMissing = true)
@Order(1)
public class HttpBasicSecurityConfiguration extends WebSecurityConfigurerAdapter {

    private final RestBasicAuthEntryPoint authenticationEntryPoint;
    private final DataSource dataSource;

    @Autowired
    public HttpBasicSecurityConfiguration(final RestBasicAuthEntryPoint authenticationEntryPoint,
                                          final DataSource dataSource) {
        super();
        this.authenticationEntryPoint = authenticationEntryPoint;
        this.dataSource = dataSource;
    }

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests().antMatchers({"/api/func1/**","/api/func2/**"}).authenticated()
            .and()
            .httpBasic()
            .authenticationEntryPoint(authenticationEntryPoint);
    }

    @Override
    protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
        // Usernames, passwords and roles are stored into USERS and AUTHORITIES tables
        auth.jdbcAuthentication()
            .passwordEncoder(passwordEncoder())
            .dataSource(dataSource);
    }

    private PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
}

@Configuration
@EnableWebSecurity(debug=true)
@ConditionalOnProperty(name = "auth.enable", matchIfMissing = true)
@Order(2)
public class Oauth2SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    public Oauth2SecurityConfiguration(CactusSystemConfiguration systemConfiguration) {
        super();
        this.systemConfiguration = systemConfiguration;
    }

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeRequests()
            .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
            .antMatchers({"/ws/**","/api/mock/**"}).permitAll()
            .and()
            .authorizeRequests().antMatchers("/api/**").authenticated()
            .and()
            .oauth2ResourceServer().jwt();
    }
}

当调用需要使用不记名令牌进行身份验证的方法时,Spring 安全调试会打印以下信息:

Request received for GET '/api/genericFunc':

org.apache.catalina.connector.RequestFacade@517df0fb

servletPath:/api/genericFunc
pathInfo:null
headers: 
host: devbox:8080
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0
accept: */*
accept-language: en-US,en;q=0.5
accept-encoding: gzip, deflate
authorization: Bearer eyJ0eXAiOiJ.....
connection: keep-alive
cookie: JSESSIONID=D3E8CED6AB13EFF0D15BFA6C69AFEED4; JSESSIONID=C923091B9B9E38A19106BC4F529F7D13


Security filter chain: [
  WebAsyncManagerIntegrationFilter
  SecurityContextPersistenceFilter
  HeaderWriterFilter
  LogoutFilter
  BasicAuthenticationFilter
  RequestCacheAwareFilter
  SecurityContextHolderAwareRequestFilter
  AnonymousAuthenticationFilter
  SessionManagementFilter
  ExceptionTranslationFilter
  FilterSecurityInterceptor
]

显然BearerTokenAuthenticationFilter没有加载。

如果我从运行中排除 HttpBasicSecurityConfiguration ,那么我得到:

Security filter chain: [
  WebAsyncManagerIntegrationFilter
  SecurityContextPersistenceFilter
  HeaderWriterFilter
  LogoutFilter
  BearerTokenAuthenticationFilter
  RequestCacheAwareFilter
  SecurityContextHolderAwareRequestFilter
  AnonymousAuthenticationFilter
  SessionManagementFilter
  ExceptionTranslationFilter
  FilterSecurityInterceptor
]

知道为什么会这样吗? 也许不可能在 Spring Security 中使用具有相同祖先路径(即 /api)的 API 的两种不同的身份验证方法

发生这种情况是因为HttpBasicSecurityConfiguration匹配所有请求,因此只有/api/func1/**/api/func2/**会针对 http 基本身份验证进行检查,而其他请求则不需要进行身份验证。 此时将跳过 Spring 安全过滤器链,并且永远不会触发另一个过滤器。

您需要限制应用第一个过滤器的请求。 只需将HttpBasicSecurityConfiguration中的configure方法更改为:

@Override
protected void configure(final HttpSecurity http) throws Exception {
    http.csrf().disable()
            .requestMatchers().antMatchers("/api/func1/**","/api/func2/**")
            .and()
            .authorizeRequests().anyRequest().authenticated()
            .and()
            .httpBasic()
            .authenticationEntryPoint(authenticationEntryPoint);
}

请注意, .requestMatchers().antMatchers("/api/func1/**","/api/func2/**").authorizeRequests().anyRequest()之前应用,然后您可以使用任何请求,有无需再次匹配 url。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM