本文介绍了Spring Boot密钥罩和基本身份验证在同一项目中一起使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Spring Boot安全问题。我想要的是在Spring Boot中同时对同一项目进行两种不同的身份验证。一种是对除‘/download/export/*’之外的所有路径的SSO(密钥罩认证),另一种是Spring Boot基本认证。以下是我的配置文件:

@Configuration 
@EnableWebSecurityp 
public class MultiHttpSecurityConfig {
@Configuration
@Order(1)
public static class DownloadableExportFilesSecurityConfig extends WebSecurityConfigurerAdapter
{
@Override
protected void configure(HttpSecurity http) throws Exception
{
    http
            .antMatcher("/download/export/test")
            .authorizeRequests()
            .anyRequest().hasRole("USER1")
            .and()
            .httpBasic();    }

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception
{
    auth.inMemoryAuthentication()
            .withUser("user").password("password1").roles("USER1");
}
}

@Configuration
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public static class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter
{
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception
{
    auth.authenticationProvider(keycloakAuthenticationProvider());
}

@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy()
{
    return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}

@Override
protected void configure(HttpSecurity http) throws Exception
{
    super.configure(http);
    http
            .regexMatcher("^(?!.*/download/export/test)")
            .authorizeRequests()
            .anyRequest().hasAnyRole("ADMIN", "SUPER_ADMIN")
            .and()
            .logout().logoutSuccessUrl("/bye");

}
}
上述代码的问题如下:如果我请求url‘/download/export/test’,那么它会询问我的用户名/密码(基本身份验证)。成功登录后,它再次要求我输入用户名/密码(但这次是密钥罩身份验证),即使请求的url已从SecurityConfig(密钥罩适配器)中排除。

它只给我一个警告:

2016-06-20 16:31:28.771  WARN 6872 --- [nio-8087-exec-6] o.k.a.s.token.SpringSecurityTokenStore   : Expected a KeycloakAuthenticationToken, but found org.springframework.security.authentication.UsernamePasswordAuthenticationToken@3fb541cc: Principal: org.springframework.security.core.userdetails.User@36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER1; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: 4C1BD3EA1FD7F50477548DEC4B5B5162; Granted Authorities: ROLE_USER1

您知道如何同时使用密钥罩和基本身份验证吗?

非常感谢!卡洛

推荐答案

问题说明

您遇到的问题是KeycloakAuthenticationProcessingFilter.java拦截每个带有HTTP Authorization标头的请求。如果您的请求未使用Keycloak进行身份验证(即使您使用任何其他身份验证提供商进行了身份验证!-在您的情况下使用基本身份验证),您将始终被重定向到Keycloak的登录页(在您的情况下)或获得401未经授权(如果您在keycloak.json中的Keycloak客户端配置为仅承载)。

默认情况下,如果请求匹配KeycloakAuthenticationProcessingFilter.DEFAULT_REQUEST_MATCHER,则调用KeycloakAuthenticationProcessingFilter.java

public static final RequestMatcher DEFAULT_REQUEST_MATCHER =
    new OrRequestMatcher(
            new AntPathRequestMatcher(DEFAULT_LOGIN_URL),
            new RequestHeaderRequestMatcher(AUTHORIZATION_HEADER),
            new QueryParamPresenceRequestMatcher(OAuth2Constants.ACCESS_TOKEN)
    );

这意味着任何与DEFAULT_LOGIN_URL(/sso/login)包含Authorization HTTP标头(在您的情况下)ACCESS_TOKEN作为查询参数的请求都将由KeycloakAuthenticationProcessingFilter.java处理。

这就是为什么您必须将RequestHeaderRequestMatcher(AUTHORIZATION_HEADER)替换为您自己的实现,在使用基本身份验证对请求进行身份验证时,该实现将跳过KeycloakAuthenticationProcessingFilter.java的调用。

解决方案

下面是一个完整的解决方案,使您能够在相同路径上同时使用基本身份验证和密钥罩身份验证。特别要注意IgnoreKeycloakProcessingFilterRequestMatcher实现,它取代了默认的RequestHeaderRequestMatcher。此匹配器将仅匹配包含值未以"Basic "为前缀的授权HTTP标头的请求。

在下面示例中,具有TESTER角色的用户可以访问/download/export/test,而具有ADMINSUPER_ADMIN角色的用户可以访问所有其他路径(在您的情况下,我假设这些用户是Keycloak服务器上的帐户)。

@KeycloakConfiguration
public class MultiHttpSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("tester")
                .password("testerPassword")
                .roles("TESTER");
        auth.authenticationProvider(keycloakAuthenticationProvider());
    }

    @Bean
    @Override
    protected KeycloakAuthenticationProcessingFilter keycloakAuthenticationProcessingFilter() throws Exception {
        RequestMatcher requestMatcher =
                new OrRequestMatcher(
                        new AntPathRequestMatcher(DEFAULT_LOGIN_URL),
                        new QueryParamPresenceRequestMatcher(OAuth2Constants.ACCESS_TOKEN),
                        // We're providing our own authorization header matcher
                        new IgnoreKeycloakProcessingFilterRequestMatcher()
                );
        return new KeycloakAuthenticationProcessingFilter(authenticationManagerBean(), requestMatcher);
    }

    // Matches request with Authorization header which value doesn't start with "Basic " prefix
    private class IgnoreKeycloakProcessingFilterRequestMatcher implements RequestMatcher {
        IgnoreKeycloakProcessingFilterRequestMatcher() {
        }

        public boolean matches(HttpServletRequest request) {
            String authorizationHeaderValue = request.getHeader("Authorization");
            return authorizationHeaderValue != null && !authorizationHeaderValue.startsWith("Basic ");
        }
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.authorizeRequests()
                .antMatchers("/download/export/test")
                .hasRole("TESTER")
                .anyRequest()
                .hasAnyRole("ADMIN", "SUPER_ADMIN")
                .and()
                .httpBasic();
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }
}

这篇关于Spring Boot密钥罩和基本身份验证在同一项目中一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-28 03:15