Spring Boot(Spring Security)应用集成Keycloak实现统一身份验证、权限控制
Keycloak后台配置
创建Keycloak客户端
服务端的web类型的应用,最常见的就是提供web页面服务以及Restful API。不同类型的应用,Keycloak后台创建的配置会稍微有些区别。下面将创建3个客户端:
spring-boot-keycloak-web
:对应于纯web页面的应用srping-boot-keycloak-security-api
:对应于只提供Restful API服务的应用spring-boot-keycloak-webapi
:对应于同时提供web页面以及Restful API服务的应用
创建spring-boot-keycloak-web客户端

创建srping-boot-keycloak-security-api客户端

创建spring-boot-keycloak-webapi客户端

客户端访问类型(Access Type)说明
之前的文章已经说明过,这里再列一下,Keycloak目前的访问类型共有3种:
confidential
:适用于服务端应用,且需要浏览器登录以及需要通过密钥获取access token
的场景。典型的使用场景就是服务端渲染的web系统。
public
:适用于客户端应用,且需要浏览器登录的场景。典型的使用场景就是前端web系统,包括采用vue、react实现的前端项目等。
bearer-only
:适用于服务端应用,不需要浏览器登录,只允许使用bearer token
请求的场景。典型的使用场景就是restful api。
创建角色、用户,并进行绑定
分别创建用户admin、customer,及角色ROLE_ADMIN、ROLE_CUSTOMER,并进行绑定


Spring Boot(Spring Security)集成Keycloak配置及代码
添加Maven依赖
Spring Boot集成keycloak依赖
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
<version>${keycloak.version}</version>
</dependency>
如需使用Spring Security,则添加如下依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Keycloak关键配置
Keycloak配置文件示例
这里以同时提供web页面及Restful API服务的项目spring-boot-keycloak-webapi的配置为例,来看下怎么进行keycloak相关的配置
server:
port: 8083
keycloak:
realm: demo
auth-server-url: http://127.0.0.1:8080/auth
resource: spring-boot-keycloak-webapi
ssl-required: external
credentials:
secret: d50a059a-484b-4138-8403-22a491fbc488
use-resource-role-mappings: false
bearer-only: false
autodetect-bearer-only: true
security-constraints:
- authRoles:
- ROLE_CUSTOMER
securityCollections:
- name: customer
patterns:
- /customer
- authRoles:
- ROLE_ADMIN
securityCollections:
- name: admin
patterns:
- /admin
realm
:Keycloak后台对应的realm
auth-server-url
:Keycloak的地址
resource
:Keycloak后台创建的对应的Client
credentials.secret
:Keycloak添加客户端后Credentials Tab内对应的内容
use-resource-role-mappings
:使用realm级别还是应用级别的角色控制
bearer-only
:应用的Keycloak访问类型是bearer-only设置为true,否则设为false
autodetect-bearer-only
:应用同时提供web页面跟Restful API服务时需设置为true,Keycloak会根据请求的方式,将未通过认证的请求重定向到登录页或者直接返回401
状态码
security-constraints
:针对不同的路径定义相应的角色以实现权限管理,如果是集成Spring Security,则不需要此配置,改为在Spring Security相关的配置中控制
更多的配置控制可以查阅官方文档:Keycloak官方Java适配器配置
autodetect-bearer-only
机制说明
官方文档对于autodetect-bearer-only
的说明比较含糊
Keycloak auto-detects SOAP or REST clients based on typical headers like
X-Requested-With
,SOAPAction
orAccept
.
针对HTTP请求头到底取什么样的值才被识别为是API类型的请求,其实并没有描述的很清楚。我们只能找到相应的源码来具体看下,实际实现到底是怎么样的。
源码位于keycloak-adapter-core
jar包中的RequestAuthenticator
抽像类中
protected boolean isAutodetectedBearerOnly(HttpFacade.Request request) {
if (!deployment.isAutodetectBearerOnly()) return false;
String headerValue = facade.getRequest().getHeader("X-Requested-With");
if (headerValue != null && headerValue.equalsIgnoreCase("XMLHttpRequest")) {
return true;
}
headerValue = facade.getRequest().getHeader("Faces-Request");
if (headerValue != null && headerValue.startsWith("partial/")) {
return true;
}
headerValue = facade.getRequest().getHeader("SOAPAction");
if (headerValue != null) {
return true;
}
List<String> accepts = facade.getRequest().getHeaders("Accept");
if (accepts == null) accepts = Collections.emptyList();
for (String accept : accepts) {
if (accept.contains("text/html") || accept.contains("text/*") || accept.contains("*/*")) {
return false;
}
}
return true;
}
通过上面这段源码,我们就能很清晰的知道,在autodetect-bearer-only
配置设置为true时,HTTP请求头需满足以下几种情况,才被认为是API类型请求,这种情况下未通过认证直接返回401
状态码而不是重定向到登录页
X-Requested-With
请求头的值为XMLHttpRequest
Faces-Request
请求头的值以partial/
开头- 出现
SOAPAction
请求头 Accept
的请求头的值,不能包含text/html
、text/*
、*/*
这些值
集成Spring Security的配置说明
如需集成Spring Security,则Spring Boot配置中的security-constraints
可以删除,使用代码进行如下配置
@KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
/**
* Registers the KeycloakAuthenticationProvider with the authentication manager.
*/
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(keycloakAuthenticationProvider());
}
/**
* Read Keycloak config from spring boot config file
*/
@Bean
public KeycloakConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
/**
* Defines the session authentication strategy.
*/
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/customer/**").hasRole("CUSTOMER")
.antMatchers("/admin/**").hasAnyRole("ADMIN")
.anyRequest().permitAll();
}
}
Controller相关代码
@RequestMapping(value = "/", method = RequestMethod.GET)
public String index() {
return "index";
}
@RequestMapping(value = "/customer", method = {RequestMethod.GET, RequestMethod.POST})
public String customer(HttpServletRequest request) {
KeycloakSecurityContext keycloak = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
return "customer :: " + keycloak.getTokenString();
}
@RequestMapping(value = "/admin", method = {RequestMethod.GET, RequestMethod.POST})
public String admin(HttpServletRequest request) {
KeycloakSecurityContext keycloak = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
return "admin :: " + keycloak.getTokenString();
}
@RequestMapping(value = "/logout", method = {RequestMethod.GET, RequestMethod.POST})
public String logout(HttpServletRequest request) {
try {
request.logout();
return "logout success";
} catch (ServletException e) {
LOGGER.error("keycloak logout error", e);
return "logout fail";
}
}
项目效果演示
运行同时提供web页面及Restful API服务的项目spring-boot-keycloak-webapi,看下相关的效果
浏览器使用customer用户登录并访问/customer

浏览器使用customer用户登录并访问/admin

Postman直接请求/customer

Postman带上Accept请求头请求/customer

Postman带上Accept、Authorization请求头访问/customer
Authorization为上面浏览器customer用户登录后获取的access token值

Postman带上Accept、Authorization请求头访问/admin
Authorization为上面浏览器customer用户登录后获取的access token值

总结
得益于Keycloak官方提供的keycloak-spring-boot-starter
包,Spring Boot(Spring Security)应用集成Keycloak非常容易,只需少量的配置及代码,就可完成统一身份验证、权限控制功能。
项目示例地址:spring-boot-keycloak
转载自:https://juejin.cn/post/6844904160366706695