AspNetCore打造一個“最安全”的api接口
Authentication,Authorization
如果公司交給你一個任務讓你寫一個api接口,那么我們應該如何設計這個api接口來保證這個接口是對外看起來“高大上”,“羨慕崇拜”,并且使用起來和普通api接口無感,并且可以完美接入aspnetcore的認證授權體系呢,而不是自定義簽名來進行自定義過濾器實現(xiàn)呢(雖然也可以但是并不是最完美的),如何讓小白羨慕一眼就知道你是老鳥。
接下來我將給大家分享你不知道的自定義認證授體系。
我相信這可能是你面對aspnetcore下一個無論如何都要跨過去的坎,也是很多老鳥不熟悉的未知領域(很多人說能用就行,那么你可以直接右上角或者左上角)
如何打造一個最最最安全的api接口
技術選型
在不考慮性能的影響下我們選擇非對稱加密可以選擇sm或者rsa加密,這邊我們選擇rsa2048位pkcs8密鑰來進行,http傳輸可以分為兩個一個是request一個是response兩個交互模式。
安全的交互方式在不使用https的前提下那么就是我把明文信息加密并且簽名后給你,你收到后自己解密然后把你響應給我的明文信息加密后簽名在回給我,這樣就可以保證數(shù)據(jù)交互的安全性。
非對稱加密一般擁有兩個密鑰,一個被稱作為公鑰,一個被稱作為私鑰,公鑰是可以公開的哪怕放到互聯(lián)網(wǎng)上也是沒關系的,私鑰是自己保存的,一般而言永遠不會用到自己的私鑰。
私鑰簽名的結(jié)果只能被對應的公鑰校驗成功,公鑰加密的數(shù)據(jù)只能被對應的私鑰解密
實現(xiàn)原理
假設我們現(xiàn)在是兩個系統(tǒng)間的交互,系統(tǒng)A,系統(tǒng)B。系統(tǒng)A有一對rsa密鑰對我們稱之為公鑰APubKey,私鑰APriKey,系統(tǒng)B有一對rsa密鑰我們稱之為公鑰BPubKey,私鑰BPriKey。
私鑰是每個系統(tǒng)生成后自己內(nèi)部保存的,私鑰的作用就是告訴發(fā)送方收到的人一定是我,公鑰的作用就是告訴接收到是不是我發(fā)送的,基于這兩條定理我們來設計程序。
首先我們系統(tǒng)A調(diào)用系統(tǒng)B的Api1接口假設我們傳遞一個hello,然后系統(tǒng)B會回復一個world。那么我們?nèi)绾卧O計才可以保證安全呢。首先系統(tǒng)A發(fā)送消息如何讓系統(tǒng)B知道是系統(tǒng)A發(fā)過來的而不是別的中間人共計呢。這里我們需要用到簽名,就是說系統(tǒng)A用APriKey進行對hello的加密后那么如果發(fā)過去的數(shù)據(jù)如果簽名是x內(nèi)容是hello,系統(tǒng)B收到了就會對hello進行簽名的校驗,如果校驗出來的結(jié)果是用私鑰加密的那么你用哪個公鑰進行的前面校驗就可以保證系統(tǒng)是由哪個系統(tǒng)發(fā)送的。用APriKey進行簽名的數(shù)據(jù)只有用APubKey進行簽名校驗才能通過,所以系統(tǒng)B就可以確保是有系統(tǒng)A發(fā)送的而不是別的系統(tǒng),那么我們到現(xiàn)在還是傳送的明文信息,所以我們還需要將數(shù)據(jù)進行加密,加密一般我們選擇的是接收方的公鑰,因為只有用接收方的公鑰加密后才能由接收方的私鑰解密出來。

項目創(chuàng)建
首先我們創(chuàng)建一個簡單的aspnetcore的webapi項目

創(chuàng)建一個配置選項用來存儲私鑰公鑰
public?class?RsaOptions
{
????public?string?PrivateKey?{?get;?set;?}
}
創(chuàng)建一個Scheme選項類
public?class?AuthSecurityRsaOptions:?AuthenticationSchemeOptions
{
}
定義一個常量
public?class?AuthSecurityRsaDefaults
{
????public?const?string?AuthenticationScheme?=?"SecurityRsaAuth";
}
創(chuàng)建我們的認證處理器?AuthSecurityRsaAuthenticationHandler
public?class?AuthSecurityRsaAuthenticationHandler:?AuthenticationHandler<AuthSecurityRsaOptions>
{
//正式替換成redis
????private?readonly?ConcurrentDictionary<string,?object>?_repeatRequestMap?=
????????new?ConcurrentDictionary<string,?object>();
????public?AuthSecurityRsaAuthenticationHandler(IOptionsMonitor?options,?ILoggerFactory?logger,?UrlEncoder?encoder,?ISystemClock?clock )?:?base(options,?logger,?encoder,?clock)
????{
????}
????protected?override?async?Task?HandleAuthenticateAsync()
????{
????????try
????????{
????????????string?authorization?=?Request.Headers["AuthSecurity-Authorization"];
????????????//?If?no?authorization?header?found,?nothing?to?process?further
????????????if?(string.IsNullOrWhiteSpace(authorization))
????????????????return?AuthenticateResult.NoResult();
????????????var?authorizationSplit?=?authorization.Split('.');
????????????if?(authorizationSplit.Length?!=?4)
????????????????return?await?AuthenticateResultFailAsync("簽名參數(shù)不正確");
????????????var?reg?=?new?Regex(@"[0-9a-zA-Z]{1,40}");
????????????var?requestId?=?authorizationSplit[0];
????????????if?(string.IsNullOrWhiteSpace(requestId)?||?!reg.IsMatch(requestId))
????????????????return?await?AuthenticateResultFailAsync("請求Id不正確");
????????????var?appid?=?authorizationSplit[1];
????????????if?(string.IsNullOrWhiteSpace(appid)?||?!reg.IsMatch(appid))
????????????????return?await?AuthenticateResultFailAsync("應用Id不正確");
????????????var?timeStamp?=?authorizationSplit[2];
????????????if?(string.IsNullOrWhiteSpace(timeStamp)?||?!long.TryParse(timeStamp,?out?var?timestamp))
????????????????return?await?AuthenticateResultFailAsync("請求時間不正確");
????????????//請求時間大于30分鐘的就拋棄
????????????if?(Math.Abs(UtcTime.CurrentTimeMillis()?-?timestamp)?>?30?*?60?*?1000)
????????????????return?await?AuthenticateResultFailAsync("請求已過期");
????????????var?sign?=?authorizationSplit[3];
????????????if?(string.IsNullOrWhiteSpace(sign))
????????????????return?await?AuthenticateResultFailAsync("簽名參數(shù)不正確");
????????????//數(shù)據(jù)庫獲取
????????????//Request.HttpContext.RequestServices.GetService()
????????????var?app?=?AppCallerStorage.ApiCallers.FirstOrDefault(o=>o.Id==appid);
????????????if?(app?==?null)
????????????????return?AuthenticateResult.Fail("未找到對應的應用信息");
????????????//獲取請求體
????????????var?body?=?await?Request.RequestBodyAsync();
????????????//驗證簽名
????????????if?(!RsaFunc.ValidateSignature(app.AppPublickKey,?$"{requestId}{appid}{timeStamp}{body}",?sign))
????????????????return?await?AuthenticateResultFailAsync("簽名失敗");
????????????var?repeatKey?=?$"AuthSecurityRequestDistinct:{appid}:{requestId}";
????????????//自行替換成緩存或者redis本項目不帶刪除key功能沒有過期時間原則上需要設置1小時過期,前后30分鐘服務器時間差
????????????if?(_repeatRequestMap.ContainsKey(repeatKey)?||?!_repeatRequestMap.TryAdd(repeatKey,null))
????????????{
????????????????return?await?AuthenticateResultFailAsync("請勿重復提交");
????????????}
????????????//給Identity賦值
????????????var?identity?=?new?ClaimsIdentity(AuthSecurityRsaDefaults.AuthenticationScheme);
????????????identity.AddClaim(new?Claim("appid",?appid));
????????????identity.AddClaim(new?Claim("appname",?app.Name));
????????????identity.AddClaim(new?Claim("role",?"app"));
????????????//......
????????????var?principal?=?new?ClaimsPrincipal(identity);
????????????return?HandleRequestResult.Success(new?AuthenticationTicket(principal,?new?AuthenticationProperties(),?Scheme.Name));
????????}
????????catch?(Exception?ex)
????????{
????????????Logger.LogError(ex,?"RSA簽名失敗");
????????????return?await?AuthenticateResultFailAsync("認證失敗");
????????}
????}
????private?async?Task?AuthenticateResultFailAsync(string?message)
????{
????????Response.StatusCode?=?401;
????????await?Response.WriteAsync(message);
????????return?AuthenticateResult.Fail(message);
????}
}
第三步我們添加擴展方法
public?static?class?AuthSecurityRsaExtension
{
????public?static?AuthenticationBuilder?AddAuthSecurityRsa(this?AuthenticationBuilder?builder)
????????=>?builder.AddAuthSecurityRsa(AuthSecurityRsaDefaults.AuthenticationScheme,?_?=>?{?});
????public?static?AuthenticationBuilder?AddAuthSecurityRsa(this?AuthenticationBuilder?builder,?Action?configureOptions )
????????=>?builder.AddAuthSecurityRsa(AuthSecurityRsaDefaults.AuthenticationScheme,?configureOptions);
????public?static?AuthenticationBuilder?AddAuthSecurityRsa(this?AuthenticationBuilder?builder,?string?authenticationScheme,?Action?configureOptions )
????????=>?builder.AddAuthSecurityRsa(authenticationScheme,?displayName:?null,?configureOptions:?configureOptions);
????public?static?AuthenticationBuilder?AddAuthSecurityRsa(this?AuthenticationBuilder?builder,?string?authenticationScheme,?string?displayName,?Action?configureOptions )
????{
????????return?builder.AddScheme(authenticationScheme,?displayName,?configureOptions);
????}
}
添加返回結(jié)果加密解密 SafeResponseMiddleware
public?class?SafeResponseMiddleware
{
????private?readonly?RequestDelegate?_next;
????public?SafeResponseMiddleware(RequestDelegate?next)
????{
????????_next?=?next;
????}
????public?async?Task?Invoke(HttpContext?context)
????{
????????//AuthSecurity-Authorization
????????if?(?context.Request.Headers.TryGetValue("AuthSecurity-Authorization",?out?var?authorization)?&&?!string.IsNullOrWhiteSpace(authorization))
????????{
????????????//獲取Response.Body內(nèi)容
????????????var?originalBodyStream?=?context.Response.Body;
????????????await?using?(var?newResponse?=?new?MemoryStream())
????????????{
????????????????//替換response流
????????????????context.Response.Body?=?newResponse;
????????????????await?_next(context);
????????????????string?responseString?=?null;
????????????????var?identityIsAuthenticated?=?context.User?.Identity?.IsAuthenticated;
????????????????if?(identityIsAuthenticated.HasValue?&&?identityIsAuthenticated.Value)
????????????????{
????????????????????var?authorizationSplit?=?authorization.ToString().Split('.');
????????????????????var?requestId?=?authorizationSplit[0];
????????????????????var?appid?=?authorizationSplit[1];
????????????????????using?(var?reader?=?new?StreamReader(newResponse))
????????????????????{
????????????????????????newResponse.Position?=?0;
????????????????????????responseString?=?(await?reader.ReadToEndAsync())??string.Empty;
????????????????????????????var?responseStr?=?JsonConvert.SerializeObject(responseString);
????????????????????????????var?app?=?AppCallerStorage.ApiCallers.FirstOrDefault(o?=>?o.Id?==?appid);
????????????????????????????var?encryptBody?=?RsaFunc.Encrypt(app.AppPublickKey,?responseStr);
????????????????????????????var?signature?=?RsaFunc.CreateSignature(app.MyPrivateKey,?$"{requestId}{appid}{encryptBody}");
????????????????????????????context.Response.Headers.Add("AuthSecurity-Signature",?signature);
????????????????????????????responseString?=?encryptBody;
????????????????????}
????????????????????await?using?(var?writer?=?new?StreamWriter(originalBodyStream))
????????????????????{
????????????????????????await?writer.WriteAsync(responseString);
????????????????????????await?writer.FlushAsync();
????????????????????}
????????????????}
????????????}
????????}
????????else
????????{
????????????await?_next(context);
????????}
????}
}
新增基礎基類來實現(xiàn)認證
[Authorize(AuthenticationSchemes?=AuthSecurityRsaDefaults.AuthenticationScheme?)]
public?class?RsaBaseController?:?ControllerBase
{
}
到這個時候我們的接口已經(jīng)差不多寫完了,只是適配了微軟的框架,但是還是不能happy coding,接下來我們要實現(xiàn)模型的解析和校驗
模型解析
首先我們要確保微軟是如何通過request body的字符串到model的綁定的,通過源碼解析我們可以發(fā)現(xiàn)aspnetcore是通過IModelBinder。
首先實現(xiàn)模型綁定
public?class?EncryptBodyModelBinder?:?IModelBinder
{
????public?async?Task?BindModelAsync(ModelBindingContext?bindingContext)
????{
????????var?httpContext?=?bindingContext.HttpContext;
????????//if?(bindingContext.ModelType?!=?typeof(string))
????????//????return;
????????string?authorization?=?httpContext.Request.Headers["AuthSecurity-Authorization"];
????????if?(!string.IsNullOrWhiteSpace(authorization))
????????{
????????????//有參數(shù)接收就反序列化并且進行校驗
????????????if?(bindingContext.ModelType?!=?null)
????????????{
????????????????//獲取請求體
????????????????var?encryptBody?=?await?httpContext.Request.RequestBodyAsync();
????????????????if?(string.IsNullOrWhiteSpace(encryptBody))
????????????????????return;
????????????????//解密
????????????????var?rsaOptions?=?httpContext.RequestServices.GetService();
????????????????var?body?=?RsaFunc.Decrypt(rsaOptions.PrivateKey,?encryptBody);
????????????????var?request?=?JsonConvert.DeserializeObject(body,?bindingContext.ModelType);
????????????????if?(request?==?null)
????????????????{
????????????????????return;
????????????????}
????????????????bindingContext.Result?=?ModelBindingResult.Success(request);
????????????}
????????}
????}
}
添加attribute的特性解析
[AttributeUsage(AttributeTargets.Class?|?AttributeTargets.Struct?|?AttributeTargets.Enum?|?AttributeTargets.Property?|?AttributeTargets.Parameter,?AllowMultiple?=?false,?Inherited?=?true)]
public?class?RsaModelParseAttribute?:?Attribute,?IBinderTypeProviderMetadata,?IBindingSourceMetadata,?IModelNameProvider
{
????private?readonly?ModelBinderAttribute?modelBinderAttribute?=?new?ModelBinderAttribute()?{?BinderType?=?typeof(EncryptBodyModelBinder)?};
????public?BindingSource?BindingSource?=>?modelBinderAttribute.BindingSource;
????public?string?Name?=>?modelBinderAttribute.Name;
????public?Type?BinderType?=>?modelBinderAttribute.BinderType;
}
添加測試dto
[RsaModelParse]
public?class?TestModel
{
????[Display(Name?=?"id"),Required(ErrorMessage?=?"{0}不能為空")]
????public?string?Id?{?get;?set;?}
}
創(chuàng)建模型控制器
[Route("api/[controller]/[action]")]
[ApiController]
public?class?TestController:?RsaBaseController
{
????[AllowAnonymous]
????public?IActionResult?Test()
????{
????????return?Ok();
????}
//正常測試
????public?IActionResult?Test1()
????{
????????var?appid?=?Request.HttpContext.User.Claims.FirstOrDefault(o=>o.Type==?"appid").Value;
????????var?appname?=?Request.HttpContext.User.Claims.FirstOrDefault(o=>o.Type==?"appname").Value;
????????return?Ok($"appid:{appid},appname:{appname}");
????}
///模型校驗
????public?IActionResult?Test2(TestModel?request)
????{
????????return?Ok(JsonConvert.SerializeObject(request));
????}
//異常錯誤校驗
????public?IActionResult?Test3(TestModel?request)
????{
????????var?x?=?0;
????????var?a?=?1?/?x;
????????return?Ok("ok");
????}
}
添加異常全局捕獲
public?class?HttpGlobalExceptionFilter?:?IExceptionFilter
{
????private?readonly?ILogger?_logger;
????public?HttpGlobalExceptionFilter(ILogger?logger )
????{
????????_logger?=?logger;
????}
????public?void?OnException(ExceptionContext?context)
????{
????????_logger.LogError(new?EventId(context.Exception.HResult),
????????????context.Exception,
????????????context.Exception.Message);
????????context.Result?=?new?OkObjectResult("未知異常");
????????context.HttpContext.Response.StatusCode?=?(int)HttpStatusCode.OK;
????????context.ExceptionHandled?=?true;
????}
}
添加模型校驗
public?class?ValidateModelStateFilter?:?ActionFilterAttribute
{
????public?override?void?OnActionExecuting(ActionExecutingContext?context)
????{
????????if?(context.ModelState.IsValid)
????????{
????????????return;
????????}
????????var?validationErrors?=?context.ModelState
????????????.Keys
????????????.SelectMany(k?=>?context.ModelState[k].Errors)
????????????.Select(e?=>?e.ErrorMessage)
????????????.ToArray();
????????context.Result?=?new?OkObjectResult(string.Join(",",?validationErrors));
????????context.HttpContext.Response.StatusCode?=?(int)HttpStatusCode.OK;
????}
}
startup配置
public?void?ConfigureServices(IServiceCollection?services)
{
????services.Configure(options?=>
????{
????????//忽略系統(tǒng)自帶校驗你[ApiController]?
????????options.SuppressModelStateInvalidFilter?=?true;
????});
????services.AddControllers(options?=>
????{
????????options.Filters.Add();
????????options.Filters.Add();
????});
????services.AddControllers();
????services.AddAuthentication().AddAuthSecurityRsa();
????????services.AddSingleton(sp?=>
????????{
????????????return?new?RsaOptions()
????????????{
????????????????PrivateKey?=?Configuration.GetSection("RsaConfig")["PrivateKey"],
????????????};
????????});
}
public?void?Configure(IApplicationBuilder?app,?IWebHostEnvironment?env)
{
????if?(env.IsDevelopment())
????{
????????app.UseDeveloperExceptionPage();
????}
????app.UseMiddleware();
????app.UseRouting();
????app.UseAuthentication();
????app.UseAuthorization();
????app.UseEndpoints(endpoints?=>
????{
????????endpoints.MapControllers();
????});
}
到此為止我們服務端的所有api接口和配置都已經(jīng)完成了接下來我們通過編寫客戶端接口和生成rsa密鑰對就可以開始使用api了
如何生成rsa秘鑰首先我們下載openssl
下載地址openssl

雙擊

輸入創(chuàng)建命令
打開bin下openssl.exe
生成RSA私鑰
openssl>genrsa?-out?rsa_private_key.pem?2048
生成RSA公鑰
openssl>rsa?-in?rsa_private_key.pem?-pubout?-out?rsa_public_key.pem
將RSA私鑰轉(zhuǎn)換成PKCS8格式
openssl>pkcs8?-topk8?-inform?PEM?-in?rsa_private_key.pem?-outform?PEM?-nocrypt?-out?rsa_pkcs8_private_key.pem

公鑰和私鑰不是xml格式的C#使用rsa需要xml格式的秘鑰,所以先轉(zhuǎn)換對應的秘鑰
首先nuget下載公鑰私鑰轉(zhuǎn)換工具
Install-Package?BouncyCastle.NetCore?-Version?1.8.8
public?class?RsaKeyConvert
{
????private?RsaKeyConvert()
????{
????}
????public?static?string?RsaPrivateKeyJava2DotNet(string?privateKey)
????{
????????RsaPrivateCrtKeyParameters?privateKeyParam?=?(RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(TrimPrivatePrefixSuffix(privateKey)));
????????return?string.Format("{0} {1} {2}
{3}
{4} {5} {6} {7} ",
????????????Convert.ToBase64String(privateKeyParam.Modulus.ToByteArrayUnsigned()),
????????????Convert.ToBase64String(privateKeyParam.PublicExponent.ToByteArrayUnsigned()),
????????????Convert.ToBase64String(privateKeyParam.P.ToByteArrayUnsigned()),
????????????Convert.ToBase64String(privateKeyParam.Q.ToByteArrayUnsigned()),
????????????Convert.ToBase64String(privateKeyParam.DP.ToByteArrayUnsigned()),
????????????Convert.ToBase64String(privateKeyParam.DQ.ToByteArrayUnsigned()),
????????????Convert.ToBase64String(privateKeyParam.QInv.ToByteArrayUnsigned()),
????????????Convert.ToBase64String(privateKeyParam.Exponent.ToByteArrayUnsigned()));
????}
????public?static?string?RsaPrivateKeyDotNet2Java(string?privateKey)
????{
????????XmlDocument?doc?=?new?XmlDocument();
????????doc.LoadXml(TrimPrivatePrefixSuffix(privateKey));
????????BigInteger?m?=?new?BigInteger(1,?Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Modulus")[0].InnerText));
????????BigInteger?exp?=?new?BigInteger(1,?Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Exponent")[0].InnerText));
????????BigInteger?d?=?new?BigInteger(1,?Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("D")[0].InnerText));
????????BigInteger?p?=?new?BigInteger(1,?Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("P")[0].InnerText));
????????BigInteger?q?=?new?BigInteger(1,?Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Q")[0].InnerText));
????????BigInteger?dp?=?new?BigInteger(1,?Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("DP")[0].InnerText));
????????BigInteger?dq?=?new?BigInteger(1,?Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("DQ")[0].InnerText));
????????BigInteger?qinv?=?new?BigInteger(1,?Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("InverseQ")[0].InnerText));
????????RsaPrivateCrtKeyParameters?privateKeyParam?=?new?RsaPrivateCrtKeyParameters(m,?exp,?d,?p,?q,?dp,?dq,?qinv);
????????PrivateKeyInfo?privateKeyInfo?=?PrivateKeyInfoFactory.CreatePrivateKeyInfo(privateKeyParam);
????????byte[]?serializedPrivateBytes?=?privateKeyInfo.ToAsn1Object().GetEncoded();
????????return?Convert.ToBase64String(serializedPrivateBytes);
????}
????public?static?string?RsaPublicKeyJava2DotNet(string?publicKey)
????{
????????RsaKeyParameters?publicKeyParam?=?(RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(TrimPublicPrefixSuffix(publicKey)));
????????return?string.Format("{0} {1} ",
????????????Convert.ToBase64String(publicKeyParam.Modulus.ToByteArrayUnsigned()),
????????????Convert.ToBase64String(publicKeyParam.Exponent.ToByteArrayUnsigned()));
????}
????public?static?string?RsaPublicKeyDotNet2Java(string?publicKey)
????{
????????XmlDocument?doc?=?new?XmlDocument();
????????doc.LoadXml(TrimPublicPrefixSuffix(publicKey));
????????BigInteger?m?=?new?BigInteger(1,?Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Modulus")[0].InnerText));
????????BigInteger?p?=?new?BigInteger(1,?Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Exponent")[0].InnerText));
????????RsaKeyParameters?pub?=?new?RsaKeyParameters(false,?m,?p);
????????SubjectPublicKeyInfo?publicKeyInfo?=?SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pub);
????????byte[]?serializedPublicBytes?=?publicKeyInfo.ToAsn1Object().GetDerEncoded();
????????return?Convert.ToBase64String(serializedPublicBytes);
????}
????public?static?string?TrimPublicPrefixSuffix(string?publicKey)
????{
????????return?publicKey
????????????.Replace("-----BEGIN?PUBLIC?KEY-----",?string.Empty)
????????????.Replace("-----END?PUBLIC?KEY-----",?string.Empty)
????????????.Replace("\r\n",?"");
????}
????public?static?string?TrimPrivatePrefixSuffix(string?privateKey)
????{
????????return?privateKey
????????????.Replace("-----BEGIN?PRIVATE?KEY-----",?string.Empty)
????????????.Replace("-----END?PRIVATE?KEY-----",?string.Empty)
????????????.Replace("\r\n",?"");
????}
}
編寫好client后開始調(diào)用


依次啟動兩個項目就可以看到我們調(diào)用成功了,
本項目采用rsa雙向簽名和加密來接入aspnetcore的權限系統(tǒng)并且可以獲取到系統(tǒng)調(diào)用方用戶
完美接入aspnetcore認證系統(tǒng)和權限系統(tǒng)(后續(xù)會出一篇如何設計權限)
系統(tǒng)交互采用雙向加密和簽名認證
完美接入模型校驗
完美處理響應結(jié)果
注意本項目僅僅只是是一個學習demo,而且根據(jù)實踐得出的結(jié)論rsa加密僅僅是滿足了最最最安全的api這個條件,但是性能上而言會隨著body的變大性能急劇下降,所以并不是一個很好的抉擇當然可以用在雙方交互的時候設置秘鑰提供api接口,實際情況下可以選擇使用對稱加密比如:AES或者DES進行body體的加密解密,但是在簽名方面完全沒問題可以選擇rsa,本次使用的是rsa2(rsa 2048位的秘鑰)秘鑰位數(shù)越大加密等級越高但是解密性能越低
當然你可以直接上https,本文章也不是說一定要雙向處理更多的是分享如何接入aspnetcore的認證體系中和模型校驗,而不用帖一大堆的attribute
demo:AspNetCoreSafeApi
最后
分享本人開發(fā)的efcore分表分庫讀寫分離組件,希望為.net生態(tài)做一份共享,如果喜歡或者覺得有用請點下star或者贊讓更多的人看到
Gitee Star 助力dotnet 生態(tài) Github Star
作者:博客園【薛家明】
原文鏈接:https://www.cnblogs.com/xuejiaming/p/15384015.html
QQ群:771630778
個人QQ:326308290(歡迎技術支持提供您寶貴的意見)
個人郵箱:[email protected]
