mirror of
				https://gitee.com/ThingsGateway/ThingsGateway.git
				synced 2025-10-31 23:53:58 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			279 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			279 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| //------------------------------------------------------------------------------
 | ||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | ||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | ||
| //  源代码使用协议遵循本仓库的开源协议及附加协议
 | ||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | ||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | ||
| //  使用文档:https://thingsgateway.cn/
 | ||
| //  QQ群:605534569
 | ||
| //------------------------------------------------------------------------------
 | ||
| 
 | ||
| using Microsoft.AspNetCore.Authentication;
 | ||
| using Microsoft.AspNetCore.Authorization;
 | ||
| using Microsoft.AspNetCore.Http;
 | ||
| 
 | ||
| using System.Security.Claims;
 | ||
| 
 | ||
| using ThingsGateway.Authorization;
 | ||
| using ThingsGateway.DataEncryption;
 | ||
| 
 | ||
| namespace ThingsGateway.Admin.Application;
 | ||
| 
 | ||
| /// <inheritdoc/>
 | ||
| public class BlazorAuthenticationHandler : AppAuthorizeHandler
 | ||
| {
 | ||
|     private readonly ISysDictService _sysDictService;
 | ||
|     private readonly ISysRoleService _sysRoleService;
 | ||
|     private readonly ISysUserService _sysUserService;
 | ||
| 
 | ||
|     public BlazorAuthenticationHandler(ISysUserService sysUserService, ISysRoleService sysRoleService, ISysDictService sysDictService)
 | ||
|     {
 | ||
|         _sysUserService = sysUserService;
 | ||
|         _sysRoleService = sysRoleService;
 | ||
|         _sysDictService = sysDictService;
 | ||
|     }
 | ||
| 
 | ||
|     /// <inheritdoc/>
 | ||
|     public override async Task HandleAsync(AuthorizationHandlerContext context, DefaultHttpContext httpContext)
 | ||
|     {
 | ||
|         var isAuthenticated = context.User.Identity?.IsAuthenticated;
 | ||
|         if (isAuthenticated == true)
 | ||
|         {
 | ||
|             if (await CheckVerificatFromCacheAsync(context).ConfigureAwait(false))
 | ||
|             {
 | ||
|                 await AuthorizeHandleAsync(context).ConfigureAwait(false);
 | ||
|             }
 | ||
|             else
 | ||
|             {
 | ||
|                 await Fail(context).ConfigureAwait(false);
 | ||
|             }
 | ||
|         }
 | ||
|         else
 | ||
|         {
 | ||
|             await Fail(context).ConfigureAwait(false);// 授权失败
 | ||
|         }
 | ||
|     }
 | ||
|     static async Task Fail(AuthorizationHandlerContext context)
 | ||
|     {
 | ||
|         var verificatId = UserManager.VerificatId;
 | ||
|         var verificatInfo = App.GetService<IVerificatInfoService>().GetOne(verificatId, false);
 | ||
|         if (App.HttpContext != null)
 | ||
|         {
 | ||
|             var identity = new ClaimsIdentity();
 | ||
|             App.HttpContext.User = new ClaimsPrincipal(identity);
 | ||
|         }
 | ||
|         context.Fail(); // 授权失败
 | ||
| 
 | ||
|         if (verificatInfo != null)
 | ||
|             await App.GetService<INoticeService>().UserLoginOut(verificatInfo.ClientIds, App.CreateLocalizerByType(typeof(BlazorAuthenticationHandler))["UserExpire"]).ConfigureAwait(false);
 | ||
| 
 | ||
|         DefaultHttpContext currentHttpContext = context.GetCurrentHttpContext();
 | ||
|         if (currentHttpContext == null)
 | ||
|             return;
 | ||
|         currentHttpContext.Response.StatusCode = 401; //返回401给授权筛选器用
 | ||
|         currentHttpContext.SignoutToSwagger();
 | ||
|         await currentHttpContext.SignOutAsync().ConfigureAwait(false);
 | ||
|     }
 | ||
|     /// <inheritdoc/>
 | ||
|     public override async Task<bool> PipelineAsync(AuthorizationHandlerContext context, DefaultHttpContext httpContext)
 | ||
|     {
 | ||
|         var userId = context.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.UserId)?.Value?.ToLong(0) ?? 0;
 | ||
| 
 | ||
|         var user = await _sysUserService.GetUserByIdAsync(userId).ConfigureAwait(false);
 | ||
|         if (context.Resource is Microsoft.AspNetCore.Components.RouteData routeData)
 | ||
|         {
 | ||
|             var roles = await _sysRoleService.GetRoleListByUserIdAsync(userId).ConfigureAwait(false);
 | ||
| 
 | ||
|             //这里鉴别用户使能状态
 | ||
|             if (user?.Status != true)
 | ||
|             {
 | ||
|                 return false;
 | ||
|             }
 | ||
| 
 | ||
|             //超级管理员都能访问
 | ||
|             var isSuperAdmin = context.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.SuperAdmin)?.Value.ToBoolean();
 | ||
|             if (isSuperAdmin == true) return true;
 | ||
| 
 | ||
|             // 获取超级管理员特性
 | ||
|             var superAdminAttr = routeData.PageType.CustomAttributes.FirstOrDefault(x =>
 | ||
|                x.AttributeType == typeof(SuperAdminAttribute));
 | ||
| 
 | ||
|             if (superAdminAttr != null) //如果是超级管理员才能访问的接口
 | ||
|             {
 | ||
|                 return false; //直接没权限
 | ||
|             }
 | ||
|             //获取角色授权特性
 | ||
|             var isRolePermission = routeData.PageType.CustomAttributes.FirstOrDefault(x =>
 | ||
|                x.AttributeType == typeof(RolePermissionAttribute));
 | ||
|             if (isRolePermission != null)
 | ||
|             {
 | ||
|                 //获取忽略角色授权特性
 | ||
|                 var isIgnoreRolePermission = routeData.PageType.CustomAttributes.FirstOrDefault(x =>
 | ||
|        x.AttributeType == typeof(IgnoreRolePermissionAttribute));
 | ||
|                 if (isIgnoreRolePermission == null)
 | ||
|                 {
 | ||
|                     // 路由名称
 | ||
|                     var routeName = routeData.PageType.CustomAttributes.FirstOrDefault(x =>
 | ||
|                         x.AttributeType == typeof(Microsoft.AspNetCore.Components.RouteAttribute))?.ConstructorArguments?[0].Value as string;
 | ||
|                     if (routeName == null) return true;
 | ||
| 
 | ||
|                     if ((!user.PermissionCodeList.Contains(routeName.CutStart("/")) && !user.PermissionCodeList.Contains(routeName))) //如果当前路由信息不包含在角色授权路由列表中则认证失败
 | ||
|                         return false;
 | ||
|                     else
 | ||
|                         return true;
 | ||
|                 }
 | ||
|                 else
 | ||
|                 {
 | ||
|                     return true;
 | ||
|                 }
 | ||
|             }
 | ||
|             else
 | ||
|             {
 | ||
|                 return true;
 | ||
|             }
 | ||
|         }
 | ||
|         else
 | ||
|         {
 | ||
|             //这里鉴别用户使能状态
 | ||
|             if (user?.Status != true)
 | ||
|             {
 | ||
|                 return false;
 | ||
|             }
 | ||
| 
 | ||
|             //超级管理员都能访问
 | ||
|             var isSuperAdmin = context.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.SuperAdmin)?.Value?.ToBoolean();
 | ||
|             if (isSuperAdmin == true) return true;
 | ||
| 
 | ||
|             if (httpContext == null)
 | ||
|             {
 | ||
|                 //非API请求
 | ||
|                 return true;
 | ||
|             }
 | ||
| 
 | ||
|             var superAdminAttr = httpContext.GetMetadata<SuperAdminAttribute>();
 | ||
| 
 | ||
|             if (superAdminAttr != null) //如果是超级管理员才能访问的接口
 | ||
|             {
 | ||
|                 //获取忽略超级管理员特性
 | ||
|                 var ignoreSpuerAdmin = httpContext.GetMetadata<IgnoreSuperAdminAttribute>();
 | ||
|                 if (ignoreSpuerAdmin == null && isSuperAdmin != true) //如果只能超级管理员访问并且用户不是超级管理员
 | ||
|                     return false; //直接没权限
 | ||
|             }
 | ||
| 
 | ||
|             //获取角色授权特性
 | ||
|             var isRolePermission = httpContext.GetMetadata<RolePermissionAttribute>();
 | ||
|             if (isRolePermission != null)
 | ||
|             {
 | ||
|                 //获取忽略角色授权特性
 | ||
|                 var ignoreRolePermission = httpContext.GetMetadata<IgnoreRolePermissionAttribute>();
 | ||
|                 if (ignoreRolePermission == null)
 | ||
|                 {
 | ||
|                     // 路由名称
 | ||
|                     var routeName = httpContext.Request.Path.Value;
 | ||
|                     if (routeName == null) return true;
 | ||
|                     if ((!user.PermissionCodeList.Contains(routeName.CutStart("/")) && !user.PermissionCodeList.Contains(routeName))) //如果当前路由信息不包含在角色授权路由列表中则认证失败
 | ||
|                         return false;
 | ||
|                     else
 | ||
|                     {
 | ||
|                         return true; //没有用户信息则返回认证失败
 | ||
|                     }
 | ||
|                 }
 | ||
|             }
 | ||
|             else
 | ||
|             {
 | ||
|                 return true;
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         return true;
 | ||
|     }
 | ||
| 
 | ||
|     /// <summary>
 | ||
|     /// 检查 BearerToken/Cookie 有效性
 | ||
|     /// </summary>
 | ||
|     /// <param name="context">DefaultHttpContext</param>
 | ||
|     /// <returns></returns>
 | ||
|     private async Task<bool> CheckVerificatFromCacheAsync(AuthorizationHandlerContext context)
 | ||
|     {
 | ||
|         DefaultHttpContext currentHttpContext = context.GetCurrentHttpContext();
 | ||
|         var userId = context.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.UserId)?.Value?.ToLong();
 | ||
|         var verificatId = context.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.VerificatId)?.Value?.ToLong();
 | ||
|         var expire = (await _sysDictService.GetAppConfigAsync().ConfigureAwait(false)).LoginPolicy.VerificatExpireTime;
 | ||
|         if (currentHttpContext == null)
 | ||
|         {
 | ||
|             return CheckVerificat(userId, verificatId);
 | ||
|         }
 | ||
|         else
 | ||
|         {
 | ||
|             if (await JWTEncryption.AutoRefreshToken(context, currentHttpContext, expire, expire * 2).ConfigureAwait(false))
 | ||
|             {
 | ||
|                 //var token = JWTEncryption.GetJwtBearerToken(currentHttpContext); //获取当前token
 | ||
| 
 | ||
|                 var verificatInfo = userId != null ? VerificatInfoService.GetOne(verificatId ?? 0, false) : null;//获取token信息
 | ||
|                 if (verificatInfo != null)
 | ||
|                 {
 | ||
|                     if (verificatInfo.VerificatTimeout < DateTime.Now.AddMinutes(1))
 | ||
|                     {
 | ||
|                         verificatInfo.VerificatTimeout = verificatInfo.VerificatTimeout.AddMinutes(verificatInfo.Expire); //新的过期时间
 | ||
|                         VerificatInfoService.Update(verificatInfo); //更新tokne信息到cache
 | ||
|                     }
 | ||
|                     return true;
 | ||
|                 }
 | ||
|                 else
 | ||
|                 {
 | ||
|                     return false;
 | ||
|                 }
 | ||
|             }
 | ||
|             else
 | ||
|             {
 | ||
|                 //失败
 | ||
|                 return false;
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
|     private static IVerificatInfoService _verificatInfoService;
 | ||
|     private static IVerificatInfoService VerificatInfoService
 | ||
|     {
 | ||
|         get
 | ||
|         {
 | ||
|             if (_verificatInfoService == null)
 | ||
|                 _verificatInfoService = App.GetService<IVerificatInfoService>();
 | ||
| 
 | ||
|             return _verificatInfoService;
 | ||
|         }
 | ||
|     }
 | ||
|     public static bool CheckVerificat(long? userId, long? verificatId, bool autoUpdate = true)
 | ||
|     {
 | ||
|         var verificatInfo = userId != null ? VerificatInfoService.GetOne(verificatId ?? 0, false) : null;//获取token信息
 | ||
| 
 | ||
|         if (verificatInfo != null)
 | ||
|         {
 | ||
|             if (verificatInfo.VerificatTimeout < DateTime.Now.AddMinutes(1))
 | ||
|             {
 | ||
|                 if (!autoUpdate && verificatInfo.VerificatTimeout < DateTime.Now)
 | ||
|                     return false;
 | ||
| 
 | ||
|                 if (verificatInfo.VerificatTimeout.AddMinutes(verificatInfo.Expire) < DateTime.Now)
 | ||
|                 {
 | ||
|                     return false;
 | ||
|                 }
 | ||
| 
 | ||
|                 if (autoUpdate)
 | ||
|                 {
 | ||
|                     verificatInfo.VerificatTimeout = verificatInfo.VerificatTimeout.AddMinutes(verificatInfo.Expire); //新的过期时间
 | ||
|                     VerificatInfoService.Update(verificatInfo); //更新tokne信息到cache
 | ||
|                 }
 | ||
| 
 | ||
|                 //无法在server中刷新cookies,单页面应用会一直保持登录状态,所以这里不需要刷新cookies,但是F5刷新后会重新登录
 | ||
| 
 | ||
|             }
 | ||
| 
 | ||
|             return true;
 | ||
|         }
 | ||
|         else
 | ||
|         {
 | ||
|             return false;
 | ||
|         }
 | ||
|     }
 | ||
| }
 | 
