标签:serve 示例 turn intro scheme 应该 etc image 问题
原文:asp.net core 自定义认证方式--请求头认证最近开始真正的实践了一些网关的东西,最近写几篇文章分享一下我的实践以及遇到的问题。
本文主要介绍网关后面的服务如何进行认证。
网关可以做一部分的认证和授权,服务内部有时候也会需要用户的信息,这时该怎么办呢,我们使用的是 JWT 认证,有一个 identity server去颁发,验证 token,一种简单方式可以把 token 直接往后传,传递给后面的具体某个服务,后面的服务可以去 identity server 拿到公钥信息去验证 token 的合法性,依然可以拿到用户的一些基本信息,但又觉得这样后面的服务还是要依赖 identityserver 不是太好,因为认证已经在网关做掉了,后面不应该再去做认证的事情了,而且解析 JWT token 也是有一定的性能损耗,于是想把用户的基本信息在网关认证完成之后放到请求头中。
我们网关用的Ocelot,开源的原生 .NET 项目方便自己扩展,Ocelot 中有一个 Claims2Headers 可以把 Claims 中的信息转换为请求头,详细使用参见文档,但是实现有个bug,如果有多个值他只会取第一个,详见issue,可以自己扩展一个 ocelot 的中间件替换掉原有的中间件。
把用户信息放到请求头中,后面的服务从请求头中就可以拿到用户的基本信息了,为了后面的服务不做过多的改动,我做了一个自定义的认证,从请求头中拿用户的基本信息进行认证,这样后面的服务还是可以直接使用 User.Identity.IsAuthenticated
和 User.Identity.Name
等,不需要做什么改动。于是就有了这一根据请求头认证的项目
下载示例项目,在 TestWebApplication 目录下运行 dotnet run
在浏览器中访问 http://localhost:5000/api/values
使用 postman 或 fiddler (或其它你喜欢的工具)带上 header 访问 http://localhost:5000/api/values
使用方式可以参考示例项目
使用自定义的 HeaderAuthentication 来替代之前的认证方式,默认配置了用户名,用户id以及用户角色,如果不能满足可以在 options 中的 AdditionalHeaderToClaims
中添加更多转换
services.AddAuthentication(HeaderAuthenticationDefaults.AuthenticationSchema)
.AddHeader(HeaderAuthenticationDefaults.AuthenticationSchema, options => { options.AdditionalHeaderToClaims.Add("UserEmail", ClaimTypes.Email); })
;
这样就可以了,你可以下载示例项目,快速体验,你可以直接添加下面几个请求头
UserId 用户id
UserName 用户名称
UserRoles 用户角色(多个角色以 , 分割,可以在 options 里自定义多个值的分隔符
直接访问需要授权才能访问的资源了
现在只是初步的设想与实现,并已经验证确实可行,代码还有一些业务逻辑比如 UserId 现在是必须的,可以根据自己需要自行修改,最近有点忙,找时间再修改重构一下再发布 nuget 包。如果有什么需求或问题,欢迎一起探讨。
自定义认证源码
提供了 HeaderAuthetication 和 QueryAuthentication 两种实现,一种使用请求头信息认证,一种使用 QueryString 信息认证。
HeaderAuthetication 主要实现在 HeaderAuthenticationHandler
核心代码,重写 Authenticate 方法:
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.ContainsKey(Options.UserIdHeaderName) || !Request.Headers.ContainsKey(Options.UserNameHeaderName))
{
return Task.FromResult(AuthenticateResult.NoResult());
}
var userId = Request.Headers[Options.UserIdHeaderName].ToString();
var userName = Request.Headers[Options.UserNameHeaderName].ToString();
var userRoles = new string[0];
if (Request.Headers.ContainsKey(Options.UserRolesHeaderName))
{
userRoles = Request.Headers[Options.UserRolesHeaderName].ToString()
.Split(new[] { Options.Delimiter }, StringSplitOptions.RemoveEmptyEntries);
}
var claims = new List<Claim>()
{
new Claim(ClaimTypes.NameIdentifier, userId),
new Claim(ClaimTypes.Name, userName),
};
if (userRoles.Length > 0)
{
claims.AddRange(userRoles.Select(r => new Claim(ClaimTypes.Role, r)));
}
if (Options.AdditionalHeaderToClaims.Count > 0)
{
foreach (var headerToClaim in Options.AdditionalHeaderToClaims)
{
if (Request.Headers.ContainsKey(headerToClaim.Key))
{
foreach (var val in Request.Headers[headerToClaim.Key].ToString().Split(new[] { Options.Delimiter }, StringSplitOptions.RemoveEmptyEntries))
{
claims.Add(new Claim(headerToClaim.Value, val));
}
}
}
}
// claims identity 's authentication type can not be null https://stackoverflow.com/questions/45261732/user-identity-isauthenticated-always-false-in-net-core-custom-authentication
var principal = new ClaimsPrincipal(new ClaimsIdentity(claims, Scheme.Name));
var ticket = new AuthenticationTicket(
principal,
Scheme.Name
);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
请注意,如果使用这种方式,请确保你的服务不会被外界直接访问,请求只能从网关或者本地调试发起。需要保证安全性,不能直接暴露到公网,才能使用这种方式。
如果有什么问题或者意见,欢迎与我联系。
标签:serve 示例 turn intro scheme 应该 etc image 问题
原文地址:https://www.cnblogs.com/lonelyxmas/p/10850716.html