Spring Security 中使用Keycloak作为认证授权服务器
源 / 码农小胖哥 文/
适配器集成
keycloak-spring-security-adapter
:<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-security-adapter</artifactId>
<version>15.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
<version>15.0.0</version>
</dependency>
KeycloakWebSecurityConfigurerAdapter
作为创建WebSecurityConfigurer
实例的方便基类。我们可以编写了一个配置类来定制我们的安全策略,就像这样:@KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter
{
/**
* 注册了一个Keycloak的AuthenticationProvider
*/
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(keycloakAuthenticationProvider());
}
/**
* 定义会话策略
*/
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
/**
* 常见的Spring Security安全策略
*/
@Override
protected void configure(HttpSecurity http) throws Exception
{
super.configure(http);
http
.authorizeRequests()
.antMatchers("/customers*").hasRole("USER")
.antMatchers("/admin/**").hasRole("base_user")
.anyRequest().permitAll();
}
}
java.io.FileNotFoundException: Unable to locate Keycloak configuration file: keycloak.json
keycloak.json
文件的异常。Keycloak支持的每个Java适配器都可以通过一个简单的JSON文件进行配置,我们缺失的就是这个文件。{
"realm" : "demo",
"resource" : "customer-portal",
"realm-public-key" : "MIGfMA0GCSqGSIb3D...31LwIDAQAB",
"auth-server-url" : "https://localhost:8443/auth",
"ssl-required" : "external",
"use-resource-role-mappings" : false,
"enable-cors" : true,
"cors-max-age" : 1000,
"cors-allowed-methods" : "POST, PUT, DELETE, GET",
"cors-exposed-headers" : "WWW-Authenticate, My-custom-exposed-Header",
"bearer-only" : false,
"enable-basic-auth" : false,
"expose-token" : true,
"verify-token-audience" : true,
"credentials" : {
"secret" : "234234-234234-234234"
},
"connection-pool-size" : 20,
"socket-timeout-millis": 5000,
"connection-timeout-millis": 6000,
"connection-ttl-millis": 500,
"disable-trust-manager": false,
"allow-any-hostname" : false,
"truststore" : "path/to/truststore.jks",
"truststore-password" : "geheim",
"client-keystore" : "path/to/client-keystore.jks",
"client-keystore-password" : "geheim",
"client-key-password" : "geheim",
"token-minimum-time-to-live" : 10,
"min-time-between-jwks-requests" : 10,
"public-key-cache-ttl": 86400,
"redirect-rewrite-rules" : {
"^/wsmaster/api/(.*)$" : "/api/$1"
}
}
json
文件和图中的配置项是对应的。比较人性化的是我们不需要自行编写这个json
文件,Keycloak提供了下载客户端配置的方法,这里我只使用了必要的配置项:引入客户端配置
json
文件,但是加载这个json
配置却不太顺利,经过我的摸索需要实现一个KeycloakConfigResolver
并注入Spring IoC,有下面两种实现方式。复用Spring Boot Adapter配置
KeycloakConfigResolver
实现:/**
* 复用spring boot 的方法
*
* @return the keycloak config resolver
*/
@Bean
public KeycloakConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
application.yaml
的配置项:自定义实现
json
形式已经不重要了,你可以将json
文件的内容存储到任何你擅长的地方。/**
* 自己写解析
*
* @return the keycloak config resolver
*/
@Bean
public KeycloakConfigResolver fileKeycloakConfigResolver() {
return new KeycloakConfigResolver() {
@SneakyThrows
@Override
public KeycloakDeployment resolve(HttpFacade.Request request) {
// json 文件放到resources 文件夹下
ClassPathResource classPathResource = new ClassPathResource("./keycloak.json");
AdapterConfig adapterConfig = new ObjectMapper().readValue(classPathResource.getFile(), AdapterConfig.class);
return KeycloakDeploymentBuilder.build(adapterConfig);
}
};
}
角色命名策略
ROLE_
前缀,这需要我们声明GrantedAuthoritiesMapper
的实现SimpleAuthorityMapper
来完成这一功能。Keycloak在KeycloakAuthenticationProvider
中配置该功能:KeycloakAuthenticationProvider authenticationProvider = keycloakAuthenticationProvider();
authenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
完整的配置
applicaiton.yaml
:keycloak:
# 声明客户端所在的realm
realm: felord.cn
# keycloak授权服务器的地址
auth-server-url: http://localhost:8011/auth
# 客户端名称
resource: springboot-client
# 声明这是一个公开的客户端,否则不能在keycloak外部环境使用,会403
public-client: true
@KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
/**
* 复用spring boot 的方法
*
* @return the keycloak config resolver
*/
@Bean
public KeycloakConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
/**
* 自己写解析
*
* @return the keycloak config resolver
*/
// @Bean
public KeycloakConfigResolver fileKeycloakConfigResolver() {
return request -> {
// json 文件放到resources 文件夹下
ClassPathResource classPathResource = new ClassPathResource("./keycloak.json");
AdapterConfig adapterConfig = null;
try {
adapterConfig = new ObjectMapper().readValue(classPathResource.getFile(),
AdapterConfig.class);
} catch (IOException e) {
e.printStackTrace();
}
return KeycloakDeploymentBuilder.build(adapterConfig);
};
}
/**
* 配置{@link AuthenticationManager}
* 这里会引入Keycloak的{@link AuthenticationProvider}实现
*
* @param auth the auth
*/
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
KeycloakAuthenticationProvider authenticationProvider = keycloakAuthenticationProvider();
authenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(authenticationProvider);
}
/**
* 会话身份验证策略
*/
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
/**
* 配置 session 监听器 保证单点退出生效
*
* @return the servlet listener registration bean
*/
@Bean
public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<>(new HttpSessionEventPublisher());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.authorizeRequests()
.antMatchers("/customers*").hasRole("USER")
.antMatchers("/admin/**").hasRole("base_user")
.anyRequest().permitAll();
}
}
调用流程
springboot-client
有一个接口/admin/foo
,当未登录调用该接口时会转发到:http://localhost:8011/auth/realms/felord.cn/protocol/openid-connect/auth?response_type=code&client_id=springboot-client&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fsso%2Flogin&state=ec00d608-5ce7-47a0-acc8-8a20a2bfadfd&login=true&scope=openid
authorazation code flow
。总结
好文推荐
字节跳动小组长无意中得知整个部门的薪资!自己只有28K!手下人却拿35K!怎么办?
太尴尬!百度某程序员向领导请假去面试,却在面试一楼大厅和领导相遇,网友:缘分啊!回去一起对对面试题!
END
顶级程序员:topcoding
做最好的程序员社区:Java后端开发、Python、大数据、AI
一键三连「分享」、「点赞」和「在看」
评论