身份认证传递
对于Abp比较熟悉的朋友应该对他里面的用户身份认证比较熟悉,他是通过实现微软提供的权限认证方式实现的,用户登录身份信息存储在System.Security.Claims.ClaimsPrincipal里面,但是用户的身份信息如何在不同的服务之间传递呢,不可能每一个服务都必须实现这套身份认证吧?比如我们请求调用过程如下:
Portal站点获取用户信息没有问题,但如何传递到调用的其他微服务呢?
这在之前文章中提到的传输Header就起到了作用,有两种方式可以处理,第一种我们可以直接把accessToken信息放到header里面,传输到服务提供者处理,服务提供程序解析里面的accessToken,赋值给Microsoft.AspNetCore.Authentication.JwtBearer. MessageReceivedContext的Token属性,参照abp里面signalr部分的身份认证信息处理,但是这里有个问题,就是没一个服务宿主应用都需要实现微软的身份认证这套逻辑;另外一种方式,我们的用户信息既然已经解析出来了,那可以直接拿到值,直接通过传输Header,传递给服务提供者,服务提供者拿到Header之后,直接给Claims赋值,这样传输的内容会少一些,并且服务不需要实现微软身份认证这套逻辑,我这里直接采用的第二种方式实现,代码如下:
客户端Invoke方法
var abpSession = Dependency.IocManager.Instance.Resolve<IAbpSession>(); if (!string.IsNullOrEmpty(abpSession.AccessToken)) // 将用户Token转递到其他微服务 { contextNameValueCollection.Add("AccessToken", "1"); contextNameValueCollection.Add("UserId", abpSession.UserId?.ToString()); contextNameValueCollection.Add("UserName", abpSession.UserName); contextNameValueCollection.Add("TenantId", abpSession.TenantId?.ToString()); contextNameValueCollection.Add("RoleIds", abpSession.RoleIds); } var jsonRespStr = LocalRpcRun(jsonReqStr, contextNameValueCollection);
服务端JsonRpcProcessor类方法
public static Task<string> Process(string sessionId, string jsonRpc, object context = null) { return Task<string>.Factory.StartNew((_) => { if ((context is Newtonsoft.Json.Linq.JObject) && (context as Newtonsoft.Json.Linq.JObject)["AccessToken"] != null) // 设置Token { var identity = new ClaimsIdentity(); var accessToken = ((Newtonsoft.Json.Linq.JValue)(context as Newtonsoft.Json.Linq.JObject)["AccessToken"]).Value; var userId = ((Newtonsoft.Json.Linq.JValue)(context as Newtonsoft.Json.Linq.JObject)["UserId"])?.Value; var tenantId = ((Newtonsoft.Json.Linq.JValue)(context as Newtonsoft.Json.Linq.JObject)["TenantId"])?.Value; var roleIds = ((Newtonsoft.Json.Linq.JValue)(context as Newtonsoft.Json.Linq.JObject)["RoleIds"])?.Value; var userName = ((Newtonsoft.Json.Linq.JValue)(context as Newtonsoft.Json.Linq.JObject)["UserName"])?.Value; if(userId != null) identity.AddClaim(new Claim(AbpClaimTypes.UserId, userId?.ToString())); if (tenantId != null) identity.AddClaim(new Claim(AbpClaimTypes.TenantId, tenantId?.ToString())); if (roleIds != null) identity.AddClaim(new Claim(AbpClaimTypes.RoleIds, roleIds?.ToString())); if (userName != null) identity.AddClaim(new Claim(AbpClaimTypes.UserName, userName?.ToString())); if (accessToken != null) identity.AddClaim(new Claim(AbpClaimTypes.AccessToken, accessToken.ToString())); Thread.CurrentPrincipal = new ClaimsPrincipal(identity); } if (context is NameValueCollection) { var namevalues = context as NameValueCollection; if(Thread.CurrentPrincipal == null && !string.IsNullOrEmpty(namevalues.Get("AccessToken"))) { var identity = new ClaimsIdentity(); var accessToken = namevalues.Get("AccessToken"); var userId = namevalues.Get("UserId"); var tenantId = namevalues.Get("TenantId"); var roleIds = namevalues.Get("RoleIds"); var userName = namevalues.Get("UserName"); if (userId != null) identity.AddClaim(new Claim(AbpClaimTypes.UserId, userId?.ToString())); if (tenantId != null) identity.AddClaim(new Claim(AbpClaimTypes.TenantId, tenantId?.ToString())); if (roleIds != null) identity.AddClaim(new Claim(AbpClaimTypes.RoleIds, roleIds?.ToString())); if (userName != null) identity.AddClaim(new Claim(AbpClaimTypes.UserName, userName?.ToString())); if (accessToken != null) identity.AddClaim(new Claim(AbpClaimTypes.AccessToken, accessToken.ToString())); Thread.CurrentPrincipal = new ClaimsPrincipal(identity); } } var tuple = (Tuple<string, string, object>)_; return ProcessInternal(tuple.Item1, tuple.Item2, tuple.Item3); }, new Tuple<string, string, object>(sessionId, jsonRpc, context)); }
请求处理拦截
Abp里面的拦截起点部分在ActionFilter里面,但是微服务调用没有经过ActionFilter的拦截,所以这部分需要添加到微服务处理过程中,同样是对直接调用方法的拦截,直接把代码迁移过来就可以了。
代码片段如下:
try { // 验证权限 var authorizationHelper = Dependency.IocManager.Instance.Resolve<IAuthorizationHelper>(); authorizationHelper.AuthorizeAsync(mothod.First(), mothod.First().DeclaringType).Wait(); } catch(AbpAuthorizationException ex) { JsonResponse response = new JsonResponse() { Result = null, Error = new JsonRpcException(-32600, "Authorize error", "The user has not permiss to " + Rpc.Method + ".", Rpc), Id = Rpc.Id }; callback.Invoke(response); CompletedProcess(Rpc, response, RpcContext); return response; } var _unitOfWorkDefaultOptions = Dependency.IocManager.Instance.Resolve<IUnitOfWorkDefaultOptions>(); var _unitOfWorkManager = Dependency.IocManager.Instance.Resolve<IUnitOfWorkManager>(); var unitOfWorkAttr = _unitOfWorkDefaultOptions.GetUnitOfWorkAttributeOrNull(mothod.First()); object results; if (!unitOfWorkAttr.IsDisabled) { using (var uow = _unitOfWorkManager.Begin(unitOfWorkAttr.CreateOptions())) { results = newDel.DynamicInvoke(parameters); uow.Complete(); } } else { results = newDel.DynamicInvoke(parameters); }