问题描述
我有一个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
,而具有ADMIN
或SUPER_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密钥罩和基本身份验证在同一项目中一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!