认证
目前的认证体系使用的是SSO,对应的Nuget包是Aegis.Authorization.SSO,目前提供的版本是3.0.0-preview1
目前还提供ESS认证鉴权,对应的Nuget包是Aegis.Authorization.ESS
同时还需要引入Aegis.Core.Authorization,这个包提供了CurrentUser、TokenHanlder等基本认证处理类。
启用认证和鉴权
在Component.deps.json中的Services节点中确定包含了Authorization
注意此时只是开启了基础认证和鉴权,请确保引用认证和鉴权的实现组件,否则会报错。
现有 SSO认证&鉴权组件可供使用
如果确定要使用SsoAuthorize,请在Component.deps.json且中也包含它,且SsoAuthorize顺序应该在Authorization后面。
请确保在AppSettings或者加载的配置文件中包含SsoAuthorize节点的配置,具体如下:
"SSOAuthorize": {
"Host": "http://10.11.13.75:30010/", //SSO 远程路径
"AppCode": "", //系统Code
"UserCode": "sys", //接口用户名
"Password": "App1234." //接口密码
},
如何获取当前用户
前端在请求接口的时候加入对应的HeaderAuthorization,并且传入具体的Token,表现为Bearer开头的Token
Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJJU1MiOiJTU08iLCJTVUIiOiJwdWJsaWMiLCJJQVQiOiIxNjk0NTk4NjQ2IiwiRVhQIjoiMjAxMDI0NjY0NiIsIkpUSSI6ImUzYmJlMTVkLTk5NmUtNGM1Ni05MmZjLTA4NTU4Y2UxYzMwMSJ9.FILF7psfjlp61P9mDmrHH3ZlCj9AtsnY2dA1yhEAEPk
在传入Token后,只要该Token对应的用户确实已登录,后端就能通过CurrentUser.Value准确的获取到对应的登录用户。对应的数据结构如下
public class CustomeUser
{
/// <summary>
/// 用户唯一标识
/// </summary>
public string UserIdentity { get; set; }
/// <summary>
/// 具体Token
/// </summary>
public string Token { get; set; }
/// <summary>
/// 过期时间
/// </summary>
public DateTime ExpireAt { get; set; }
/// <summary>
/// 自定义用户信息
/// </summary>
public object UserInfo { get; set; }
}
使用CurrentUser.Value.UserInfo就能获取到ISsoUserAdapter适配后的用户信息。也就是说这里的UserInfo实际上就是上面SsoUserAdapter.GetUser的返回值。
在Aegis.Authorization.SSO中提供了扩展方法支持CurrentUser.Value.SsoUser()的方法来快速获取SsoUser用户实例。本质上是将CurrentUser.Value.UserInfo转换为SsoUserModel。所以只需要调用一次并存起来就行。
用户自定义字典
Aegis.Core.Authorization 为用户提供了额外信息存储的方式,方便业务系统存储用户的额外信息。用法如下,对应的value处存储的是object类型,注意在存储值类型的时候就会出现装箱拆箱的情况,请尽量传递引用类型进去。
CurrentUser.Value.Set("keyName",1); //设置内容,如果传值类型会触发装箱
var number = CurrentUser.Value.Get("keyName"); //获取内容
认证失败与过期机制
当使用[ApiAuthorize]标注Controller或者对应的控制器方法后,如果框架获取不到当前用户CurrentUser.Value获取不到数据的情况下,会返回Http状态码为401的错误信息,告知前端需要跳转登录页面。
使用[AllowAnonymous]标注即可在没有Token的情况下进入方法内,常用与登录和用户信息设置。
目前框架内的ApiControllerBase全都带上了[ApiAuthorize],之后会去掉。
目前的SSO没有过期机制,之后会接入过期与刷新。
ESS认证鉴权
启用ESS认证授权功能
在 appsettings.json 中添加 ESS 认证配置:
"EssAuthorize": {
// ESS认证服务器地址
"DiscoveryUrl": "https://auth-sit.uicloud.com",
// ESS应用ID APPKey
"AppId": "DEZHENEMRTEST",
// ESS应用密钥 APPSecret
"AppSecret": "9855f3d80f8c5ad9256519b978a9a4d1b7a3c87464f3fa742caecf9f528033db",
// ESS开放服务的地址
"OpenUrl": "https://api-sit.uicloud.com/uap-portal-api",
// 扩展字段的key,当前只有JobNo(工号),多个以逗号分隔
"ExtendCodes": "JobNo"
}
在调试阶段可以关闭认证和授权,没有配置时默认关闭鉴权
"Auth": {
"EnableAuthentication": true, // 是否启用认证校验
"EnableAuthorization": true // 是否启用授权校验
}
在Component.deps.json文件中按顺序加入Authentication Authorization EssAuthorize
{
"Components": {
"Services": [
"Logging",
"Swagger",
"IdGenerator.SnowflakeId",
"BusinessServices",
"RequestValidation",
"EventBus",
"Jobs",
"Jobs.Postgres",
"SignalR",
"Authentication",
"Authorization",
"EssAuthorize"
],
"Middlewares": [
"Swagger",
"SignalR",
"Authentication",
"Authorization",
"EssAuthorize"
]
}
}
认证功能
采用redis存储用户会话信息,确保分布式环境下的用户认证一致性。 通过 ESS 认证服务器进行用户身份验证,支持 Token 的生成与刷新。
//获取Redis数据源
services.AddRedisSource<AegisRedisSource>(ConfigManager.Get<RedisOptions>("Redis"));
//注册ESS redis用户管理器
services.AddSingleton<IUserManager, EssRedisUserManager<AegisRedisSource>>();
登录login
提供登录接口/api/Auth/Login 根据前端传递的登录信息返回token,用户信息, 并缓存用户信息 实际的登录时前端在ESS提供的统一登录认证系统上进行登录获取授权码
// 前端传递的参数
{
// 从ESS获取的授权码
"userIdentity": "73c23ec90bf3f6deb775ecb8c3796b46",
// 登录成功跳转地址
"extendInfo": "http://localhost:4200"
}
// 具体实现在Login方法中
public Task<CustomUser> Login(LoginInfo loginInfo)
// 登录接口
curl --location --request POST 'http://localhost:5208/api/Auth/Login' \
--header 'Content-Type: application/json' \
--data-raw '{
"userIdentity": "9ef2200d4daa21283dfc85269ede7dd3",
"extendInfo": "http://localhost:4200"
}'
token校验CheckToken
当前所有的接口默认都需要token校验
添加[AllowAnonymous]不会进行校验
// 校验的header中的bear token
--header 'Authorization: Bearer eyJhbGciOi****
// 具体实现在CheckToken方法中
public Task<CustomUser?> CheckToken(string token)
UserInfo信息
/// <summary>
/// 用户姓名
/// </summary>
[JsonProperty("name")] public string Name { get; set; }
/// <summary>
/// AppId
/// </summary>
[JsonProperty("appId")] public string AppId { get; set; }
/// <summary>
/// 用户ID
/// </summary>
[JsonProperty("userId")] public string UserId { get; set; }
/// <summary>
/// 工号
/// </summary>
[JsonProperty("userCode")] public string UserCode { get; set; }
/// <summary>
/// 手机号
/// </summary>
[JsonProperty("userPhone")] public string UserPhone { get; set; }
登出logOut
提供登出的接口/api/Auth/Logout 这里的登出会清除用户信息的缓存,实际的登出是前端调用ESS的接口进行登出
// 具体实现在Logout方法中
public Task Logout(string token)
// 登出接口
curl --location --request POST 'http://localhost:5208/api/Auth/Logout' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer eyJhbGciOiJSUzI***' \
--data-raw '{}'
授权功能
默认的没有添加[AllowAnonymous]接口都是需要认证的接口
添加了[ApiAuthorize]的类或方法上 的接口表示不只是需要认证,还需要授权才能访问
用户token 访问与 客凭token访问均可以设置接口的是授权才能访问
在 DefaultResourcePermissionManager 中实现缓存接口的资源权限信息
// 缓存资源权限集合
public void SetResourcePermissions(IEnumerable<ResourcePermission> resourcePermissions, string key)
// 获取缓存中的资源权限集合, 缓存中没有则返回null,也可能是一个空集合
public IEnumerable<ResourcePermission> GetResourcePermissions(string key)
在EssAuthorizationManager.cs 中实现了与ESS交互获取权限信息的逻辑
public Task<IEnumerable<ResourcePermission>> GetResourcePermissions(string token)
注解ApiAuthorize中会对当前访问的接口进行鉴权
其中会根据token来缓存权限信息20分钟,减少频繁访问ESS服务
不管是用户token(Header中的Authorization) 还是客凭token(Header中的ClientAuthorization) 都会走这个方法进行鉴权
public void OnAuthorization(AuthorizationFilterContext context)
客凭访问访问功能
A服务访问B服务 A服务需要携带客凭token访问B服务 A服务访问B服务,B服务需要校验客凭token 接口可以配置认证或者授权,会进行认证或鉴权
在启动项中需要注册
// 获取Redis数据源
services.AddRedisSource<AegisRedisSource>(ConfigManager.Get<RedisOptions>("Redis"));
// 注册ESS 获取客户端令牌管理器
services.AddSingleton<EssTokenManager<AegisRedisSource>>();
A服务访问前需要获取客凭token
在EssTokenManager.cs 中实现了获取客凭token的逻辑, 会缓存30分钟,减少频繁访问ESS服务
public string GetClientToken()
A服务中获取客凭token访问其他服务例子
客凭访问在header中携带ClientAuthorization
其中ClientAuthorization的值为通过EssTokenManager.GetClientToken()获取的客凭token
public interface IEssTestContract
{
[Get("/v1/message/getManualMsgDetail?id=1911308108789514242")]
public Task<string> GetManualMsgDetail([Header("ClientAuthorization")] string authorization);
}
public class AuthorizationEssClientTokenTestController : ApiControllerBase
{
private readonly IEssTestContract _essTestContract;
private readonly EssTokenManager<AegisRedisSource> _essTokenManager;
public AuthorizationEssClientTokenTestController(EssTokenManager<AegisRedisSource> essTokenManager)
{
var bo = new BrokerClient("https://mh-dev.uicloud.com/oe/mr/umr-app-store");
_essTestContract = bo.Get<IEssTestContract>();
_essTokenManager = essTokenManager;
}
public async Task<string> Test()
{
// 获取客户端令牌后调用接口的例子
var resut = _essTestContract.GetManualMsgDetail(_essTokenManager.GetClientToken()).Result;
return resut;
}
依赖关系
依赖 Aegis.Caching.Redis 用于用户会话和权限信息的缓存管理 依赖 Aegis.core.Authentication 和 Aegis.core.Authorization 提供基础的认证和授权框架支持
如何扩展
当前认证体系可以接入大量扩展,提供以下接口供扩展
| 接口名 | 用途 |
|---|---|
| IUserManagaer | 用户数据储存管理接口,当前认证后的用户的管理接口,比如默认就是存在内存里,之后还可以扩展存在Redis里、数据库里等 |
| IAuthorizeManager | 具体认证管理接口,用于提供具体的认证流程,目前只提供了SSO的认证管理 |
开发模式快速验证
在DEBUG模式下,可以使用简单Token来直接通过认证和鉴权,目前可以直接传入1来快速获取一个UserName为1的Fake用户。
DEBUG模式下依然支持使用正常用户查询,在RELEASE模式下只能通过正常鉴权。
版本支持需求:
- Aegis.Core.Authorization > 2.0.2-preview1
- Aegis.Authorization.SSO > 2.0.3