.net core下,一个轻量组反向代理库,由微软发起。
做了一个简单的带验证的反向代理,应用结构如上图,一个验证服务,两个业务服务和一个YARP服务。
源码
https://github.com/axzxs2001/Asp.NetCoreExperiment/tree/master/Asp.NetCoreExperiment/YARP
YARP的Starup.cs如下,主要是用来添加YARP组件和添加权限组件部分。
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Extensions.Hosting;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Text;
using System;namespace YARPDemo01
{public class Startup{public IConfiguration Configuration { get; }public Startup(IConfiguration configuration){Configuration = configuration;}public void ConfigureServices(IServiceCollection services){AddAuth(services);services.AddReverseProxy().LoadFromConfig(Configuration.GetSection("ReverseProxy"));}public void Configure(IApplicationBuilder app, IWebHostEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseAuthentication();app.UseRouting();app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapReverseProxy();});}void AddAuth(IServiceCollection services){//读取配置文件var audienceConfig = Configuration.GetSection("Audience");var symmetricKeyAsBase64 = audienceConfig["Secret"];var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);var signingKey = new SymmetricSecurityKey(keyByteArray);var tokenValidationParameters = new TokenValidationParameters{ValidateIssuerSigningKey = true,IssuerSigningKey = signingKey,ValidateIssuer = true,ValidIssuer = audienceConfig["Issuer"],ValidateAudience = true,ValidAudience = audienceConfig["Audience"],ValidateLifetime = true,ClockSkew = TimeSpan.Zero,RequireExpirationTime = true,};var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);//这个集合模拟用户权限表,可从数据库中查询出来var permission = new List<Permission> {new Permission { Url="/webapi01/test1", Name="admin"},new Permission { Url="/webapi01/test3", Name="admin"},new Permission { Url="/webapi02/test2", Name="admin"},new Permission { Url="/webapi02/test4", Name="admin"},};//如果第三个参数,是ClaimTypes.Role,上面集合的每个元素的Name为角色名称,如果ClaimTypes.Name,即上面集合的每个元素的Name为用户名var permissionRequirement = new PermissionRequirement("/api/denied", permission,ClaimTypes.Role,audienceConfig["Issuer"],audienceConfig["Audience"],signingCredentials,expiration: TimeSpan.FromSeconds(1000000)//设置Token过期时间);services.AddAuthorization(options =>{options.AddPolicy("Permission", policy => policy.AddRequirements(permissionRequirement));}).AddAuthentication(options =>{options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;}).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, o =>{//不使用httpso.RequireHttpsMetadata = false;o.TokenValidationParameters = tokenValidationParameters;o.Events = new JwtBearerEvents{OnTokenValidated = context =>{if (context.Request.Path.Value.ToString() == "/api/logout"){var token = ((context as TokenValidatedContext).SecurityToken as JwtSecurityToken).RawData;}return Task.CompletedTask;}};});//注入授权Handlerservices.AddSingleton<IAuthorizationHandler, PermissionHandler>();services.AddSingleton(permissionRequirement);}}
}
YARP项目实现API聚合appsettings.json
{"urls": "https://*:6001;http://*:6000","Logging": {"LogLevel": {"Default": "Information","Microsoft": "Warning","Microsoft.Hosting.Lifetime": "Information"}},"AllowedHosts": "*","Audience": {"Secret": "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890","Issuer": "gsw","Audience": "everone"},//实现api聚合"ReverseProxy": {"Routes": [//业务服务webapi01{"RouteId": "webapi01","ClusterId": "webapi01_cluster","AuthorizationPolicy": "Permission","Match": {"Path": "/webapi01/{**catch-all}"}},//业务服务webapi02{"RouteId": "webapi02","ClusterId": "webapi02_cluster","AuthorizationPolicy": "Permission","Match": {"Path": "/webapi02/{**catch-all}"}},//验证服务{"RouteId": "authservice","ClusterId": "auth_cluster","Match": {"Path": "/auth/{**catch-all}"}}],"Clusters": {//业务服务webapi01"webapi01_cluster": {"Destinations": {"webapi01_cluster/destination": {"Address": "https://localhost:7001/"}}},//业务服务webapi02"webapi02_cluster": {"Destinations": {"webapi02_cluster/destination": {"Address": "https://localhost:8001/"}}},//验证服务"auth_cluster": {"Destinations": {"auth_cluster/destination": {"Address": "https://localhost:5001/"}}}}}
}
Auth项目实现登录签名部分
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
namespace AuthenticationAuthorization_Token
{public class JwtToken{/// <summary>/// 获取基于JWT的Token/// </summary>/// <param name="username"></param>/// <returns></returns>public static dynamic BuildJwtToken(Claim[] claims, PermissionRequirement permissionRequirement){var now = DateTime.UtcNow;var jwt = new JwtSecurityToken(issuer: permissionRequirement.Issuer,audience: permissionRequirement.Audience,claims: claims,notBefore: now,expires: now.Add(permissionRequirement.Expiration),signingCredentials: permissionRequirement.SigningCredentials);var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);var response = new{Status = true,access_token = encodedJwt,expires_in = permissionRequirement.Expiration.TotalMilliseconds,token_type = "Bearer"};return response;}}
}
看结果:
首先登录获取token,用户名gsw,密码111111
访问webapi01
访问webapi02