ASP.NET Core中怎么使用自定義驗(yàn)證屬性控制訪問權(quán)限?針對這個(gè)問題,這篇文章詳細(xì)介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡單易行的方法。
一、public class Startup的配置:
//啟用跨域訪問(不同端口也是跨域) services.AddCors(options => { options.AddPolicy("AllowOriginOtherBis", builder => builder.WithOrigins("https://1.16.9.12:4432", "https://pc12.ato.biz:4432", "https://localhost:44384", "https://1.16.9.12:4432", "https://pc12.ato.biz:4432").AllowAnyMethod().AllowAnyHeader()); }); //啟用自定義屬性以便對控制器或Action進(jìn)行[TerminalApp()]定義。 services.AddSingleton<IAuthorizationHandler, TerminalAppAuthorizationHandler>(); services.AddAuthorization(options => { options.AddPolicy("TerminalApp", policyBuilder => { policyBuilder.Requirements.Add(new TerminalAppAuthorizationRequirement()); }); });
二、public void Configure(IApplicationBuilder app, IHostingEnvironment env)中的配置:
app.UseHttpsRedirection(); //使用Https傳輸 app.UseCors("AllowOriginOtherBis"); //根據(jù)定義啟用跨域設(shè)置
三、示例WebApi項(xiàng)目結(jié)構(gòu):
四、主要代碼(我采用的從數(shù)據(jù)庫進(jìn)行驗(yàn)證):
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] internal class TerminalAppAttribute : AuthorizeAttribute { public string AppID { get; } /// <summary> /// 指定客戶端訪問API /// </summary> /// <param name="appID"></param> public TerminalAppAttribute(string appID="") : base("TerminalApp") { AppID = appID; } }
public abstract class AttributeAuthorizationHandler<TRequirement, TAttribute> : AuthorizationHandler<TRequirement> where TRequirement : IAuthorizationRequirement where TAttribute : Attribute { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement) { var attributes = new List<TAttribute>(); if ((context.Resource as AuthorizationFilterContext)?.ActionDescriptor is ControllerActionDescriptor action) { attributes.AddRange(GetAttributes(action.ControllerTypeInfo.UnderlyingSystemType)); attributes.AddRange(GetAttributes(action.MethodInfo)); } return HandleRequirementAsync(context, requirement, attributes); } protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement, IEnumerable<TAttribute> attributes); private static IEnumerable<TAttribute> GetAttributes(MemberInfo memberInfo) { return memberInfo.GetCustomAttributes(typeof(TAttribute), false).Cast<TAttribute>(); } } internal class TerminalAppAuthorizationHandler : AttributeAuthorizationHandler<TerminalAppAuthorizationRequirement,TerminalAppAttribute> { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TerminalAppAuthorizationRequirement requirement, IEnumerable<TerminalAppAttribute> attributes) { object errorMsg = string.Empty; //如果取不到身份驗(yàn)證信息,并且不允許匿名訪問,則返回未驗(yàn)證403 if (context.Resource is AuthorizationFilterContext filterContext && filterContext.ActionDescriptor is ControllerActionDescriptor descriptor) { //先判斷是否是匿名訪問, if (descriptor != null) { var actionAttributes = descriptor.MethodInfo.GetCustomAttributes(inherit: true); bool isAnonymous = actionAttributes.Any(a => a is AllowAnonymousAttribute); //非匿名的方法,鏈接中添加accesstoken值 if (isAnonymous) { context.Succeed(requirement); return Task.CompletedTask; } else { //url獲取access_token //從AuthorizationHandlerContext轉(zhuǎn)成HttpContext,以便取出表求信息 var httpContext = (context.Resource as AuthorizationFilterContext).HttpContext; //var questUrl = httpContext.Request.Path.Value.ToLower(); string requestAppID = httpContext.Request.Headers["appid"]; string requestAccessToken = httpContext.Request.Headers["access_token"]; if ((!string.IsNullOrEmpty(requestAppID)) && (!string.IsNullOrEmpty(requestAccessToken))) { if (attributes != null) { //當(dāng)不指定具體的客戶端AppID僅運(yùn)用驗(yàn)證屬性時(shí)默認(rèn)所有客戶端都接受 if (attributes.ToArray().ToString()=="") { //任意一個(gè)在數(shù)據(jù)庫列表中的App都可以運(yùn)行,否則先判斷提交的APPID與需要ID是否相符 bool mat = false; foreach (var terminalAppAttribute in attributes) { if (terminalAppAttribute.AppID == requestAppID) { mat = true; break; } } if (!mat) { errorMsg = ReturnStd.NotAuthorize("客戶端應(yīng)用未在服務(wù)端登記或未被授權(quán)運(yùn)用當(dāng)前功能."); return HandleBlockedAsync(context, requirement, errorMsg); } } } //如果未指定attributes,則表示任何一個(gè)終端服務(wù)都可以調(diào)用服務(wù), 在驗(yàn)證區(qū)域驗(yàn)證終端提供的ID是否匹配數(shù)據(jù)庫記錄 string valRst = ValidateToken(requestAppID, requestAccessToken); if (string.IsNullOrEmpty(valRst)) { context.Succeed(requirement); return Task.CompletedTask; } else { errorMsg = ReturnStd.NotAuthorize("AccessToken驗(yàn)證失敗(" + valRst + ")","91"); return HandleBlockedAsync(context, requirement, errorMsg); } } else { errorMsg = ReturnStd.NotAuthorize("未提供AppID或Token."); return HandleBlockedAsync(context, requirement, errorMsg); //return Task.CompletedTask; } } } } else { errorMsg = ReturnStd.NotAuthorize("FilterContext類型不匹配."); return HandleBlockedAsync(context, requirement, errorMsg); } errorMsg = ReturnStd.NotAuthorize("未知錯(cuò)誤."); return HandleBlockedAsync(context,requirement, errorMsg); } //校驗(yàn)票據(jù)(數(shù)據(jù)庫數(shù)據(jù)匹配) /// <summary> /// 驗(yàn)證終端服務(wù)程序提供的AccessToken是否合法 /// </summary> /// <param name="appID">終端APP的ID</param> /// <param name="accessToken">終端APP利用其自身AppKEY運(yùn)算出來的AccessToken,與服務(wù)器生成的進(jìn)行比對</param> /// <returns></returns> private string ValidateToken(string appID,string accessToken) { try { DBContextMain dBContext = new DBContextMain(); string appKeyOnServer = string.Empty; //從數(shù)據(jù)庫讀取AppID對應(yīng)的KEY(此KEY為加解密算法的AES_KEY AuthApp authApp = dBContext.AuthApps.FirstOrDefault(a => a.AppID == appID); if (authApp == null) { return "客戶端應(yīng)用沒有在云端登記!"; } else { appKeyOnServer = authApp.APPKey; } if (string.IsNullOrEmpty(appKeyOnServer)) { return "客戶端應(yīng)用基礎(chǔ)信息有誤!"; } string tmpToken = string.Empty; tmpToken = System.Net.WebUtility.UrlDecode(accessToken);//解碼相應(yīng)的Token到原始字符(因其中可能會(huì)有+=等特殊字符,必須編碼后傳遞) tmpToken = OCrypto.AES16Decrypt(tmpToken, appKeyOnServer); //使用APPKEY解密并分析 if (string.IsNullOrEmpty(tmpToken)) { return "客戶端提交的身份令牌運(yùn)算為空!"; } else { try { //原始驗(yàn)證碼為im_cloud_sv001-appid-ticks格式 //取出時(shí)間,與服務(wù)器時(shí)間對比,超過10秒即拒絕服務(wù) long tmpTime =Convert.ToInt64(tmpToken.Substring(tmpToken.LastIndexOf("-")+1)); //DateTime dt = DateTime.ParseExact(tmpTime, "yyyyMMddHHmmss", CultureInfo.CurrentCulture); DateTime dt= new DateTime(tmpTime); bool IsInTimeSpan = (Convert.ToDouble(ODateTime.DateDiffSeconds(dt, DateTime.Now)) <= 7200); bool IsInternalApp = (tmpToken.IndexOf("im_cloud_sv001-") >= 0); if (!IsInternalApp || !IsInTimeSpan) { return "令牌未被許可或已經(jīng)失效!"; } else { return string.Empty; //成功驗(yàn)證 } } catch (Exception ex) { return "令牌解析出錯(cuò)(" + ex.Message + ")"; } } } catch (Exception ex) { return "令牌解析出錯(cuò)(" + ex.Message + ")"; } } private Task HandleBlockedAsync(AuthorizationHandlerContext context, TerminalAppAuthorizationRequirement requirement, object errorMsg) { var authorizationFilterContext = context.Resource as AuthorizationFilterContext; authorizationFilterContext.Result = new JsonResult(errorMsg) { StatusCode = 202 }; //設(shè)置為403會(huì)顯示不了自定義信息,改為Accepted202,由客戶端處理 context.Succeed(requirement); return Task.CompletedTask; } }
internal class TerminalAppAuthorizationRequirement : IAuthorizationRequirement { public TerminalAppAuthorizationRequirement() { } }
五、相應(yīng)的Token驗(yàn)證代碼:
[AutoValidateAntiforgeryToken] //在本控制器內(nèi)自動(dòng)啟用跨站攻擊防護(hù) [Route("api/get_accesstoken")] public class GetAccessTokenController : Controller { //尚未限制訪問頻率 //返回{"access_token":"ACCESS_TOKEN","expires_in":7200} 有效期2個(gè)小時(shí) //錯(cuò)誤時(shí)返回{"errcode":40013,"errmsg":"invalid appid"} [AllowAnonymous] public ActionResult<string> Get() { try { string tmpToken = string.Empty; string appID = HttpContext.Request.Headers["appid"]; string appKey = HttpContext.Request.Headers["appkey"]; if ((appID.Length < 5) || appKey.Length != 32) { return "{'errcode':10000,'errmsg':'appid或appkey未提供'}"; } //token采用im_cloud_sv001-appid-ticks數(shù)字 long timeTk = DateTime.Now.Ticks; //輸出毫微秒:633603924670937500 //DateTime dt = new DateTime(timeTk);//可以還原時(shí)間 string plToken = "im_cloud1-" + appID + "-" + timeTk; tmpToken = OCrypto.AES16Encrypt(plToken, appKey); //使用APPKEY加密 tmpToken = System.Net.WebUtility.UrlEncode(tmpToken); //編碼相應(yīng)的Token(因其中可能會(huì)有+=等特殊字符,必須編碼后傳遞) tmpToken = "{'access_token':'" + tmpToken + "','expires_in':7200}"; return tmpToken; } catch (Exception ex) { return "{'errcode':10001,'errmsg':'" + ex.Message +"'}"; } } } GetAccessTokenController.cs
六、這樣,在我們需要控制的地方加上[TerminalApp()] 即可,這樣所有授權(quán)的App都能訪問,當(dāng)然,也可以使用[TerminalApp(“app01”)]限定某一個(gè)ID為app01的應(yīng)用訪問。
[Area("SYS")] // 路由: api/sys/user [Produces("application/json")] [TerminalApp()] public class UserController : Controller { // }
七、一個(gè)CS客戶端通過Web API上傳數(shù)據(jù)調(diào)用示例:
string postURL = "http://sv12.ato.com/api/sys/user/postnew"; Dictionary<string, string> headerDic2 = new Dictionary<string, string> { { "appid", MainFramework.CloudAppID }, { "access_token", accessToken } }; string pushRst = OPWeb.Post(postURL, headerDic2, "POST", sYS_Users); if (string.IsNullOrEmpty(pushRst)) { MyMsg.Information("推送成功!"); } else { MyMsg.Information("推送失敗!", pushRst); }
string accessToken = MainFramework.CloudAccessToken; if (accessToken.IndexOf("ERROR:") >= 0) { MyMsg.Information("獲取Token出錯(cuò):" + accessToken); return; }
關(guān)于ASP.NET Core中怎么使用自定義驗(yàn)證屬性控制訪問權(quán)限問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識(shí)。
分享題目:ASP.NETCore中怎么使用自定義驗(yàn)證屬性控制訪問權(quán)限-創(chuàng)新互聯(lián)
網(wǎng)站網(wǎng)址:http://chinadenli.net/article44/doipee.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供外貿(mào)網(wǎng)站建設(shè)、網(wǎng)站營銷、電子商務(wù)、手機(jī)網(wǎng)站建設(shè)、動(dòng)態(tài)網(wǎng)站、移動(dòng)網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容