网站首页 > 技术文章 正文
Keycloak Client Adapters简介
Keycloak client adapters are libraries that make it very easy to
secure applications and services with Keycloak. We call them adapters
rather than libraries as they provide a tight integration to the
underlying platform and framework. This makes our adapters easy to use
and they require less boilerplate code than what is typically required
by a library
以上是Keycloak管网对Client Adapter的解释。简单理解就是,Keycloak为不同平台的应用提供了一些库,方便应用集成Keycloak服务。
对于使用OpenID Connector协议,Keycloak针对不同平台的应用,提供了如下adapter:
Java
- JBoss EAP
- WildFly
- Fuse
- Tomcat
- Jetty 9
- Servlet Filter
- Spring Boot
- Spring Security
JavaScript (client-side)
- JavaScript
Node.js (server-side)
- Node.js
C#
- OWIN (community)
Python
- oidc (generic)
Android
- AppAuth (generic)
iOS
- AppAuth (generic)
Apache HTTP Server
- mod_auth_openidc
这里主要关注Java相关的adapter使用。首先介绍使用比较简单、通用的Servlet Filter Adapter,并通过源码分析Servlet Filter Adapter的原理、流程,后续再介绍如何使用Spring Boot Adapter以及Spring Security Adapter
使用Servlet Filter Adapter实现Token校验
背景简介
有一个应用,提供了REST API,但是需要提供合法的Token,Token校验通过了才能访问
环境准备
1.在Keycloak中创建测试Realm,名称为SpringBoot
2.创建Access Type为public类型的Client,client id为adapter-servlet-public,用于模拟前端应用获取Token,如下:
3.创建Acess Type为bearer-only类型的Client,client id为adapter-servlet-bearer,对应后端应用,如下:
从Installation Tab页面获取adapter-servlet-bearer配置信息,如下:
{
"realm": "SpringBoot",
"bearer-only": true,
"auth-server-url": "http://localhost:8180/auth/",
"ssl-required": "external",
"resource": "adapter-servlet-bearer",
"confidential-port": 0
}
4.在Keycloak管理控制台创建用户zhangsan,密码为123456
以上相关信息的创建可以参考前文Keycloak管理控制台操作
应用开发
1.创建SpringBoot应用
在父pom中指定SpringBoot版本及keycloak版本,如下:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring-boot.version>2.5.5</spring-boot.version>
<keycloak.version>16.0.0</keycloak.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.创建keycloak-adapter-servlet子模块
创建一个子模块,并添加Keycloak Servlet Filter Client Adapter依赖和spring-boot-web依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-servlet-filter-adapter</artifactId>
<version>${keycloak.version}</version>
</dependency>
添加application.yml配置,指定服务端口及日志级别
server:
port: 8081
logging:
level:
org.keycloak: debug
3.创建REST 资源
package com.ywu.keycloak.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class ResourceController {
@ResponseBody
@RequestMapping("/protected/resource")
public String protectedResource() {
return "this is protected resource";
}
@ResponseBody
@RequestMapping("/common/resource")
public String commonResource() {
return "this is common resource";
}
}
其中,/protected/resource表示受保护的资源,需要验证后才能访问;/common/resource表示能随意访问
4.创建Keycloak配置
在resources目录下新建keycloak目录,并在keycloak目录下创建keycloak-bearer-only.json文件,内容为前文创建的bearer-only类型client的Installation信息,如下:
{
"realm": "SpringBoot",
"bearer-only": true,
"auth-server-url": "http://localhost:8180/auth/",
"ssl-required": "external",
"resource": "adapter-servlet-bearer",
"confidential-port": 0
}
5.配置Keycloak Servlet Filter
package com.ywu.keycloak.config;
import org.keycloak.adapters.servlet.KeycloakOIDCFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.File;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class FilterConfig {
/**
* 配置keycloak servlet adapter 过滤器
* @return
*/
@Bean
public FilterRegistrationBean<KeycloakOIDCFilter> keycloakOIDCFilter() {
FilterRegistrationBean<KeycloakOIDCFilter> keycloakOIDCFilter = new FilterRegistrationBean<>();
keycloakOIDCFilter.setFilter(new KeycloakOIDCFilter());
// 添加过滤器拦截的URL,即访问这些URL需要认证收授权
keycloakOIDCFilter.addUrlPatterns("/protected/*");
// 为过滤器设置keycloak json配置文件的路径
Map<String, String> initParams = new HashMap<>();
String configFileName = "keycloak-bearer-only.json";
String keycloakJsonFilePath = getKeycloakJsonFilePath(configFileName);
initParams.put(KeycloakOIDCFilter.CONFIG_FILE_PARAM, keycloakJsonFilePath);
keycloakOIDCFilter.setInitParameters(initParams);
return keycloakOIDCFilter;
}
/**
* 获取resources目录下keycloak json配置文件的绝对路径
*
* @param fileName
* @return
*/
private String getKeycloakJsonFilePath(String fileName) {
URL resource = FilterConfig.class.getResource(String.join(File.separator, "/keycloak", fileName));
return resource.getPath();
}
}
通过FilterRegistrationBean注册了一个KeycloakOIDCFilter,拦截/protected/*模式匹配的资源,指定了Filter使用的Keycloak配置为keycloak-bearer-only.json
到此,整个应用就开发完毕了
测试
启动测试服务,正常情况下,服务会监听在8081端口
直接访问
通过Post Man访问common resource
可以正常访问
使用Post Man访问受保护资源,如下:
返回401,表示未授权
获取Token
使用之前创建的public类型的client来获取Token,模拟前端请求Token,如下:
其中,获取Token的URL格式为http://{keycloak服务器域名}:{keycloak服务器端口}/auth/realms/{自定义realm名称}/protocol/openid-connect/token
cURL格式的请求如下:
curl --location --request POST 'http://localhost:8180/auth/realms/SpringBoot/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Cookie: JSESSIONID=6FCFD67D3063CFE0A443BD7B161601A5.f64bbac2c4cd; JSESSIONID=6FCFD67D3063CFE0A443BD7B161601A5' \
--data-urlencode 'client_id=adapter-servlet-public' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'username=zhangsan' \
--data-urlencode 'password=123456'
正常情况下就能获取到Token,可以看出Token是JWT,并且默认有效期只要300秒
通过Token访问受保护资源
重新访问受保护资源,这次在请求Header中携带上Token,如下:
这次请求添加了请求头Authorization,值为Bearer {Token},其中Token部分即为上一步获取的JWT
cURL格式请求如下:
curl --location --request GET 'http://localhost:8081/protected/resource' \
--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJyOGxFQUMyQmZSVUhUVDUtRGEyUUp3dFJBNFdMbnpOaHZsTjdMSVF1YXVZIn0.eyJleHAiOjE2NTAyOTA1ODUsImlhdCI6MTY1MDI5MDI4NSwianRpIjoiY2JiMmZjOTYtNGNhNi00YjI2LTkzMGItMmQ2MzczZmUyZTFhIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MTgwL2F1dGgvcmVhbG1zL1NwcmluZ0Jvb3QiLCJhdWQiOlsiYWRhcHRlci1zZXJ2bGV0IiwiYWNjb3VudCJdLCJzdWIiOiI5MjUzNmM2Ny00YzdlLTQ2YzctOWM0NS04YWRkYWVjYTRmYzQiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhZGFwdGVyLXNlcnZsZXQtcHVibGljIiwic2Vzc2lvbl9zdGF0ZSI6IjQ0NmRmYTJmLTZkOGEtNDU0Mi05YmRhLWZmODU4ZjQxYzVhMSIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJBRE1JTiIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWRhcHRlci1zZXJ2bGV0Ijp7InJvbGVzIjpbIlNZUyJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJwcm9maWxlIGVtYWlsIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ6aGFuZ3NhbiJ9.o7nnY_ZqCCJYy_bXQFgx99PKrX0ZH9k6v3JzHDFkpzsHL9nEyZu68EDJyts04DGb58gkHyMgcULu6YoN2KJ48jwmMwiM4yfXfSGR6lrKPfWxBpmMYeB1kxgU69eCwxNdVfIGC49Ilt6kwVTI7GZinwxJNfWZrOUpgXqpMvajd0MsB4PVCZS46cN5qh9AH-RAvbEkMKoa3mV_lTe7QZ5VqKk45ztS2gP5SUYNygMhgT6TXuHBQRTSMAcALEvyIjNmKNuPX4SNwReLk3DrmpA3gcHEQG8uyRTT5LZXchS71egr7QAXNIwBnrUg-pDPsa-Un-pJHbjJ1xJZmNU1H-Ihjw' \
--header 'Cookie: JSESSIONID=6FCFD67D3063CFE0A443BD7B161601A5'
可以看出,这次能正常访问受保护资源了
使用Servlet Filter Adapter实现登录授权认证
背景简介
有些应用需要用户授权登录才能访问,此时可以使用授权码流程,当用户登录到应用时,发现未登录,则重定向到Keycloak服务并登陆,再重定向回应用
环境准备
- 创建Access Type类型为confidential的Client,client id为adapter-servlet-credentials-1,添加Valid Redirect URIs为http://localhost:8081/protected/*,获取其Installation信息,如下:
{
"realm": "SpringBoot",
"auth-server-url": "http://localhost:8180/auth/",
"ssl-required": "external",
"resource": "adapter-servlet-credentials-1",
"credentials": {
"secret": "0e053b89-d87f-460a-92ec-adcd11ce6034"
},
"confidential-port": 0
}
应用修改
1.修改之前创建的子模块keycloak-adapter-servlet,在resources下的keycloak目录中创建keycloak-confidential.json,内容如下:
{
"realm": "SpringBoot",
"auth-server-url": "http://localhost:8180/auth/",
"ssl-required": "external",
"resource": "adapter-servlet-credentials-1",
"credentials": {
"secret": "0e053b89-d87f-460a-92ec-adcd11ce6034"
},
"confidential-port": 0
}
2.修改FilterConfig类
将配置文件名称修改为keycloak-confidential.json,如下
测试
重启应用后,在浏览器中输入访问地址:http://localhost:8081/protected/resource,结果浏览器重定向到了Keycloak的登录页面,并提示用户登录
完整的地址如下:
http://localhost:8180/auth/realms/SpringBoot/protocol/openid-connect/auth?
response_type=code&client_id=adapter-servlet-credentials-1&
redirect_uri=http%3A%2F%2Flocalhost%3A8081%2Fprotected%2Fresource
&state=ade02c3f-4323-4888-b0cc-c5ad6dfd8a1f&login=true&scope=openid
其中,response_type值为code表示先获取code再用code换取Token的授权码流程,redirect_uri就是我们刚才访问的受保护资源。填写之前创建的用户名/密码为zhangsan/123456后,点击登陆,就能正常访问受保护资源了
示例代码地址
至此,就完成了使用Servlet Filter Adapter实现Token校验及授权码流程认证,代码地址如下:https://github.com/ywu2014/keycloak-demo/tree/master/keycloak-adapter-servlet
猜你喜欢
- 2024-12-25 Spring Boot整合Spring Cloud GateWay代理第三方应用的调用接口?
- 2024-12-25 Java 近期新闻:Hibernate 6.0、JobRunr 5.0、JHipster 7.8.0
- 2024-12-25 如何在Spring Boot中保证RESTful接口的安全性?
- 2024-12-25 Java项目实战第6天:登录业务的实现
- 2024-12-25 JavaEE概述总结:Servlet生命周期+JSP内置对象
- 2024-12-25 SpringBoot 无感刷新 Token springboot的token
- 2024-12-25 若依开发框架解析笔记(7)-jwt的应用
- 2024-12-25 Spring MVC中提供了哪些扩展机制?如何使用这些扩展机制?
- 2024-12-25 49个Spring经典面试题总结(附带答案)
- 2024-12-25 互联网项目的进步--前后端是怎么分离的?
- 02-21走进git时代, 你该怎么玩?_gits
- 02-21GitHub是什么?它可不仅仅是云中的Git版本控制器
- 02-21Git常用操作总结_git基本用法
- 02-21为什么互联网巨头使用Git而放弃SVN?(含核心命令与原理)
- 02-21Git 高级用法,喜欢就拿去用_git基本用法
- 02-21Git常用命令和Git团队使用规范指南
- 02-21总结几个常用的Git命令的使用方法
- 02-21Git工作原理和常用指令_git原理详解
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)