Keycloak & Asp.net core webapi 整合跳坑之旅
前言
之前,一直使用IdentityServer4作为.net core程序的外部身份认证程序,ID4的优点自不必说了,缺点就是缺乏完善的管理界面。
后来,学习java quarkus框架时,偶然遇到了keycloak,具备完善的管理界面,并且支持多个realms,和quarkus oidc结合非常完美,于是就思考能否用keycloak来控制.net core程序的身份认证。
准备工作
dotnet new webapi,创建一个默认的webapi项目
安装keycloak的docker版本,我这里使用mariadb来持久化keycloak的数据,贴出docker-compose文件如下:
version: '3'
services:
keycloak:
image: jboss/keycloak:9.0.3
environment:
KEYCLOAK_USER: admin
KEYCLOAK_PASSWORD: admin
DB_USER: keycloak
DB_PASSWORD: password
ports:
- 8180:8080 mariadb:
image: mariadb:10.4
command: ['--character-set-server=utf8','--collation-server=utf8_general_ci','--default-time-zone=+8:00']
environment:
MYSQL_ROOT_PASSWORD: example
MYSQL_DATABASE: keycloak
MYSQL_USER: keycloak
MYSQL_PASSWORD: password
volumes:
- mariadata:/var/lib/mysql volumes:
mariadata:
docker-compose up 启动keycloak,然后可以在 http://localhost:8180 访问管理界面。
不要使用默认的realm,新建一个realm,比如“test2”。
然后新建client,比如“webapi”,地址填写 http://localhost:5000, 就是asp.net core webapi程序即将运行的地址。
然后创建角色和用户。
代码编写
修改Controllers/WeatherForcastController.cs
在控制器类前面增加[Authorize], 并且修改反馈的内容,方便调试。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging; namespace WebApi1.Controllers
{
[Authorize]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
}; private readonly ILogger<WeatherForecastController> _logger; public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
} [HttpGet]
public IEnumerable<string> Get()
{
var result = new List<string>();
foreach (var claim in User.Claims)
result.Add(claim.Type+": "+claim.Value); result.Add("username: " + User.Identity.Name);
result.Add("IsAdmin: " + User.IsInRole("admin").ToString());
return result;
}
}
}
注意12行。
修改startup.cs
namespace WebApi1
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
} public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(); services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = "http://localhost:8180/auth/realms/test2";
options.RequireHttpsMetadata = false;
options.Audience = "account";
options.TokenValidationParameters = new TokenValidationParameters{
NameClaimType = "preferred_username"
}; options.Events = new JwtBearerEvents{
OnTokenValidated = context =>{
var identity = context.Principal.Identity as ClaimsIdentity;
var access = context.Principal.Claims.FirstOrDefault(p => p.Type == "realm_access");
var jo = JObject.Parse(access.Value);
foreach (var role in jo["roles"].Values()){
identity.AddClaim(new Claim(ClaimTypes.Role, role.ToString()));
}
return Task.CompletedTask;
}
};
});
} // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} //app.UseHttpsRedirection(); IdentityModelEventSource.ShowPII = true; app.UseRouting(); app.UseAuthentication();
app.UseAuthorization(); app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
这里的代码是遇到几个坑并解决之后的结果,下面列举遇到的坑和解决方法:
1、使用postman获取token之后,访问资源仍提示401,查看具体错误信息是audience=account,但是我们根据各种教程设置为webapi(同client-id)
第25行,设置audience=account后解决。
到现在也不知道为啥keycloak返回的是account而不是client-id。
2、控制器中User.Identity.Name=null
这主要源于ClaimType名称的问题,keycloak返回的claims中,使用preferred_username来表示用户名,和asp.net core identity默认的不同
第26行,修改默认的Claim名称后,User.Identity.Name可以正常返回用户名。
3、控制器中无法获取角色信息
和用户名类似,也是因为ClaimType问题,keycloak返回的角色信息claim名称是realm_access,而且内容是一段json文本,需要解析处理。
第30行,OnTokenValidated 事件中对角色Claim进行转换,然后角色信息正常。
修改后就可以使用[Authorize(Roles="admin")]来保护控制器或者方法了。
最后列举WeatherForecastController 的Get方法返回的各种claims和其他信息
[
"exp: 1587544810",
"iat: 1587544510",
"jti: 72648e7f-3bb4-4db1-b866-33cc26a5e5a1",
"iss: http://localhost:8180/auth/realms/test2",
"aud: account",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier: 8811d051-52a6-40fc-b7f3-15d949fb25cd",
"typ: Bearer",
"azp: webapi",
"session_state: a9fb6a90-368b-4619-8789-43e26c7f2b85",
"http://schemas.microsoft.com/claims/authnclassreference: 1",
"allowed-origins: http://localhost:5000",
"realm_access: {\"roles\":[\"offline_access\",\"admin\",\"uma_authorization\"]}",
"resource_access: {\"account\":{\"roles\":[\"manage-account\",\"manage-account-links\",\"view-profile\"]}}",
"scope: email profile",
"email_verified: false",
"preferred_username: admin",
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role: offline_access",
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role: admin",
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role: uma_authorization",
"username: admin",
"IsAdmin: True"
]
Keycloak & Asp.net core webapi 整合跳坑之旅的更多相关文章
- ionic + asp.net core webapi + keycloak实现前后端用户认证和自动生成客户端代码
概述 本文使用ionic/angular开发网页前台,asp.net core webapi开发restful service,使用keycloak保护前台页面和后台服务,并且利用open api自动 ...
- Asp.net Core WebApi 使用Swagger做帮助文档,并且自定义Swagger的UI
WebApi写好之后,在线帮助文档以及能够在线调试的工具是专业化的表现,而Swagger毫无疑问是做Docs的最佳工具,自动生成每个Controller的接口说明,自动将参数解析成json,并且能够在 ...
- Asp.net core WebApi 使用Swagger生成帮助页
最近我们团队一直进行.net core的转型,web开发向着前后端分离的技术架构演进,我们后台主要是采用了asp.net core webapi来进行开发,开始每次调试以及与前端人员的沟通上都存在这效 ...
- ASP.NET Core WebAPI中的分析工具MiniProfiler
介绍 作为一个开发人员,你知道如何分析自己开发的Api性能么? 在Visual Studio和Azure中, 我们可以使用Application Insight来监控项目.除此之外我们还可以使用一个免 ...
- Asp.net core WebApi 使用Swagger生成帮助页实例
最近我们团队一直进行.net core的转型,web开发向着前后端分离的技术架构演进,我们后台主要是采用了asp.net core webapi来进行开发,开始每次调试以及与前端人员的沟通上都存在这效 ...
- asp.net core webapi之跨域(Cors)访问
这里说的跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架中(iframe)的数据.只要协议.域名.端口有任何一个不同,都被当作 ...
- ASP.NET Core WebAPI 开发-新建WebAPI项目
ASP.NET Core WebAPI 开发-新建WebAPI项目, ASP.NET Core 1.0 RC2 即将发布,我们现在来学习一下 ASP.NET Core WebAPI开发. 网上已经有泄 ...
- Asp.Net Core WebApi学习笔记(四)-- Middleware
Asp.Net Core WebApi学习笔记(四)-- Middleware 本文记录了Asp.Net管道模型和Asp.Net Core的Middleware模型的对比,并在上一篇的基础上增加Mid ...
- ASP.Net Core WebApi几种版本控制对比
版本控制的好处: (1)助于及时推出功能, 而不会破坏现有系统. (2)它还可以帮助为选定的客户提供额外的功能. API 版本控制可以采用不同的方式进行控制,方法如下: (1)在 URL 中追加版本或 ...
随机推荐
- C - Sigma Function LightOJ - 1336 (数论)
一个让人脑洞大开的题. 题目大意比较简单,询问[1,n]有多少个数其因子和为偶数. 因子分解定理中求因子和的公式是 f(n)=(1+p1+p1^2+p1^3+...+p1^a1)(1+p2+p2^2+ ...
- [PHP] 调用微博API 发微博OAuth2.0
在实际测试中出现很多问题, 第一就是按照文档调用ACCESS_TOKEN的时候费老劲啦,因为是编辑线上的,有好多中文空格,没有看出来!整了好久! 第二个就是在调用api发微博的时候出现乱码!必须把发送 ...
- go 基础 结构体
结构体是类型中带有成员的复合类型.go语言使用结构体和结构体成员来描述真实世界的实体和实体对应的各种属性. go语言中的类型可以被实例化,使用new和&构造类型实例的类型是类型的指针. 结构体 ...
- python数据分析工具——Pandas、StatsModels、Scikit-Learn
Pandas Pandas是 Python下最强大的数据分析和探索工具.它包含高级的数据结构和精巧的工具,使得在 Python中处理数据非常快速和简单. Pandas构建在 Numpy之上,它使得以 ...
- python慎用os.getcwd() ,除非你知道【文件路径与当前工作路径的区别】
当你搜索 "获取当前文件路径" 时,有的文章会提到用os.getcwd(),但是这玩意要慎用! 废话不多说,直接上例子: E:\program_software\Pycharm\y ...
- 抖音人脸识别Autojs脚本
title: 抖音人脸识别Autojs脚本 用Autojs写的抖音人脸颜值检测脚本 疫情期间宅家久了,昨天闲着没事(好吧,有事情,但是我不想做) ,消费之火熊熊燃烧.一咬牙把Autojs入正了.我 ...
- 非阻塞算法(Lock-Free)的实现
目录 非阻塞的栈 非阻塞的链表 非阻塞算法(Lock-Free)的实现 上篇文章我们讲到了使用锁会带来的各种缺点,本文将会讲解如何使用非阻塞算法.非阻塞算法一般会使用CAS来协调线程的操作. 虽然非阻 ...
- 【Linux网络基础】TCP/IP 协议簇(各个常见协议介绍)
一.应用层协议 1. FTP 协议所在层次:应用层协议 名称:FTP协议 协议端口:20,21 协议说明: FTP(File Transfer Protocol,文件传输协议)是TCP/IP协议组 ...
- 如何理解Java的值传递
结论 为了加深印象,先把结论放在文章开头. ++Java中只有值传递++. 形参与实参 在理解Java的值传递 实参Argument 实际参数,主调用函数传递给调用函数的参数 形参Parameter ...
- JavaWEB开发时FCKeditor类似office界面的ajax框架,加入后就能做界面类似office,能进行简单的文本编辑操作+配置手册...
2019独角兽企业重金招聘Python工程师标准>>> FCKeditor是一款功能强大的开源在线文本编辑器(DHTML editor),它使你在web上可以使用类似微软Word 的 ...