小试YARP
.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 =>
{
//不使用https
o.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;
}
};
});
//注入授权Handler
services.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
想要更快更方便的了解相关知识,可以关注微信公众号
****欢迎关注我的asp.net core系统课程****
《asp.net core精要讲解》 https://ke.qq.com/course/265696
《asp.net core 3.0》 https://ke.qq.com/course/437517
《asp.net core项目实战》 https://ke.qq.com/course/291868
《基于.net core微服务》 https://ke.qq.com/course/299524
《asp.net core精要讲解》 https://ke.qq.com/course/265696
《asp.net core 3.0》 https://ke.qq.com/course/437517
《asp.net core项目实战》 https://ke.qq.com/course/291868
《基于.net core微服务》 https://ke.qq.com/course/299524