官方的keycloak的适配器并没有提供.net版本的,所以我们需要自己去实现一下,目前打算把资源服务器对接KC之后,让资源服务器的API接口通过KC的UMA授权方式来管理起来,所以需要对这个功能进行开发,springboot版本官方已经实现,.net core版本我们自己实现了一下,对UMA授权不清楚的同学可以先看我这篇文章《keycloak~授权功能的使用》。
- 添加标准的依赖包
<packagereference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.13" />
- 如果你只用kc做认证,授权自己去实现,你可以直接引用OIDC包,然后对OIDC产生的token进行解析,拿到kc颁发的角色即可
<packagereference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="3.1.13" />
/// <summary>
/// OIDC认证
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
/// <param name="scopes"></param>
/// <returns></returns>
public static AuthenticationBuilder addKcOidc(this IServiceCollection services, IConfiguration configuration, ICollection<string> scopes)
{
return services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()//开启cookie的支持
.AddOpenIdConnect(options =>
{
options.Authority = configuration["Oidc:Authority"];
options.ClientId = configuration["Oidc:ClientId"];
options.ClientSecret = configuration["Oidc:ClientSecret"];
options.Savetokens = true;
options.ResponseType = OpenIdConnectResponseType.CodeIdTokenToken;
options.RequireHttpsMetadata = false;
options.GetClaimsFromUserInfoEndpoint = true;
options.Savetokens = true;
options.Scope.Clear();
foreach (var scope in scopes)
{
options.Scope.Add(scope);
}
options.Events = new OpenIdConnectEvents
{
OnTokenValidated = context =>
{
//获取到了id_token
var identity = context.Principal.Identity as ClaimsIdentity;
var token = context.ProtocolMessage.Accesstoken;
if (token != null)
{
var payload = token.Split(".")[1];
string payloadJson = Encoding.UTF8.GetString(Base64UrlDecode(payload));
JObject json = JObject.Parse(payloadJson);
if (json.ContainsKey("realm_access"))
{
var access = json["realm_access"].Values();
foreach (var role in access.Values())
{
identity.AddClaim(new Claim(ClaimTypes.Role, role.ToString()));
}
}
// 客户端角色
if (json.ContainsKey("resource_access"))
{
var access = json["resource_access"].Values();
foreach (var role in access["roles"].Values())
{
identity.AddClaim(new Claim(ClaimTypes.Role, role.ToString()));
}
}
}
return Task.CompletedTask;
},
OnTokenResponseReceived = context =>
{
return Task.CompletedTask;
},
};
});
}
/// <summary>
/// uma远程资源
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
/// <param name="scopes"></param>
/// <returns></returns>
public static AuthenticationBuilder addKcUma(this IServiceCollection services, IConfiguration configuration)
{
return services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.Authority = configuration["Oidc:Authority"];
options.Audience = configuration["Oidc:ClientId"];
options.IncludeErrorDetails = true;
options.RequireHttpsMetadata = false;
options.Savetoken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = true,
Validissuer = configuration["Oidc:Authority"],
ValidateLifetime = false
};
options.Events = new JwtBearerEvents
{
OnTokenValidated = context =>
{
//获取到了id_token
var identity = context.Principal.Identity as ClaimsIdentity;
if (context.Request.Headers.ContainsKey(AuthName))
{
var token = context.Request.Headers[AuthName].ToString();
if (token != null)
{
var provider = services.BuildServiceProvider();//get an instance of IServiceProvider
var clientFactory = provider.GetService<IHttpClientFactory>();
string uma = getUmaToken(clientFactory, options.Authority, options.Audience, token).Result;
// 客户端所拥有的资源
List<UmaResource> umaResources = getUmaPermissions(uma);
// 本服务器的所有资源
List<string> allResources = getServerUmaPermissions(clientFactory, options.Authority, options.Audience, configuration["Oidc:ClientSecret"]).Result;
// 过滤本服务器的资源
umaResources = umaResources.Where(i => allResources.Contains(i.rsid)).ToList();
// 当前url
string currentUrl = context.Request.Path;
foreach (UmaResource item in umaResources)
{
// 校验当前url是否在客户端授权的url中
List<string> urls = getUmaUrls(clientFactory, options.Authority, item.rsid, token).Result;
foreach (string url in urls)
{
if (url.EndsWith("*"))
{
if (currentUrl.Contains(url.TrimEnd('*')))
return Task.CompletedTask;
}
else
{
if (url.Equals(currentUrl))
return Task.CompletedTask;
}
}
}
}
}
var payload = JsonConvert.SerializeObject(new { Code = "403", Message = "很抱歉,您无权访问该接口" });
context.Response.ContentType = "application/json";
context.Response.StatusCode = StatusCodes.Status403Forbidden;
context.Response.WriteAsync(payload);
return Task.Fromresult(0);
}
};
});
}
- 在startup中去注册我们的认证方式
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.addKcUma(Configuration);
services.AddAuthorization();
services.AddHttpClient();
}
- 未认证的资源将出现403的结果
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。