背景
在微服务架构下,一般都会按不同的业务或功能将整个系统切分成不同的独立子系统,再通过REST API或RPC进行通讯并相互调用,形成各个子系统之间的串联结构。在这里,我们将采用REST API的通讯方式。比如:
1、有一个“用户中心”独立子系统名为“Lezhima.UserHub”,是一个基于ASP.NET Core mvc 2.0的项目。
2、有一个处理用户订单的独立子系统名为“Lezhima.UserOrder”,是一个基于ASP.NET Core webp api 2.0的项目。
3、同时还有一个处理用户文件上传的独立子系统名为“Lezhima.UserUpload”,是一个基于ASP.NET Core webp api 2.0的项目。
业务关系如下:
用户成功登录后进入“Lezhima.UserHub”,在用户查看订单时通过前端Ajax调用“Lezhima.UserOrder”的web api接口,在用户上传图片是通过前端Ajax调用“Lezhima.UserUpload”的web api接口。
至此,我们了解了上面的业务关系后,心里一定产生出如下两个问题:
1、如何保障“Lezhima.UserOrder”与“Lezhima.UserUpload”两个独立系统内的web api接口安全,因为它们已经被暴露在了前端。
2、如何在“Lezhima.UserHub”站颁发Token。
那么,带着问题我们下面就结合ASP.NET Core 自带的Jwt技术来讨论具体的实现(也许聪明的你有更好的解决方法,请一定告知我,谢谢)。
Jwt 全名为:JSON Web Token,是一个很成熟的技术,园子里也有很多这方面的知识,我这里就不再重述了。
实现原理
“Lezhima.UserHub”站因为已经做了登录验证,我们暂且认为它是可信的,所以在前端Ajax请求“Lezhima.UserOrder”站的web api接口时先到自已后端去生成一个Token,并随之同本次跨站请求一块携带至“Lezhima.UserOrder”站,“Lezhima.UserOrder”站验证请求头中的Token是否合法,如合法则继续路由到具体方法中,否则结束请求。“Lezhima.UserUpload”站原理与“Lezhima.UserOrder”相同。
实现代码
Lezhima.UserHub颁发Token代码:
???????/// <summary> ???????/// 颁发一个指定有效期的Token,并将当前登录的用户id传递进来 ???????/// </summary> ???????/// <param name="currentUserId"></param> ???????/// <param name="expiresMinutes"></param> ???????/// <returns></returns> ?public static async Task<string> GetAccessToken(string currentUserId,int expiresMinutes=2) ???????{ ???????????return await Task.Run(() => ???????????{//约定私钥,下面三个参数可放到配置文件中 ???????????????var secret = "NGUzNmNlNzQtZThkZC00YjRh";//发行者 ???????????????var iss = "Andre";//接受者 ???????????????var aud = "Andre"; ???????????????if (string.IsNullOrEmpty(secret) || string.IsNullOrEmpty(iss) || string.IsNullOrEmpty(aud)) ???????????????????return ""; ???????????????if (string.IsNullOrEmpty(currentUserId)) ???????????????????currentUserId = Guid.NewGuid().ToString(); ???????????????var now = DateTime.UtcNow; ???????????????var claims = new Claim[] ???????????????{ ???????????????????new Claim(JwtRegisteredClaimNames.Sub, currentUserId), ???????????????????new Claim(JwtRegisteredClaimNames.Iat, now.ToUniversalTime().ToString(), ClaimValueTypes.Integer64) ???????????????}; ???????????????var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secret)); ???????????????var jwt = new JwtSecurityToken( ??????????????????????issuer: iss, ??????????????????????audience: aud, ??????????????????????claims: claims, ??????????????????????notBefore: now, ??????????????????????expires: now.Add(TimeSpan.FromMinutes(expiresMinutes)), ??????????????????????signingCredentials: new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256) ????????????????); ???????????????return new JwtSecurityTokenHandler().WriteToken(jwt); ???????????}); ??????????????????}
Lezhima.UserHub前端Ajax跨站请求代码:
//封装一个Ajax请求公共方法function GetWebDataByObject(url, requestMethon, paramter) { ???jQuery.support.cors = true; ???apiUrl = ‘http://127.0.0.1:8012/‘; ???var token = GetToken(); //调用本站内的Token颁发Web api接口 ????var result = []; ???$.ajax({ ???????type: requestMethon, ???????url: apiUrl + url, ???????data: paramter, ???????async: false, ???????beforeSend: function (xhr) {//将Token携带到请求头中 ???????????xhr.setRequestHeader("Authorization", "Bearer " + token); ???????}, ?????????success: function (data) { ???????????result = data; ???????}, ???????error: function (XMLHttpRequest, textStatus, errorThrown) { ???????????// 状态码 ???????????console.log(XMLHttpRequest.status); ???????????// 状态 ???????????console.log(XMLHttpRequest.readyState); ???????????// 错误信息 ??????????????console.log(textStatus); ???????} ???}); ???return result;}
“Lezhima.UserOrder”站开启Jwt的Token验证,在Startup.cs里添加如下代码:
???????public IServiceProvider ConfigureServices(IServiceCollection services) ???????{ ???????????????????????services.AddCors();//从配置文件中获取私钥、发行者、接受者三个参数//三个参数的值必需与颁发Token站相同 ???????????var audienceConfig = Configuration.GetSection("Audience"); ???????????var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(audienceConfig["Secret"])); ???????????var tokenValidationParameters = new TokenValidationParameters ???????????{ ???????????????ValidateIssuerSigningKey = true, ???????????????IssuerSigningKey = signingKey, ???????????????ValidateIssuer = true, ???????????????ValidIssuer = audienceConfig["Iss"], ???????????????ValidateAudience = true, ???????????????ValidAudience = audienceConfig["Aud"], ???????????????ValidateLifetime = true, ???????????????ClockSkew = TimeSpan.Zero, ???????????????RequireExpirationTime = true, ???????????};//注入Jwt验证 ???????????services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) ???????????????.AddJwtBearer(options => { ???????????????????options.RequireHttpsMetadata = false; ???????????????????options.TokenValidationParameters = tokenValidationParameters; ???????????????}); ???????????services.AddMvc(); ???????????var builder = new ContainerBuilder(); ???????????builder.RegisterModule(new Evolution()); ???????????builder.Populate(services); ???????????var container = builder.Build(); ???????????return container.Resolve<IServiceProvider>(); ???????}
???????public void Configure(IApplicationBuilder app, IHostingEnvironment env) ???????{ ???????????if (env.IsDevelopment()) ???????????{ ???????????????app.UseDeveloperExceptionPage(); ???????????} ???????????app.UseCors(builder => ?????????????builder.WithOrigins("*") ?????????????.AllowAnyHeader() ?????????????.AllowAnyMethod() ?????????????.AllowCredentials() ???????????); ????????????????????//开启验证 ???????????app.UseAuthentication(); ???????????app.UseMvc(); ???????}
“Lezhima.UserOrder”站内的控制器里添加验证过滤器[Authorize],如下代码:
???[Route("api/[Controller]")] ???//添加过滤器后,该控制器内所有Action都将进行Token验证 ???[Authorize] ???public class OrderController : Controller ???{ ???????????}
至此,基于ASP.NET Core的Jwt跨站验证Token方案就全部完成了,是不是很简单呀^_^ ^_^
ASP.NET Core 2.0利用Jwt实现授权认证
原文地址:https://www.cnblogs.com/Andre/p/9542863.html