AuthFilterAttribute.cs 9.01 KB
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Dynamic;
using System.Linq;
using Hh.Mes.Api;
using Hh.Mes.API.Common;
using Hh.Mes.Common.config;
using Hh.Mes.Common.Json;
using Hh.Mes.Common.log;
using Hh.Mes.Common.Redis;
using Hh.Mes.POJO.Entity;
using Hh.Mes.Pojo.System;
using Hh.Mes.Service.Logs;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;

namespace Hh.Mes.API.AOP
{
    /// <summary>
    /// 全局登入token认证 验证
    /// </summary>
    public class AuthFilterAttribute : ActionFilterAttribute
    {
        #region 属性
        private readonly string ReturnMsgTokens = "{\"Code\":401,\"Status\":false,\"Message\":\"Tokens失效 请重新登入\",\"Result\":null}";
        private string requestContext { get; set; }
        private Stopwatch Stopwatch { get; set; }
        private UserAuthSession user { get; set; }
        private string token { get; set; }
        private HttpRequest httpContext { get; set; }
        private ControllerActionDescriptor description { get; set; }
        private string ip { get; set; }
        private readonly string[] alloweLog = { "PdaOfflineMenu", "GetOrderState" };
        #endregion

        public override void OnActionExecuting(ActionExecutingContext context)
        {
            #region 初始化 赋值
            Stopwatch = new Stopwatch();
            Stopwatch.Start();
            httpContext = context.HttpContext.Request;
            description = (ControllerActionDescriptor)context.ActionDescriptor;
            requestContext = JsonConvert.SerializeObject(context.ActionArguments);
            if (requestContext == "{}" && httpContext.ContentType != null && httpContext.Method == "POST")
            {
                if (httpContext.ContentType.IndexOf("form-data", StringComparison.Ordinal) > -1)
                {
                    requestContext = "{";
                    foreach (var item in context.HttpContext.Request.Form.Keys)
                    {
                        requestContext += item == "password" ? $"\"{item}\":\"{string.Concat(Enumerable.Repeat("*", context.HttpContext.Request.Form[item].ToString().Count()))}\"" : $"\"{item}\":\"{context.HttpContext.Request.Form[item]}\"";
                        if (item != context.HttpContext.Request.Form.Keys.Last()) requestContext += ",";
                    }
                    requestContext += "}";
                }
            }
            token = GetToken(context);
            ip = context.HttpContext.Connection.RemoteIpAddress.ToString();
            #endregion

            #region 获取请求参数 写日志
            //dynamic response;
            //if (httpContext.Method.ToLower() == "get")
            //{
            //    response = httpContext.QueryString.ToString();
            //}
            //else
            //{
            //    dynamic result = context.Result;
            //    response = IsPropertyExist(result, "Value") ? result.Value : "返回结果前";
            //}
            //EnqueueInterLog(response, user?.Account);
            #endregion

            #region 忽略拦截

            if (AopAllowed())
            {
                base.OnActionExecuting(context);
                return;
            }
            #endregion

            #region token 读取 日志写入队列
            if (string.IsNullOrEmpty(token))
            {
                ContextResponse(context);
                Log4NetHelper.Instance.Info("tokens 为null");
            }
            else
            {
                var cli = new RedisBase();
                user = cli.GetT<UserAuthSession>(token);
                if (user == null || string.IsNullOrEmpty(user.Account))
                {
                    Log4NetHelper.Instance.Info("user 为null:" + token);
                    ContextResponse(context);
                    Stopwatch.Stop();
                    EnqueueInterLog(ReturnMsgTokens, "");
                }
            }
            base.OnActionExecuting(context);
            #endregion
        }

        public override void OnActionExecuted(ActionExecutedContext context)
        {
            base.OnActionExecuted(context);
            Stopwatch.Stop();

            #region 获取请求参数 写日志
            dynamic response;
            if (httpContext.Method.ToLower() == "get")
            {
                var temp = context.Result as Microsoft.AspNetCore.Mvc.ObjectResult;
                response = IsPropertyExist(temp, "Value") ? temp.Value : "在返回结果后发生了异常Get";
            }
            else
            {
                dynamic result = context.Result;
                response = IsPropertyExist(result, "Value") ? result.Value : "在返回结果后发生了异常";
            }
            var keyUrl = context.HttpContext.Request.Path.Value;
            EnqueueInterLog(response, user?.Account, keyUrl);
            #endregion

            #region 忽略拦截

            if (AopAllowed()) return;
            #endregion

        }

        #region 自定义方法
        private string GetToken(ActionExecutingContext context)
        {
            var temp = context.HttpContext.Request.Query[Program.tokens];
            if (string.IsNullOrEmpty(temp)) temp = context.HttpContext.Request.Cookies[Program.tokens];
            if (string.IsNullOrEmpty(temp)) temp = context.HttpContext.Request.Headers[Program.tokens];
            return temp;
        }

        private void ContextResponse(ActionExecutingContext context)
        {
            context.HttpContext.Response.ContentType = "application/json";
            context.Result = new ContentResult
            {
                Content = ReturnMsgTokens,
                StatusCode = StatusCodes.Status200OK,
                ContentType = "application/json"
            };
        }

        /// <summary>
        /// 动态类型 dynamic 是否存在某个属性
        /// </summary>
        private static bool IsPropertyExist(dynamic data, string propertyname)
        {
            try
            {
                if (data == null) return false;
                if (data is ExpandoObject)
                    return ((IDictionary<string, object>)data).ContainsKey(propertyname);
                return data.GetType().GetProperty(propertyname) != null;
            }
            catch (Exception)
            {
                Log4NetHelper.Instance.Info("【AuthFilterAttribute、IsPropertyExist】方法异常,不影响程序运行,反复出现请检查程序");
                return false;
            }
        }

        private void EnqueueInterLog(dynamic response, string name, string keyUrl = "")
        {
            var path = httpContext.Path.ToString();
            if (alloweLog.Any(t => path.IndexOf(t, StringComparison.Ordinal) > -1)) return;

            var browser = httpContext.Headers[HeaderNames.UserAgent].ToString();
            var system = "api";
            if (browser.IndexOf("Android", StringComparison.Ordinal) > -1) system = "app Android";
            bool isOk = response is string;
            if (!isOk) response = JsonHelper.Instance.Serialize(response);
            var type = "接收";
            var isSaveLog = true;
            if (SystemVariable.apiList.Count == 0) SystemVariable.InitApiList();

            if (!string.IsNullOrEmpty(keyUrl) && SystemVariable.apiList.ContainsKey(keyUrl))
            {
                type = SystemVariable.apiList[keyUrl].Item1;
                isSaveLog = SystemVariable.apiList[keyUrl].Item2;
            }
            //InitApiList 配置false 不写入数据库队列日志
            if (!isSaveLog) return;

            QueueInterLog.GetInstance.Queue().Enqueue(new sys_interface_log()
            {
                type = type,
                system = system,
                method = httpContext.Method,
                server = httpContext.Host.ToString(),
                path = path,

                apiGroup = description.ControllerName,
                actionName = description.ActionName,
                queryString = httpContext.QueryString.ToString(),
                request = requestContext,
                response = response,

                totalMilliseconds = Stopwatch.Elapsed.TotalSeconds,
                logTime = DateTime.Now,
                createBy = user == null ? system : user.Name,
                name = name,
                ip = ip,
                browser = browser,
                result = "token:" + token
            });
        }


        /// <summary>
        /// 当前url 是否匹配 忽略拦截的 配置路径
        /// </summary>
        /// <returns></returns>
        private bool AopAllowed()
        {
            var allowedUrl = ConfigRead.GetInstance.GetAppsetConnection().allowed;
            if (allowedUrl == null || allowedUrl.Length == 0) return false;

            var url = httpContext.Path.Value;
            return allowedUrl.Any(item => url.IndexOf(item, StringComparison.Ordinal) > -1);
        }
        #endregion
    }
}