bas*_*bas 4 c# authorization jwt keycloak asp.net-core
我正在努力,真的,我正在努力。但我无法理解 ASP NET Core 中的授权概念。
我有:
什么有效:
什么不起作用:
[Authorize]属性修饰的任何控制器进行的任何调用。我读过这个、这个、这个、我能找到的关于 asp net core 身份验证/授权的每个 MSDN 页面,并且我尝试了许多不同的 nuget 包来完成工作。但什么也没有给出。
使用Keycloak.AuthServices.Authentication和Keycloak.AuthServices.Authorization实现了大部分目标,但即使如此,我也陷入了我真正想要授权的部分。无论我尝试什么,我都会不断收到[Authorize]修饰调用的 401 响应。
我的 keycloak 配置非常简单:
似乎做了它必须做的事情,我可以在我的角度前端应用程序中进行身份验证。
我设置的角度应用程序使用keycloak-angular它来将承载 JWT 复制到我向 REST API 后端发出的每个请求的 https 标头中。
在我的 ASP NET core 后端中,我设置了 Keycloak.AuthServices 中间件,如下所示:
在我的中添加了此部分appsettings.Development.json:
"Keycloak": {
"realm": "projects",
"auth-server-url": "http://keycloak/keycloak",
"ssl-required": "none",
"resource": "projects",
"verify-token-audience": false,
"credentials": {
"secret": ""
},
"confidential-port": 0
},
Run Code Online (Sandbox Code Playgroud)
该http://keycloakurl 是 docker 网络 url。这是有效的。当我将其更改为不存在的内容时,我收到 API 无法到达 keycloak 后端的错误。当我设置正确的地址时,只要控制器方法没有用该[Authorize]属性修饰,API 将允许来自前端应用程序的任何调用。
我在 ASP NET Core 应用程序中唯一需要做的就是:
// add keycloak auth service
builder.Services.AddKeycloakAuthentication(builder.Configuration);
// Specify that I want to use both authentication and authorization middleware
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.UseCors(PolicyName);
app.MapControllers();
Run Code Online (Sandbox Code Playgroud)
根据Keycloak.AuthServices 的作者的说法,这应该是保护 REST api 的最小应用程序。我现在应该能够拒绝[Authorize]没有有效承载 JWT 的修饰 API 调用,并允许它们使用有效承载 JWT。但它不会...它会导致 401,无论是否有有效的持有者 JWT。
最烦人的部分是,我无法“调试”任何东西。几乎所有事情都发生在“幕后”。如果事情是显而易见的,而且更重要的是,如果事情有效的话,那就太好了。但如果出现问题,那就太令人沮丧了......
有谁能够解开这个噩梦吗?调试技巧?向下钻取的技巧?
bas*_*bas 12
我将分享我所学到的一切,希望对其他人有所帮助。让我们从一些优秀博客中的答案开始吧!
对我了解身份验证世界如何运作有很大帮助的是:
与我读过的大量其他页面以及需要从不同“示例”拼凑而成的各种片段相比,这些页面实际上解释了其背后的理论,这对我的案例非常有帮助。
我还Keycloak.AuthServices.Auth*出于以下三个原因放弃了这些软件包:
下面我将详细介绍:
因此,如果您正在寻找其他内容或更具体的内容,我可能没有您正在寻找的答案。:)
我认为这是最简单的部分,至少对我来说是这样。当然,我首先必须设置 keycloak,但是有很多页面可以正确解释这部分,我不会添加更多噪音。我将分享我的 keycloak 实例中的内容:
我通过集成keycloak-Angular来使用 keycloak (嗯,确实是谷歌)进行了 Angular 身份验证。如果有更好的图书馆,我洗耳恭听,我找到了这个,而且它有效。
我遵循了他们的官方文档,它几乎是开箱即用的。
我可以通过简单地使用它们进行身份验证,KeycloakService如下所示
constructor(
private readonly keycloak : KeycloakService) {
}
async login() {
var isLoggedIn = await this.keycloak.isLoggedIn();
if (!isLoggedIn) {
await this.keycloak.login();
}
}
Run Code Online (Sandbox Code Playgroud)
也许是值得注意的事情。我确实在 my 的初始化函数中添加了一两个东西,app.module.ts这在接下来的步骤中非常方便地在后端 API 中获取 JWT。
function initializeKeycloak(keycloak: KeycloakService) {
return () =>
keycloak.init({
enableBearerInterceptor: true,
config: {
realm: 'projects',
url: `${environment.keyCloakUrl}`,
clientId: 'projects'
},
initOptions: {
onLoad: 'check-sso',
silentCheckSsoRedirectUri:
window.location.origin + '/assets/silent-check-sso.html'
}
});
}
Run Code Online (Sandbox Code Playgroud)
在这里,enableBearerInterceptor: true注入一个 http 拦截器,它将在应用程序进行的每个 http 调用中复制“承载 JWT 令牌”,从而将 JWT 令牌提供给后端 API(http 标头的一部分,带有“授权”密钥)。
在上面的代码中,我还替换了硬编码的 url,以便我可以从我的环境中检索它。这样我就可以设置一个我想在开发中使用的 url,以及一个我想在应用程序在我的 AWS EC2 实例上运行时使用的 url。
在经历了互联网上大量误导/完全不真实/模糊的建议之后,事实证明这非常简单。正如本答案的第一个链接(集成 OpenID 连接)中完美解释的那样,后端的“身份验证”可归结为两件事:
对于第一部分(验证 JWT),我需要做的就是提供 keycloak url,它“公开”keycloak 的 API 方法。其余的将由 ASP NET core 处理。这是通过在启动代码中添加“身份验证”服务来完成的。
我一直引用“身份验证”,因为对我来说这非常令人困惑。据我所知,当涉及到网络应用程序堆栈时,有不同的方法。我使用 Angular 作为前端,使用 ASP NET Rest API 作为后端。当然,anguar 可以被 ASP NET MCV 取代。“身份验证”发生在前端。好吧,无论如何,在我的脑海里;-)。因此,每次我在 google 上找到详细介绍“ASP NET Core 中的身份验证”的内容时,我总是感觉它是在 ASP NET MVC 的上下文中。但是,事实证明“检查承载 JWT”也与“身份验证”相关。我确信这是有道理的,只是对我来说没有。
我个人喜欢保持Program.cs组织性,所以我只是遵循 ASP NET core 的扩展方法方法,并将特定的“设置/配置”代码放在扩展方法中。对于身份验证服务,我最终得到以下结果:
public static IServiceCollection AddKeycloakAuthentication(this IServiceCollection services, IConfiguration configuration)
{
// https://dev.to/kayesislam/integrating-openid-connect-to-your-application-stack-25ch
services
.AddAuthentication()
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = Convert.ToBoolean($"{configuration["Keycloak:require-https"]}");
x.MetadataAddress = $"{configuration["Keycloak:server-url"]}/realms/projects/.well-known/openid-configuration";
x.TokenValidationParameters = new TokenValidationParameters
{
RoleClaimType = "groups",
NameClaimType = $"{configuration["Keycloak:name_claim"]}",
ValidAudience = $"{configuration["Keycloak:audience"]}",
// /sf/ask/4221432281/
ValidateIssuer = Convert.ToBoolean($"{configuration["Keycloak:validate-issuer"]}"),
};
});
services.AddAuthorization(o =>
{
o.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.RequireClaim("email_verified", "true")
.Build();
});
return services;
}
Run Code Online (Sandbox Code Playgroud)
从更高层次的角度来看,这告诉 ASP NET core 它将使用 Bearer JWT 来处理身份验证,并且将通过要求具有经过验证的电子邮件的经过身份验证的用户来进行授权。就我而言,经过验证的电子邮件可能是无意义的,因为我只允许谷歌提供商根据定义拥有有效的电子邮件。但是无所谓。
Bearer JWT 定义可能更有趣。
第一个JwtBearerOption是RequireHttpsMetadata。我在开发中以http模式使用 keycloak,在生产中以 https 模式使用 keycloak。我这样做是因为 https 需要证书。当我在开发中运行时,我只能提供自签名证书(因为据我所知,本地主机的证书不存在)。当 ASP NET 调用 keycloak api 时,使用自签名证书将导致错误,因为它将尝试验证自签名证书,但会失败。在浏览器中,您将能够接受风险。但由于我的其余 api 没有在浏览器中运行,所以这是一个死胡同。为了通过 http 验证令牌,您必须RequireHttpsMetadata = false在选项中指定。
下一个属性MetadataAddress很简单。这里我提供了keycloak的url,其中keycloak公开了它自己的API。该 URL 通常类似于“http://your-keycloak-url/realms/your-keycloak-realm/.well-known/openid-configuration”。
令牌验证参数用于从 JWT 中提取某些信息并将其提供给 ASP NET 默认结构。但它也告诉 ASP NET core 如何验证、验证什么等。
NameClaimType仅当您想将 JWT 令牌中的某些字段映射到属性时,这才有意义HttpContext.User.Identity.Name。就我而言,我希望将谷歌帐户的电子邮件地址映射到用户名。
“可配置”数据显然来自我的应用程序设置。如果需要的话,我将在这里分享它们以提供完整的概述。
appSettings.Development.json
"Keycloak": {
"server-url": "http://keycloak/keycloak",
"audience": "account",
"name_claim" : "preferred_username",
"validate-issuer": false,
"require-https": false
},
Run Code Online (Sandbox Code Playgroud)
appSettings.Production.json
"Keycloak": {
"server-url": "https://projects-admin.ddns:444/keycloak",
"audience": "account",
"name_claim" : "preferred_username",
"validate-issuer": true,
"require-https": true
},
Run Code Online (Sandbox Code Playgroud)
在启动代码中添加服务后,builder用于提供实际的WebApplication。我只需要指定要使用的中间件(身份验证和授权)。
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.UseCors(policyName);
app.MapControllers();
if (app.Environment.IsDevelopment() || app.Environment.IsStaging())
{
app.UseSwagger();
app.UseSwaggerUI(options => options.EnableTryItOutByDefault());
}
Run Code Online (Sandbox Code Playgroud)
我读过很多关于添加中间件的警告标志。要么是我无意中以正确的顺序复制/粘贴了内容,要么是随着时间的推移事情发生了变化,从而减少了麻烦。不管怎样,我从来没有遇到过这个具体问题。这个命令似乎对我来说效果很好。
所有代码就位后,唯一要做的就是用属性装饰一些控制器或方法Authorize,当不提供有效的 JWT 时,它们将返回 401(因此,从我的角度应用程序以外的任何其他地方调用它们)。
[Authorize]
[Route("api/[controller]")]
public class ProjectController : Controller
{
private readonly IProjectsService _projectsService;
private static readonly ILog Log = LogManager.GetLogger(typeof(ProjectController));
public ProjectController(
IProjectsService projectsService
)
{
_projectsService = projectsService;
}
[HttpGet]
[Route("all")]
public async Task<IActionResult> GetAll()
{
var projects = await _projectsService.GetAll().ConfigureAwait(false);
return Ok(projects);
}
}
Run Code Online (Sandbox Code Playgroud)
在上面的示例中,我装饰了控制器,使类中的所有 API 方法都受到保护。我知道我只复制粘贴了一个,在我的实际课堂上还有更多:)。如果我想让一种方法在没有 JWT 令牌的情况下可用,我可以用[AllowAnonymous]. 否则,如果没有有效的 JWT,ASP NET 将返回 401 - 未经授权。
剩下要做的唯一一件事就是让 swagger 再次发挥作用。因为我刚刚保护了所有控制器,使它们实际上也无法用于 swagger。
再说一次,我在互联网上旅行的时间比我承认的要长得多。似乎有很多方法可以采取,从提供 Bearer JWT,到登录到 keycloak 中的专用 swagger 客户端,再到使用 google 提供程序再次进行实际身份验证。我只成功使用了 Bearer JWT(从运行我的角度应用程序的网络浏览器手动复制它)。我想这不是最方便的方法,或者也许是。我不在乎,无论如何我都会去的:)。
public static IServiceCollection AddSwaggerApplication(this IServiceCollection services)
{
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Projects-admin Swagger", Version = "v1" });
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Description = "Please provide JWT with bearer (Bearer {jwt token})",
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
},
new List<string>() }
});
});
return services;
}
Run Code Online (Sandbox Code Playgroud)
当我在我的 swagger 页面中按“授权”时,这会提供以下对话框
当我从浏览器复制/粘贴 Bearer JWT 并点击“授权”时,我正在使用该 API,就像从我的 Angular 应用程序调用它一样。当然,JWT 会过期,所以如果我等待太久,我将需要重复这些步骤。这就是我目前所拥有的一切。
复制承载 JWT
在安全的 Rest API 中使用 Swagger,无需 Bearer JWT
提供 Bearer JWT 后重试
| 归档时间: |
|
| 查看次数: |
4164 次 |
| 最近记录: |