diff --git a/.gitignore b/.gitignore index 6efe9d9..8e920e0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Created by .ignore support plugin (hsz.mobi) ### C template # Prerequisites +*.sln: *.d # Object files diff --git a/Communications/Base.cs b/Communications/Base.cs index f67da37..c1d7f6d 100644 --- a/Communications/Base.cs +++ b/Communications/Base.cs @@ -25,6 +25,7 @@ namespace YYApi.Communications private readonly Dictionary MsgBox = new Dictionary() { {200, "成功"}, + {204, "没有数据"}, {400, "参数错误"}, {401, "需要登录"}, {403, "无权访问"}, @@ -100,7 +101,25 @@ namespace YYApi.Communications } /// - /// 动态内容 + /// 动态内容分页 + /// + /// + public class BasePageResponse : BaseResponse + { + /// + /// 动态内容列表 + /// + public T List { get; set; } + //当前页码 + public int Page { get; set; } + //每页条数 + public int Count { get; set; } + //总计条数 + public int Total { get; set; } + } + + /// + /// 动态Data返回 /// /// public class BaseResponse : BaseResponse @@ -109,11 +128,5 @@ namespace YYApi.Communications /// 动态内容列表 /// public T Data { get; set; } - //当前页码 - public int Page { get; set; } - //每页条数 - public int Count { get; set; } - //总计条数 - public int Total { get; set; } } } \ No newline at end of file diff --git a/Controllers/BaseController.cs b/Controllers/BaseController.cs index de0467f..7c8284d 100644 --- a/Controllers/BaseController.cs +++ b/Controllers/BaseController.cs @@ -65,6 +65,17 @@ namespace YYApi.Controllers } } + /// + /// 检测给定的变量是否为NUll + /// + /// 检测条件 + /// 错误代码 + /// 错误描述 + protected static void ErrorWhenNull(object condition, int code, string message = null) + { + ErrorWhen(condition == null, code, message); + } + /// /// 错误回馈页面 /// @@ -78,5 +89,10 @@ namespace YYApi.Controllers response.SetCode(code); return response; } + + protected BaseResponse UploadFile() + { + return new BaseResponse(); + } } } \ No newline at end of file diff --git a/Extensions.cs b/Extensions.cs index 52f8467..ebaf2f2 100644 --- a/Extensions.cs +++ b/Extensions.cs @@ -47,7 +47,7 @@ namespace YYApi { return builder.AddSwaggerGen(x => { - x.SwaggerDoc("api", new OpenApiInfo {Title = title, Description = description}); + x.SwaggerDoc("api", new OpenApiInfo { Title = title, Description = description }); x.EnableAnnotations(); x.AddSecurityRequirement(new OpenApiSecurityRequirement { @@ -60,10 +60,11 @@ namespace YYApi } }); x.AddSecurityDefinition("HeaderToken", - new OpenApiSecurityScheme {Name = "Token", In = ParameterLocation.Header}); + new OpenApiSecurityScheme { Name = "Token", In = ParameterLocation.Header }); }); } + /// /// 使用异常中间价扩展 /// diff --git a/Helpers/AuthMiddleware.cs b/Helpers/AuthMiddleware.cs index c6c9d5f..285cf6e 100644 --- a/Helpers/AuthMiddleware.cs +++ b/Helpers/AuthMiddleware.cs @@ -65,6 +65,12 @@ namespace YYApi.Helpers Types = new[] { "user" }; } + //只检测一种用户类型的快捷方式 + public CheckAuthAttribute(string type) + { + Types = new[] { type }; + } + //设定特定类型的检测 public CheckAuthAttribute(string[] types) { diff --git a/JobService/HFDashboardAuth.cs b/JobService/HFDashboardAuth.cs new file mode 100644 index 0000000..f8e1719 --- /dev/null +++ b/JobService/HFDashboardAuth.cs @@ -0,0 +1,124 @@ +using System; +using Hangfire.Dashboard; +using Microsoft.Extensions.Configuration; +using Microsoft.AspNetCore.Http; +using StackExchange.Redis; +using System.Collections.Generic; +using System.Text; +using System.Security.Cryptography; + +namespace YYApi.JobService +{ + /// + /// Redis缓存Key定义 + /// + public struct CacheKey + { + public const string HangfireDashboardAuthPrefix = "{hangfire}:dashboard:auth:"; + } + + /// + /// HangFire Dashboard Digest认证. + /// + public class HFDashboardAuth : IDashboardAuthorizationFilter + { + private IConfiguration _config { get; } + private IDatabase _redis { get; } + public HFDashboardAuth(IConfiguration config, IDatabase redis) + { + _redis = redis; + _config = config; + } + + public bool Authorize(DashboardContext context) + { + var http = context.GetHttpContext(); + if (http.Request.Headers.ContainsKey("Authorization")) + { + var authObj = _processAuthHeader(http.Request.Headers["Authorization"].ToString()); + if (http.Request.QueryString.ToString().Contains("logout")) + { + _redis.KeyDelete(CacheKey.HangfireDashboardAuthPrefix + authObj["opaque"]); + _redirect(http); + return true; + } + if (authObj["username"] == _config["Hangfire:User"]) + { + var a1 = _md5(string.Format("{0}:Need Login:{1}", authObj["username"], _config["Hangfire:Pass"])); + var a2 = _md5(string.Format("{0}:{1}", http.Request.Method, authObj["uri"])); + var nonce = _redis.StringGet(CacheKey.HangfireDashboardAuthPrefix + authObj["opaque"]); + var validCode = _md5(string.Format("{0}:{1}:{2}:{3}:{4}:{5}", a1, nonce, authObj["nc"], authObj["cnonce"], authObj["qop"], a2)); + if (authObj["response"] == validCode) + { + _redis.StringSet(CacheKey.HangfireDashboardAuthPrefix + authObj["opaque"], nonce, new TimeSpan(0, 5, 0)); + return true; + } + } + } + _challenge(http); + return false; + } + + /// + /// 生成401认证header + /// + /// The challenge. + private void _challenge(HttpContext http) + { + var response = http.Response; + var guid = Guid.NewGuid().ToString(); + var opaque = _md5(guid); + _redis.StringSet(CacheKey.HangfireDashboardAuthPrefix + opaque, guid, new TimeSpan(0, 0, 30)); + response.StatusCode = 401; + response.Headers.Add("WWW-Authenticate", string.Format("Digest realm=\"Need Login\",qop=\"auth\",nonce=\"{0}\",opaque=\"{1}\"", guid, opaque)); + } + + /// + /// 跳转到页面不附加参数 + /// + /// Http. + private void _redirect(HttpContext http) + { + var response = http.Response; + response.StatusCode = 302; + response.Headers.Add("Location", (http.Request.PathBase + http.Request.Path).ToString()); + } + + /// + /// 处理DigestHeader中的参数为字典 + /// + /// The auth header. + /// Auth data. + private Dictionary _processAuthHeader(string authData) + { + var authDataArray = authData.Replace("Digest ", string.Empty).Replace("\"", string.Empty).Split(", "); + var authDic = new Dictionary(); + foreach (var item in authDataArray) + { + var tmp = item.Split("=", 2); + authDic.Add(tmp[0], tmp[1]); + } + return authDic; + } + + /// + /// 计算字符串32位MD5 + /// + /// The md5. + /// Source. + private string _md5(string source) + { + byte[] sor = Encoding.UTF8.GetBytes(source); + var md5 = MD5.Create(); + byte[] result = md5.ComputeHash(sor); + StringBuilder strbul = new StringBuilder(40); + for (int i = 0; i < result.Length; i++) + { + strbul.Append(result[i].ToString("x2"));//加密结果"x2"结果为32位,"x3"结果为48位,"x4"结果为64位 + + } + return strbul.ToString(); + + } + } +} diff --git a/JobService/JobDashboardConfig.cs b/JobService/JobDashboardConfig.cs new file mode 100644 index 0000000..86e7ea3 --- /dev/null +++ b/JobService/JobDashboardConfig.cs @@ -0,0 +1,10 @@ +using System; +namespace YYApi.JobService +{ + public class JobDashboardConfig + { + public string Path { get; set; } + public string Username { get; set; } + public string Password { get; set; } + } +} diff --git a/JobService/JobStorage.cs b/JobService/JobStorage.cs new file mode 100644 index 0000000..4728bc2 --- /dev/null +++ b/JobService/JobStorage.cs @@ -0,0 +1,10 @@ +using System; +namespace YYApi.JobService +{ + public class JobStorage + { + public JobStorage() + { + } + } +} diff --git a/YYApi.csproj b/YYApi.csproj index e48dd10..a8421ea 100644 --- a/YYApi.csproj +++ b/YYApi.csproj @@ -4,7 +4,7 @@ netcoreapp3.1 Library true - 0.1.6 + 0.2.1 xRain@YYTech AspNetCore一个方便的API文档,捕获异常,统一输入输出的API类库 YY-Tech.YYApi @@ -17,10 +17,15 @@ + - - + + + + + +