.NET云原生应用实践(四):基于Keycloak的认证与授权

news/2025/1/23 2:19:06/文章来源:https://www.cnblogs.com/daxnet/p/18500344

本章目标

  1. 完成Keycloak的本地部署与配置
  2. 在Stickers RESTful API层面完成与Keycloak的集成
  3. 在Stickers RESTful API上实现认证与授权

Keycloak的本地部署

Keycloak的本地部署最简单的方式就是使用Docker。可以根据官方文档构建Dockerfile,然后使用Docker Compose直接运行。由于Keycloak也是基础设施的一部分,所以可以直接加到我们在上一讲使用的docker-compose.dev.yaml文件中。同样,在docker文件夹下新建一个keycloak的文件夹,然后新建一个Dockerfile,内容如下:

FROM quay.io/keycloak/keycloak:26.0 AS builder# Enable health and metrics support
ENV KC_HEALTH_ENABLED=true
ENV KC_METRICS_ENABLED=true# Configure a database vendor
ENV KC_DB=postgresWORKDIR /opt/keycloak
# for demonstration purposes only, please make sure to use proper certificates in production instead
RUN keytool -genkeypair -storepass password -storetype PKCS12 -keyalg RSA -keysize 2048 -dname "CN=server" -alias server -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -keystore conf/server.keystore
RUN /opt/keycloak/bin/kc.sh buildFROM quay.io/keycloak/keycloak:26.0
COPY --from=builder /opt/keycloak/ /opt/keycloak/ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]

然后修改docker-compose.dev.yaml文件,加入一个名为stickers-keycloak的新的service:

stickers-keycloak:image: daxnet/stickers-keycloak:devbuild:context: ./keycloakdockerfile: Dockerfileenvironment:- KC_DB=postgres- KC_DB_USERNAME=postgres- KC_DB_PASSWORD=postgres- KC_DB_SCHEMA=public- KC_DB_URL=jdbc:postgresql://stickers-pgsql:5432/stickers_keycloak?currentSchema=public- KC_HOSTNAME=localhost- KC_HOSTNAME_PORT=5600- KC_HTTP_ENABLED=true- KC_HOSTNAME_STRICT=false- KC_HOSTNAME_STRICT_HTTPS=false- KC_PROXY=edge- KC_BOOTSTRAP_ADMIN_USERNAME=admin- KC_BOOTSTRAP_ADMIN_PASSWORD=admin- QUARKUS_TRANSACTION_MANAGER_ENABLE_RECOVERY=truecommand: ['start','--optimized']depends_on:- stickers-pgsqlports:- "5600:8080"

在这些环境变量中,KC_DB指定了Keycloak所使用的数据库类型,我们打算复用上一讲中所使用的PostgreSQL数据库,所以这里填写postgresKC_DB_USERNAMEKC_DB_PASSWORDKC_DB_SCHEMAKC_DB_URL指定了数据库的用户名、密码、schema名称以及数据库连接字符串。KC_HOSTNAMEKC_HOSTNAME_PORT指定了Keycloak运行的主机名和端口号,这个端口号需要跟ports里指定的对外端口号一致。KC_BOOTSTRAP_ADMIN_USERNAMEKC_BOOTSTRAP_ADMIN_PASSWORD指定了Keycloak默认的管理员名称和密码。

在启动Keycloak之前,还需要准备好PostgreSQL数据库,Keycloak启动后会自动连接数据库并创建数据库对象(表、字段、关系等等)。准备数据库也非常简单,继续沿用上一讲介绍的方法,在构建PostgreSQL数据库镜像的时候,将创建数据库的SQL文件复制到镜像中的/docker-entrypoint-initdb.d文件夹中即可。SQL文件包含以下内容:

SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;CREATE DATABASE stickers_keycloak WITH TEMPLATE = template0 ENCODING = 'UTF8' LOCALE_PROVIDER = libc LOCALE = 'en_US.utf8';
ALTER DATABASE stickers_keycloak OWNER TO postgres;

然后重新构建并运行PostgreSQL和Keycloak容器:

$ docker compose -f docker-compose.dev.yaml build
$ docker compose -f docker-compose.dev.yaml up

强烈建议在重建和运行容器之前,清除本地的stickers-pgsql:dev镜像,并且删除docker_stickers_postgres_data卷,以确保旧数据不会影响新的部署。

在成功启动容器之后,打开浏览器访问http://localhost:5600,应该可以打开Keycloak的主页面并用admin/admin进行登录。

在Keycloak中配置Stickers Realm

限于篇幅,这里就不把配置Keycloak的整个过程一一展示出来了,请移步到我之前写的几篇文章查看详细步骤:

  • 有关在Keycloak中实现多租户,并且对于单个租户下认证的配置,请参考《在Keycloak中实现多租户并在ASP.NET Core下进行验证》
  • 有关在Keycloak的租户下启用授权机制,请参考《Keycloak中授权的实现》

请根据上面两篇文章的步骤,进行如下的配置:

  1. 新建一个名为stickers的Realm
  2. 切换到stickers Realm,新建一个名为public的Client
  3. public Client下启用Direct access grants(暂时启用,用作测试)
  4. 新建一个名为usergroups的Client Scope,在这个client scope中,添加一个类型为Group Membership的client scope。将其Token Claim Name设置为groups。然后将这个client scope添加到public Client下
  5. public Client下新建两个角色:administratorregular_user,然后新建三个用户:daxnetnobodysuper并设置密码,然后创建一个名为public的group(名称与Client的名称一致),在public group下,新建users group,再在users group下,新建administrators group。将daxnet添加到users group,将super添加到administrators group,并将users group赋予regular_user角色,将administrators group赋予administrator角色
  6. public Client的Authorization配置中,创建四个Scope:admin.manage_usersstickers.readstickers.updatestickers.delete;然后创建两个resource:admin-api,它具有admin.manage_users scope,以及stickers-api,它具有stickers.readstickers.updatestickers.delete这三个scope
  7. 在public Client下,创建两个基于角色的Policy:require-admin-policy,它分配了administrator角色,以及require-registered-user-policy,它分配了regular_user角色
  8. 在Permissions下,创建四个Permission:
    1. admin-manage-users-permission:基于require-admin-policy,作用在admin.manage_users Scope
    2. stickers-view-permission:基于require-registered-user-policy,作用在stickers.read Scope
    3. stickers-update-permission:基于require-registered-user-policy,作用在stickers.update Scope
    4. stickers-delete-permission:基于require-registered-user-policy,作用在stickers.delete Scope

你可以参考上面列出的两篇文章和这些步骤来配置Keycloak,也可以使用本章的代码直接编译Keycloak Docker镜像然后直接运行容器,Keycloak容器运行起来之后,所有的配置都会自动导入,此时就可以使用根据界面上的设置,比对上面的步骤进行学习了。

在完成Keycloak端的配置之后,就可以开始修改Stickers.WebApi项目,使我们的API支持认证与授权了。

在Stickers.WebApi中启用认证机制

关于什么是认证,什么是授权,这里就不多作讨论了,网上相关文章很多,也可以通过ChatGPT获得详细的解释和介绍。我们首先实现一个目标,就是只允许注册用户可以访问Stickers微服务,而不管这些用户是不是真的具有访问其中的某些API的权限。我这里用粗体字强调了“注册用户”和“权限”两个概念,也就可以区分出什么是认证,什么是授权了,通俗地说:认证就是该用户是否被允许使用网站的服务,授权就是在允许使用网站服务的前提下,该用户是否可以对其中的某些功能进行操作。

在ASP.NET Core中,集成认证与授权机制是非常容易的,首先,向Stickers.WebApi项目添加Microsoft.AspNetCore.Authentication.JwtBearer NuGet包,然后在Program.cs中,加入如下代码:

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{options.Authority = "http://localhost:5600/realms/stickers";options.RequireHttpsMetadata = false;options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters{NameClaimType = "preferred_username",RoleClaimType = ClaimTypes.Role,ValidateIssuer = true,ValidateAudience = false};
});

上面的代码用来初始化ASP.NET Core的认证机制,我们使用Jwt Bearer Token的认证模块,在配置中,指定认证机构Authority为stickers Realm的Base URL,然后对token的认证进行参数配置。这里的NameClaimType指定了在解析access token的时候,应该将哪个Claim看成是用户名称,同理,RoleClaimType指定了应该将哪个Claim看成是用户角色。在启动了PostgreSQL和Keycloak容器之后,可以使用类似下面的cURL命令获得access token:

$ curl --location 'http://localhost:5600/realms/stickers/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_id=public' \
--data-urlencode 'client_secret=B2REunrXWN57KtQoJWoP2Dhr7gqKJrol' \
--data-urlencode 'username=daxnet' \
--data-urlencode 'password=daxnet'

然后打开jwt.io,将这个access token复制到Debugger的Encoded部分,在Decoded部分可以看到,用户名是在preferred_username字段指定的,这就是NameClaimType指定为preferred_username的原因:

当然,还需要在Program.cs文件中加入Authentication和Authorization的Middleware:

app.UseAuthentication();
app.UseAuthorization();

并在StickersController上启用Authorize特性:

[ApiController]
[Authorize]
[Route("[controller]")]
public class StickersController(ISimplifiedDataAccessor dac) : ControllerBase
{// ...
}

此时如果启动Stickers API,然后使用cURL获取所有的“贴纸”,则会返回401 Unauthorized:

$ curl --location 'http://localhost:5141/stickers?asc=true&size=20&page=0' -v
* Host localhost:5141 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:5141...
* Connected to localhost (::1) port 5141
> GET /stickers?asc=true&size=20&page=0 HTTP/1.1
> Host: localhost:5141
> User-Agent: curl/8.5.0
> Accept: */*
> 
< HTTP/1.1 401 Unauthorized
< Content-Length: 0
< Date: Sat, 26 Oct 2024 13:05:21 GMT
< Server: Kestrel
< WWW-Authenticate: Bearer
< 
* Connection #0 to host localhost left intact

但如果将刚刚获得的access token加到cURL命令中,就可以正常访问API了(access token太长,这里先把它截断了):

$ curl --location 'http://localhost:5141/stickers?asc=true&size=20&page=0' \--header 'Authorization: Bearer eyJh...' -v
* Host localhost:5141 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:5141...
* Connected to localhost (::1) port 5141
> GET /stickers?asc=true&size=20&page=0 HTTP/1.1
> Host: localhost:5141
> User-Agent: curl/8.5.0
> Accept: */*
> Authorization: Bearer eyJh...
> 
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< Date: Sat, 26 Oct 2024 13:08:06 GMT
< Server: Kestrel
< Transfer-Encoding: chunked
< 
* Connection #0 to host localhost left intact
{"items":[],"pageIndex":0,"pageSize":20,"totalCount":0,"totalPages":0}

 在Stickers.WebApi中启用授权机制

在我之前写的《ASP.NET Core Web API下基于Keycloak的多租户用户授权的实现》一文中,已经详细介绍了如何基于Keycloak完成授权,在Stickers案例中,我会采用相同的实现方式,因此这里就不再赘述具体的实现过程了,仅介绍Stickers微服务所特有的部分。

上面我们已经在Keycloak中配置了授权,这里大致总结一下与授权相关的配置。首先,我们定义了四个scope,分别是:admin.manage_users、stickers.read、stickers.update以及stickers.delete。所谓的scope,其实就是对资源的操作类型;然后,我们定义了两种资源:admin-api和stickers-api,分别表示两组不同的API:admin-api表示与站点管理相关的API(虽然暂时我们还没有实现管理API),而stickers-api则表示与“贴纸”相关的API(也就是StickersController所提供的API);接下来,我们又定义了两个Policy:require-admin-policy和require-registered-user-policy,分别表示“干某件事需要管理员角色”和“干某件事需要注册用户角色”。可以看到,其实基于角色的授权,在Keycloak的整个授权体系中,只是其中的一种特例,Keycloak所支持的Policy类型,并不仅只有基于角色这一种策略;最后,定义了四个Permission:admin-manage-users-permission、stickers-delete-permission、stickers-update-permission和stickers-view-permission,这些permission都关联了对应的策略(这里都是基于角色的策略)和对资源的操作类型scope,而这些操作类型又进一步被资源所引用。所以,总的来说,Permission就定义了符合某种策略(Policy)的访问者对某种资源(Resource)具有完成何种操作类型(Scope)的权限。

仔细思考你会发现,我们其实根本不关心当前登录用户是什么角色,我们只关心该用户的某些特质是否达到访问某种资源并完成相应操作的需求,角色只不过是这些特质中的一种。所以,一方面在API上,我们定义该API是什么资源,它支持什么操作,而另一方面,当认证用户访问该API时,我们从用户的Claims中读取该用户在该资源上所能完成的操作名称,两者进行比对即可,而至于认证用户是否满足访问该资源并完成该操作的需求,在Keycloak的授权模块中就已经完成计算了,Keycloak只是在发送的token中带上计算结果就可以了。

下图展示了在Keycloak中,针对daxnet这个用户所进行的权限评估,从评估结果可以看到,该用户在stickers-api资源上的stickers.read、stickers.update以及stickers.delete操作是具有权限的;而在admin-api资源上的admin.manage_users上是没有权限的。所以,我们只需要在Stickers.WebApi上实现这个判断就可以了。

完成这个判断逻辑,大致会需要两个步骤:首先,使用access token,通过将grant_type设置成urn:ietf:params:oauth:grant-type:uma-ticket并再次调用/realms/stickers/protocol/openid-connect/token接口,以获得包含授权信息的user claims,然后,在API被访问时,根据该API所支持的操作列表,从带有授权信息的user claims中查找,看是否API所支持的操作在user claims中能被找到,如果能找到,就说明该用户可以访问API,否则就返回403 Forbidden

完整代码这里就不详细介绍了,还是强烈建议移步阅读《ASP.NET Core Web API下基于Keycloak的多租户用户授权的实现》这篇博文,并配套本章节的源代码以了解细节。

这里还是涉及到user claims缓存的问题,因为在获取用户授权信息的时候,存在两次Keycloak的调用,这样做并特别高效,后续会考虑引入缓存机制来解决这个问题。

在完成代码的实现之后,就可以进行测试了,使用daxnet用户获取access token:

$ curl --location 'http://localhost:5600/realms/stickers/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_id=public' \
--data-urlencode 'client_secret=B2REunrXWN57KtQoJWoP2Dhr7gqKJrol' \
--data-urlencode 'username=daxnet' \
--data-urlencode 'password=daxnet'

然后使用这个access token来访问GET /stickers API,可以看到,能够成功返回结果:

$ curl --location 'http://localhost:5141/stickers' \--header 'Authorization: Bearer eyJhbGci......' \-v && echo
* Host localhost:5141 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:5141...
* Connected to localhost (::1) port 5141
> GET /stickers HTTP/1.1
> Host: localhost:5141
> User-Agent: curl/8.5.0
> Accept: */*
> Authorization: Bearer eyJhbGci......
> 
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< Date: Mon, 28 Oct 2024 13:15:58 GMT
< Server: Kestrel
< Transfer-Encoding: chunked
< 
* Connection #0 to host localhost left intact
{"items":[],"pageIndex":0,"pageSize":20,"totalCount":0,"totalPages":0}

重新使用nobody用户获取access token:

$ curl --location 'http://localhost:5600/realms/stickers/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_id=public' \
--data-urlencode 'client_secret=B2REunrXWN57KtQoJWoP2Dhr7gqKJrol' \
--data-urlencode 'username=nobody' \
--data-urlencode 'password=nobody'

然后使用这个access token来访问GET /stickers API,可以看到,API返回403 Forbidden

$ curl --location 'http://localhost:5141/stickers' \--header 'Authorization: Bearer eyJhbGci......' -v && echo
* Host localhost:5141 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:5141...
* Connected to localhost (::1) port 5141
> GET /stickers HTTP/1.1
> Host: localhost:5141
> User-Agent: curl/8.5.0
> Accept: */*
> Authorization: Bearer eyJhbGci......
> 
< HTTP/1.1 403 Forbidden
< Content-Length: 0
< Date: Mon, 28 Oct 2024 13:18:31 GMT
< Server: Kestrel
< 
* Connection #0 to host localhost left intact

总结

本文简单介绍了在Stickers.WebApi上基于Keycloak实现认证与授权的步骤,由于一些原理性的内容和具体实现细节在之前我的博文中都有详细介绍,所以这里也就不再重复了,建议可以结合这些文章来阅读本章代码,相信会有不少的收获。下一章会基于.NET Web Assembly实现前端,并在开发环境中调通整个前后端流程。

源代码

本章源代码在chapter_4这个分支中:https://gitee.com/daxnet/stickers/tree/chapter_4/

下载源代码前,请先删除已有的stickers-pgsql:devstickers-keycloak:dev两个容器镜像,并删除docker_stickers_postgres_data数据卷。

下载源代码后,进入docker目录,然后编译并启动容器:

$ docker compose -f docker-compose.dev.yaml build
$ docker compose -f docker-compose.dev.yaml up

现在就可以直接用Visual Studio 2022或者JetBrains Rider打开stickers.sln解决方案文件,并启动Stickers.WebApi进行调试运行了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/823256.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

温习 SPI 机制 (Java SPI 、Spring SPI、Dubbo SPI)

SPI 全称为 Service Provider Interface,是一种服务发现机制。 SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。1…

.NET周刊【10月第3期 2024-10-20】

国内文章 我被 .NET8 JIT 的一个BUG反复折磨了半年之久(JIT tier1 finally optimizations) https://www.cnblogs.com/calvinK/p/18469889 作者分享了一次在公司中API服务器从.NET 6升级到.NET 8后遇到的JIT BUG经历。升级后一个实例在某些部署中出现AES解密明文字符丢失的问题,…

mysql弱密码爆破

mySQL弱密码靶场:/vulhub/mysql/CVE-2012-2122启动: docker-compose up -d扫描端口 nmap -Sv -Pn -T4 靶机ip看到在3306端口开启了mysql服务爆破账号密码1.使用超级弱口令检测工具(github下载)爆破出root/1234562.使用Hydra爆破 hydra -L 用户名字典 —P 密码字典 靶机IP m…

本人高分硕士论文项目:工业异常检测基准引擎

1. 架构 如图所示,IADBE(Industrial Anomaly Detection Benchmark Engine)系统由三个主要部分组成: IADBE、IADBE 服务器和 IADBE 后台。IADBE 是系统的核心,API 和 CLI 是网关。数据集、模型和指标是系统最重要的部分。模型基于开源的 Anomalib 和 YOLOv8。系统有三个主要…

CSP-S 2024 复赛游记

CSP-S 2024 游记Day -2 空白的一天。huge 不想太多天连着打模拟赛,并且想在明天安排一场,所以安排了专题。 今天是 dp 专题。 听了丁真的去做 AT 的 dp 专题 了,很晚才看 Vjudge。 效率有点低啊,这状态怎么打复赛(。 Day -1 全真模拟,换了座位。模拟赛有关。 挂了 40pts,…

现在才投简历还来得及吗?

某客热帖“现在才投互联网还有没有 HC?”,一时间引发了广泛的讨论。事情是这样的:有个小哥,其自身条件也不错,本硕 985 院校,求职意向是 Java 后端研发工程师,拿过国奖、有实习经历,各方面条件都不错。 但就是比较刚,秋招开始后只投了央国企、银行、运营商之类的工作,…

MaskGCT,AI语音克隆大模型本地部署(Windows11),基于Python3.11,TTS,文字转语音

前几天,又一款非自回归的文字转语音的AI模型:MaskGCT,开放了源码,和同样非自回归的F5-TTS模型一样,MaskGCT模型也是基于10万小时数据集Emilia训练而来的,精通中英日韩法德6种语言的跨语种合成。数据集Emilia是全球最大且最为多样的高质量多语种语音数据集之一。 本次分享…

图像处理领域的加速算子收集

1、Simd库——CPU指令集加速 算子 Simd Library Documentation. 部分算子截图: 2、VPI库——CPU、GPU(CUDA)加速 算子 VPI - Vision Programming Interface: Algorithms 部分算子截图: 3、CV-CUDA库 算子 CV-CUDA — CV-CUDA Beta documentation 部分算子截图:

postgresql 下载安装

一、postgresql 下载 pg官网:postgres.org一般推荐用源码安装,下载 .tar.gz 包 二、安装 本文以12.6版本安装为例: 2.1、安装前要求和环境配置 # 1、要求GNU make版本3.80或以上(GNU make有时以名字gmake安装),要测试make版本可以使用以下命令(如果是安装其他版本的pg具…

“药品追溯到客户管理:数字化转型下的药企发展之路”

随着科技进步和市场环境的变化,医药企业面临着前所未有的机遇和挑战。数字化转型已成为药企创新管理模式、提升市场竞争力的关键举措。在这一过程中,药品追溯和客户管理作为重要环节,通过数字化手段可以实现信息的高效流通和透明管理。以下将从药品追溯、客户管理以及未来发…

sgx模拟执行,不需要sgx硬件---sgx executed in simulation,No need to support hardware for SGX

sgx executed in simulation 使用项目:https://github.com/intel/linux-sgx.git前言:目前国内和国外互联网上关于使用模拟模式来完成sgx的博客我是真的一点没有找到,因此自己写一份博客来完成记录 环境:Ubuntu22.04不支持sgx,没有硬件存在(mac也可以按照本教程来完成工作…

算法定制视频分析网关拍照检测工业园区/厂区/工厂智慧安监方案

一、方案背景 随着工业化进程的加速,特别是制造业、建筑业、化工等高风险行业,生产安全事故频发,对人们的生命安全和健康构成了严重威胁。为了有效预防和减少重大事故的发生,提高安全管理水平,智慧安监方案应运而生。 二、方案内容 智慧安监方案的核心在于利用物联网、大数…