java - Spring应用程序基本身份验证通过keycloack
问题描述
我需要将身份验证添加到我的 Spring Boot (MVC) 应用程序中。身份验证提供者是通过 OpenID 的 keycloak。隐式和授权代码授权都被禁用,所以我被资源所有者凭据授权所困扰。我想要实现的是未授权用户的基本身份验证提示。以这种方式检索的凭据应该用于从 keycloak 获取令牌和用户信息,以供 Spring Security 进一步使用。应在每个请求上检查令牌。
我发现的大多数示例都是使用org.keycloak:keycloak-spring-boot-starter
. 虽然我在enable-basic-auth
这里找到了,但它对我不起作用。我知道我必须使用keycloak.bearer-only=true
来关闭重定向,但它可以正常工作,而不是重定向它为未经授权的用户返回 401。
我的安全配置:
@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
@Bean
public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().permitAll();
}
}
我的 keycloak 属性(我不使用占位符,只是为了安全起见):
keycloak.auth-server-url=https://${keycloak.host}/auth/
keycloak.realm=master
keycloak.resource=${client.name}
keycloak.enable-basic-auth=true
keycloak.credentials.secret=${client.secret}
对不起,一般问题。我主要使用 jdbcAuthentication,这是我第一次使用身份管理软件。
解决方案
这是通过具有资源所有者密码凭据流的 keycloak openid 向您的应用程序添加基本身份验证的一种方法。
首先,您需要将其添加到 pom.xml:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.keycloak.bom</groupId>
<artifactId>keycloak-adapter-bom</artifactId>
<version>4.2.0.Final</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
...
<dependencies>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
</dependency>
...
</dependencies>
添加 keycloak 服务器属性:
keycloak.auth-server-url=https://${keycloak.host}/auth/
keycloak.realm=master
keycloak.resource=${client.name}
keycloak.credentials.secret=${client.secret}
keycloak.enable-basic-auth=true
keycloak.bearer-only=true
enable-basic-auth
实际上负责启用资源所有者密码凭据流。因此,每次您使用基本授权发送请求时,BasicAuthRequestAuthenticator
都会从 keycloak 请求令牌。bearer-only
需要关闭访问代码流。每次您在没有基本授权的情况下(以及外部授权会话)向安全资源发出请求时,您都会被重定向到 keycloak 服务器——我们不希望这样。bearer-only
你会得到401 。
最后一步是添加安全配置:
@Configuration
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
@EnableWebSecurity
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
@Bean
public KeycloakAuthenticationProvider authenticationProvider() {
KeycloakAuthenticationProvider provider = new KeycloakAuthenticationProvider();
provider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
return provider;
}
@Bean
public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.csrf().disable()
.exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"Restricted Content\"");
response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
})
.and()
.authorizeRequests()
.antMatchers("/admin/**").hasRole("admin")
.anyRequest().permitAll();
}
}
这里最有趣的部分是:
.exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"Restricted Content\"");
response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
})
标准 keycloakAuthenticationEntryPoint
实现将WWW-Authenticate
标头设置为String.format("Bearer realm=\"%s\"", realm)
以防授权失败。我需要将其设置Basic realm="Restricted Content"
为弹出基本身份验证提示。如果你想避免使用这个提示(你想添加你自己的登录表单) - 删除这部分。
推荐阅读
- ios - 无法结束 BackgroundTask:不存在具有标识符的后台任务,或者它可能已经结束。中断 UIApplicationEndBackgroundTaskError()
- c# - 我希望用 C# 替换网页中的文本
- python - 如何将同一行上多个熊猫列中的值连接到一个新列中?
- android - 观察 onCreate 片段时调用的 LiveData
- swift - 滚动iOS 13时使导航栏透明并将其更改为不透明
- python - 如何在烧瓶中已打开的选项卡中显示配置文件?
- c - 如何在 C 中创建一个将二维矩阵作为参数(并对其进行修改)的函数?
- image - Silverstripe 4 - SiteConfig 模块图像在模板中不起作用
- python - 如何在目录中随机显示每个文件一次,然后在我完成显示所有文件后再次显示
- ruby - 何时在 Ruby 中分配单例类?