init
This commit is contained in:
42
DouyinApi.Gateway/Controllers/UserController.cs
Normal file
42
DouyinApi.Gateway/Controllers/UserController.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using DouyinApi.Common.HttpContextUser;
|
||||
using DouyinApi.Model;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace DouyinApi.Gateway.Controllers
|
||||
{
|
||||
[Authorize(AuthenticationSchemes = Permissions.GWName)]
|
||||
[Route("/gateway/[controller]/[action]")]
|
||||
public class UserController : ControllerBase
|
||||
{
|
||||
private readonly IUser _user;
|
||||
|
||||
public UserController(IUser user)
|
||||
{
|
||||
_user = user;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public MessageModel<List<ClaimDto>> MyClaims()
|
||||
{
|
||||
return new MessageModel<List<ClaimDto>>()
|
||||
{
|
||||
success = true,
|
||||
response = (_user.GetClaimsIdentity().ToList()).Select(d =>
|
||||
new ClaimDto
|
||||
{
|
||||
Type = d.Type,
|
||||
Value = d.Value
|
||||
}
|
||||
).ToList()
|
||||
};
|
||||
}
|
||||
}
|
||||
public class ClaimDto
|
||||
{
|
||||
public string Type { get; set; }
|
||||
public string Value { get; set; }
|
||||
}
|
||||
}
|
||||
37
DouyinApi.Gateway/DouyinApi.Gateway.csproj
Normal file
37
DouyinApi.Gateway/DouyinApi.Gateway.csproj
Normal file
@@ -0,0 +1,37 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile>..\DouyinApi.Gateway\DouyinApi.Gateway.xml</DocumentationFile>
|
||||
<NoWarn>1701;1702;1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Extensions\ApiResponseHandler.cs" />
|
||||
<Compile Remove="Helper\HeaderDelegatingHandler.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="index.html" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="index.html" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.10" NoWarn="NU1605" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.10" NoWarn="NU1605" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="8.0.10" />
|
||||
<PackageReference Include="Ocelot" Version="20.0.0" />
|
||||
<PackageReference Include="Ocelot.Provider.Consul" Version="20.0.0" />
|
||||
<PackageReference Include="Ocelot.Provider.Polly" Version="20.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DouyinApi.Extensions\DouyinApi.Extensions.csproj" />
|
||||
<ProjectReference Include="..\DouyinApi.Model\DouyinApi.Model.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
55
DouyinApi.Gateway/DouyinApi.Gateway.xml
Normal file
55
DouyinApi.Gateway/DouyinApi.Gateway.xml
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>DouyinApi.Gateway</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="T:DouyinApi.AuthHelper.CustomJwtTokenAuthMiddleware">
|
||||
<summary>
|
||||
中间件
|
||||
原做为自定义授权中间件
|
||||
先做检查 header token的使用
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:DouyinApi.AuthHelper.CustomJwtTokenAuthMiddleware.Schemes">
|
||||
<summary>
|
||||
验证方案提供对象
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:DouyinApi.AuthHelper.CustomJwtTokenAuthMiddleware._next">
|
||||
<summary>
|
||||
请求上下文
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:DouyinApi.AuthHelper.CustomJwtTokenAuthMiddleware.Invoke(Microsoft.AspNetCore.Http.HttpContext)">
|
||||
<summary>
|
||||
网关授权
|
||||
</summary>
|
||||
<param name="httpContext"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:DouyinApi.AuthHelper.CustomJwtTokenAuthMiddleware.SendResponse(Microsoft.AspNetCore.Http.HttpContext,System.String,System.Net.HttpStatusCode)">
|
||||
<summary>
|
||||
返回相应
|
||||
</summary>
|
||||
<param name="context"></param>
|
||||
<param name="message"></param>
|
||||
<param name="code"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:DouyinApi.AuthHelper.CustomJwtTokenAuthMiddleware.CheckWhiteList(System.String)">
|
||||
<summary>
|
||||
判断是否在白名单内,支持通配符 ****
|
||||
</summary>
|
||||
<param name="url"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:DouyinApi.AdminMvc.Startup.#ctor(Microsoft.Extensions.Configuration.IConfiguration,Microsoft.AspNetCore.Hosting.IWebHostEnvironment)">
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ 描 述:模拟一个网关项目
|
||||
│ 测 试:在网关swagger中查看具体的服务
|
||||
│ 作 者:anson zhang
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
77
DouyinApi.Gateway/Extensions/ApiResponseHandler.cs
Normal file
77
DouyinApi.Gateway/Extensions/ApiResponseHandler.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using DouyinApi.Model;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DouyinApi.Gateway.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 这里不需要,目前集成的是 DouyinApi.Extensions 下的接口处理器
|
||||
/// 但是你可以单独在网关中使用这个。
|
||||
/// </summary>
|
||||
public class ApiResponseHandler : DelegatingHandler
|
||||
{
|
||||
JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings()
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||
};
|
||||
|
||||
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await base.SendAsync(request, cancellationToken);
|
||||
var contentType = response.Content.Headers.ContentType?.MediaType ?? "";
|
||||
if (!contentType.Equals("application/json")) return response;
|
||||
|
||||
dynamic result = null;
|
||||
var resultStr = await response.Content.ReadAsStringAsync();
|
||||
try
|
||||
{
|
||||
result = JsonConvert.DeserializeObject<dynamic>(resultStr);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return response;
|
||||
}
|
||||
|
||||
if (result != null && result.code == 500) resultStr = result.msg.ToString();
|
||||
|
||||
var apiResponse = new ApiResponse(StatusCode.CODE200).MessageModel;
|
||||
if (response.StatusCode != HttpStatusCode.OK || result.code == (int)HttpStatusCode.InternalServerError)
|
||||
{
|
||||
var exception = new Exception(resultStr);
|
||||
apiResponse = new ApiResponse(StatusCode.CODE500).MessageModel;
|
||||
}
|
||||
else if (result.code == (int)HttpStatusCode.Unauthorized)
|
||||
{
|
||||
apiResponse = new ApiResponse(StatusCode.CODE401).MessageModel;
|
||||
|
||||
}
|
||||
else if (result.code == (int)HttpStatusCode.Forbidden)
|
||||
{
|
||||
apiResponse = new ApiResponse(StatusCode.CODE403).MessageModel;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
var statusCode = apiResponse.status == 500 ? HttpStatusCode.InternalServerError
|
||||
: apiResponse.status == 401 ? HttpStatusCode.Unauthorized
|
||||
: apiResponse.status == 403 ? HttpStatusCode.Forbidden
|
||||
: HttpStatusCode.OK;
|
||||
|
||||
response.StatusCode = statusCode;
|
||||
response.Content = new StringContent(JsonConvert.SerializeObject(apiResponse, jsonSerializerSettings), Encoding.UTF8, "application/json");
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
53
DouyinApi.Gateway/Extensions/CustomAuthenticationHandler.cs
Normal file
53
DouyinApi.Gateway/Extensions/CustomAuthenticationHandler.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DouyinApi.Gateway.Extensions
|
||||
{
|
||||
public class CustomAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
|
||||
{
|
||||
public CustomAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
|
||||
ILoggerFactory logger,
|
||||
UrlEncoder encoder,
|
||||
ISystemClock clock) : base(options, logger, encoder, clock)
|
||||
{
|
||||
}
|
||||
|
||||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
// 可以查询数据库等操作
|
||||
// 获取当前用户不能放到token中的私密信息
|
||||
var userPhone = "15010000000";
|
||||
|
||||
var claims = new List<Claim>()
|
||||
{
|
||||
new Claim("user-phone", userPhone),
|
||||
new Claim("gw-sign", "gw")
|
||||
};
|
||||
|
||||
var principal = new ClaimsPrincipal(new ClaimsIdentity(claims, Scheme.Name));
|
||||
var ticket = new AuthenticationTicket(principal, Scheme.Name);
|
||||
await Task.CompletedTask;
|
||||
return AuthenticateResult.Success(ticket);
|
||||
}
|
||||
|
||||
protected virtual string GetTokenStringFromHeader()
|
||||
{
|
||||
var token = string.Empty;
|
||||
string authorization = Request.Headers[HeaderNames.Authorization];
|
||||
|
||||
if (!string.IsNullOrEmpty(authorization) && authorization.StartsWith($"Bearer ", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
token = authorization["Bearer ".Length..].Trim();
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
}
|
||||
}
|
||||
34
DouyinApi.Gateway/Extensions/CustomOcelotSetup.cs
Normal file
34
DouyinApi.Gateway/Extensions/CustomOcelotSetup.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using DouyinApi.Extensions;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ocelot.DependencyInjection;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Provider.Nacos;
|
||||
using Ocelot.Provider.Polly;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DouyinApi.Gateway.Extensions
|
||||
{
|
||||
public static class CustomOcelotSetup
|
||||
{
|
||||
public static void AddCustomOcelotSetup(this IServiceCollection services)
|
||||
{
|
||||
if (services == null) throw new ArgumentNullException(nameof(services));
|
||||
|
||||
services.AddAuthentication_JWTSetup();
|
||||
services.AddOcelot()
|
||||
.AddDelegatingHandler<CustomResultHandler>()
|
||||
//.AddNacosDiscovery()
|
||||
//.AddConsul()
|
||||
.AddPolly();
|
||||
}
|
||||
|
||||
public static async Task<IApplicationBuilder> UseCustomOcelotMildd(this IApplicationBuilder app)
|
||||
{
|
||||
await app.UseOcelot();
|
||||
return app;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
62
DouyinApi.Gateway/Extensions/CustomResultHandler.cs
Normal file
62
DouyinApi.Gateway/Extensions/CustomResultHandler.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DouyinApi.Gateway.Extensions
|
||||
{
|
||||
public class CustomResultHandler : DelegatingHandler
|
||||
{
|
||||
JsonSerializerSettings _camelSettings = new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver() };
|
||||
|
||||
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await base.SendAsync(request, cancellationToken);
|
||||
var contentType = response.Content.Headers.ContentType?.MediaType ?? "";
|
||||
if (!contentType.Equals("application/json")) return response;
|
||||
|
||||
dynamic result = null;
|
||||
var resultStr = await response.Content.ReadAsStringAsync();
|
||||
try
|
||||
{
|
||||
Console.WriteLine(resultStr);
|
||||
result = JsonConvert.DeserializeObject<dynamic>(resultStr);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return response;
|
||||
}
|
||||
|
||||
if (result != null && result.errorCode == 500) resultStr = result.message.ToString();
|
||||
|
||||
var exception = new Exception(resultStr);
|
||||
|
||||
if (response.StatusCode == HttpStatusCode.InternalServerError || result.errorCode == (int)HttpStatusCode.InternalServerError)
|
||||
{
|
||||
var apiResult = new
|
||||
{
|
||||
Result = false,
|
||||
Message = "服务器内部错误",
|
||||
ErrorCode = (int)HttpStatusCode.InternalServerError,
|
||||
Data = new
|
||||
{
|
||||
exception.Message,
|
||||
exception.StackTrace
|
||||
}
|
||||
};
|
||||
response.Content = new StringContent(JsonConvert.SerializeObject(apiResult, _camelSettings), Encoding.UTF8, "application/json");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
81
DouyinApi.Gateway/Extensions/CustomSwaggerSetup.cs
Normal file
81
DouyinApi.Gateway/Extensions/CustomSwaggerSetup.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using DouyinApi.Common;
|
||||
using DouyinApi.Extensions.Middlewares;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.Filters;
|
||||
using Swashbuckle.AspNetCore.SwaggerUI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using static DouyinApi.Extensions.CustomApiVersion;
|
||||
namespace DouyinApi.Gateway.Extensions
|
||||
{
|
||||
public static class CustomSwaggerSetup
|
||||
{
|
||||
public static void AddCustomSwaggerSetup(this IServiceCollection services)
|
||||
{
|
||||
if (services == null) throw new ArgumentNullException(nameof(services));
|
||||
|
||||
var basePath = AppContext.BaseDirectory;
|
||||
|
||||
services.AddMvc(option => option.EnableEndpointRouting = false);
|
||||
|
||||
services.AddSwaggerGen(c =>
|
||||
{
|
||||
c.SwaggerDoc("v1", new OpenApiInfo
|
||||
{
|
||||
Version = "v1",
|
||||
Title = "自定义网关 接口文档",
|
||||
});
|
||||
|
||||
var xmlPath = Path.Combine(basePath, "DouyinApi.Gateway.xml");
|
||||
c.IncludeXmlComments(xmlPath, true);
|
||||
|
||||
c.OperationFilter<AddResponseHeadersFilter>();
|
||||
c.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();
|
||||
|
||||
c.OperationFilter<SecurityRequirementsOperationFilter>();
|
||||
|
||||
c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
|
||||
{
|
||||
Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格)\"",
|
||||
Name = "Authorization",
|
||||
In = ParameterLocation.Header,
|
||||
Type = SecuritySchemeType.ApiKey
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public static void UseCustomSwaggerMildd(this IApplicationBuilder app, Func<Stream> streamHtml)
|
||||
{
|
||||
if (app == null) throw new ArgumentNullException(nameof(app));
|
||||
|
||||
var apis = new List<string> { "blog-svc" };
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI(c =>
|
||||
{
|
||||
c.SwaggerEndpoint($"/swagger/v1/swagger.json", "gateway");
|
||||
apis.ForEach(m =>
|
||||
{
|
||||
c.SwaggerEndpoint($"/swagger/apiswg/{m}/swagger.json", m);
|
||||
});
|
||||
|
||||
|
||||
if (streamHtml.Invoke() == null)
|
||||
{
|
||||
var msg = "index.html的属性,必须设置为嵌入的资源";
|
||||
throw new Exception(msg);
|
||||
}
|
||||
|
||||
c.IndexStream = streamHtml;
|
||||
|
||||
c.RoutePrefix = "";
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
187
DouyinApi.Gateway/Helper/CustomJwtTokenAuthMiddleware.cs
Normal file
187
DouyinApi.Gateway/Helper/CustomJwtTokenAuthMiddleware.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using DouyinApi.Common;
|
||||
using DouyinApi.Common.Caches;
|
||||
using DouyinApi.Common.Caches.Interface;
|
||||
using DouyinApi.Common.Helper;
|
||||
|
||||
namespace DouyinApi.AuthHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 中间件
|
||||
/// 原做为自定义授权中间件
|
||||
/// 先做检查 header token的使用
|
||||
/// </summary>
|
||||
public class CustomJwtTokenAuthMiddleware
|
||||
{
|
||||
private readonly ICaching _cache;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 验证方案提供对象
|
||||
/// </summary>
|
||||
public IAuthenticationSchemeProvider Schemes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 请求上下文
|
||||
/// </summary>
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
|
||||
public CustomJwtTokenAuthMiddleware(RequestDelegate next, IAuthenticationSchemeProvider schemes, AppSettings appset,ICaching cache)
|
||||
{
|
||||
_cache = cache;
|
||||
_next = next;
|
||||
Schemes = schemes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 网关授权
|
||||
/// </summary>
|
||||
/// <param name="httpContext"></param>
|
||||
/// <returns></returns>
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
var questUrl = httpContext?.Request.Path.Value.ToLower();
|
||||
if (string.IsNullOrEmpty(questUrl)) return;
|
||||
//白名单验证
|
||||
if (CheckWhiteList(questUrl))
|
||||
{
|
||||
await _next.Invoke(httpContext);
|
||||
return;
|
||||
}
|
||||
//黑名单验证
|
||||
if(CheckBlackList(questUrl))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<PermissionItem> Permissions= new();
|
||||
|
||||
httpContext.Features.Set<IAuthenticationFeature>(new AuthenticationFeature
|
||||
{
|
||||
OriginalPath = httpContext.Request.Path,
|
||||
OriginalPathBase = httpContext.Request.PathBase
|
||||
});
|
||||
|
||||
//判断请求是否拥有凭据,即有没有登录
|
||||
var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
|
||||
if (defaultAuthenticate != null)
|
||||
{
|
||||
var Authresult = await httpContext.AuthenticateAsync(defaultAuthenticate.Name);
|
||||
if (Authresult?.Principal != null)
|
||||
{
|
||||
httpContext.User = Authresult.Principal;
|
||||
// 获取当前用户的角色信息
|
||||
var currentUserRoles = (from item in httpContext.User.Claims
|
||||
where item.Type == "CofRole"
|
||||
select item.Value).ToList();
|
||||
var isMatchRole = false;
|
||||
var permisssionRoles = Permissions.Where(w => currentUserRoles.Contains(w.Role));
|
||||
foreach (var item in permisssionRoles)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Regex.IsMatch(questUrl, item.Url, RegexOptions.IgnoreCase))
|
||||
{
|
||||
isMatchRole = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
//验证权限
|
||||
if (currentUserRoles.Count <= 0 || !isMatchRole)
|
||||
{
|
||||
await httpContext.Cof_SendResponse(HttpStatusCode.ServiceUnavailable, "未授权此资源");
|
||||
return ;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await httpContext.Cof_SendResponse(HttpStatusCode.Unauthorized, "请重新登录");
|
||||
return ;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
await httpContext.Cof_SendResponse(HttpStatusCode.Unauthorized, "系统鉴权出错");
|
||||
return ;
|
||||
}
|
||||
await _next.Invoke(httpContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回相应
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="code"></param>
|
||||
/// <returns></returns>
|
||||
private async Task SendResponse(HttpContext context, string message, HttpStatusCode code)
|
||||
{
|
||||
context.Response.StatusCode = (int)code;
|
||||
context.Response.ContentType = "text/plain";
|
||||
await context.Response.WriteAsync(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断是否在白名单内,支持通配符 ****
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
public bool CheckWhiteList(string url)
|
||||
{
|
||||
List<Urlobj> WhiteList = _cache.Cof_GetICaching<List<Urlobj>>("WhiteList", () => AppSettings.app<Urlobj>("WhiteList"), 10);
|
||||
|
||||
if (!WhiteList.Cof_CheckAvailable()) return false;
|
||||
foreach (var Urlitem in WhiteList)
|
||||
{
|
||||
if (Urlitem.url.Equals(url, StringComparison.OrdinalIgnoreCase)) return true;
|
||||
|
||||
if (Urlitem.url.IndexOf("****") > 0)
|
||||
{
|
||||
string UrlitemP = Urlitem.url.Replace("****", "");
|
||||
if (Regex.IsMatch(url, UrlitemP, RegexOptions.IgnoreCase)) return true;
|
||||
if (url.Length >= UrlitemP.Length && UrlitemP.ToLower() == url.Substring(0, UrlitemP.Length).ToLower()) return true;
|
||||
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
public bool CheckBlackList(string url)
|
||||
{
|
||||
List<Urlobj> BlackList = _cache.Cof_GetICaching<List<Urlobj>>("BlackList", () => AppSettings.app<Urlobj>("BlackList"), 10);
|
||||
|
||||
if (!BlackList.Cof_CheckAvailable()) return false;
|
||||
foreach (var Urlitem in BlackList)
|
||||
{
|
||||
if (Urlitem.url.Equals(url, StringComparison.OrdinalIgnoreCase)) return true;
|
||||
|
||||
if (Urlitem.url.IndexOf("****") > 0)
|
||||
{
|
||||
string UrlitemP = Urlitem.url.Replace("****", "");
|
||||
if (Regex.IsMatch(url, UrlitemP, RegexOptions.IgnoreCase)) return true;
|
||||
if (url.Length >= UrlitemP.Length && UrlitemP.ToLower() == url.Substring(0, UrlitemP.Length).ToLower()) return true;
|
||||
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class Urlobj
|
||||
{
|
||||
public string url { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
35
DouyinApi.Gateway/Helper/HeaderDelegatingHandler.cs
Normal file
35
DouyinApi.Gateway/Helper/HeaderDelegatingHandler.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ApiGateway.Helper
|
||||
{
|
||||
public class HeaderDelegatingHandler : DelegatingHandler
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
public HeaderDelegatingHandler(IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
IEnumerable<string> headerValues;
|
||||
if (request.Headers.TryGetValues("AccessToken", out headerValues))
|
||||
{
|
||||
string accessToken = headerValues.First();
|
||||
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
|
||||
request.Headers.Remove("AccessToken");
|
||||
}
|
||||
return await base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
DouyinApi.Gateway/Program.cs
Normal file
28
DouyinApi.Gateway/Program.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace DouyinApi.AdminMvc
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
CreateHostBuilder(args).Build().Run();
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||
{
|
||||
config.AddJsonFile("appsettings.gw.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.gw.{hostingContext.HostingEnvironment.EnvironmentName}.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile("ocelot.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"ocelot.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true);
|
||||
})
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.UseStartup<Startup>().UseUrls("http://*:9000");
|
||||
});
|
||||
}
|
||||
}
|
||||
12
DouyinApi.Gateway/Properties/launchSettings.json
Normal file
12
DouyinApi.Gateway/Properties/launchSettings.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"profiles": {
|
||||
"DouyinApi.Gateway": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:9000",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
80
DouyinApi.Gateway/Startup.cs
Normal file
80
DouyinApi.Gateway/Startup.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using DouyinApi.AuthHelper;
|
||||
using DouyinApi.Common;
|
||||
using DouyinApi.Common.Caches;
|
||||
using DouyinApi.Extensions;
|
||||
using DouyinApi.Gateway.Extensions;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using System.Reflection;
|
||||
using DouyinApi.Common.Caches.Interface;
|
||||
|
||||
namespace DouyinApi.AdminMvc
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
/**
|
||||
*┌──────────────────────────────────────────────────────────────┐
|
||||
*│ 描 述:模拟一个网关项目
|
||||
*│ 测 试:在网关swagger中查看具体的服务
|
||||
*│ 作 者:anson zhang
|
||||
*└──────────────────────────────────────────────────────────────┘
|
||||
*/
|
||||
public Startup(IConfiguration configuration, IWebHostEnvironment env)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton(new AppSettings(Configuration));
|
||||
|
||||
services.AddAuthentication()
|
||||
.AddScheme<AuthenticationSchemeOptions, CustomAuthenticationHandler>(Permissions.GWName, _ => { });
|
||||
|
||||
|
||||
services.AddCustomSwaggerSetup();
|
||||
|
||||
services.AddControllers();
|
||||
|
||||
services.AddHttpContextSetup();
|
||||
|
||||
services.AddCorsSetup();
|
||||
|
||||
services.AddMemoryCache();
|
||||
services.AddDistributedMemoryCache();
|
||||
services.AddSingleton<ICaching, Caching>();
|
||||
|
||||
services.AddCustomOcelotSetup();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseCustomSwaggerMildd(() => Assembly.GetExecutingAssembly().GetManifestResourceStream("DouyinApi.Gateway.index.html"));
|
||||
|
||||
app.UseCors(AppSettings.app(new string[] { "Startup", "Cors", "PolicyName" }));
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
});
|
||||
|
||||
app.UseMiddleware<CustomJwtTokenAuthMiddleware>();
|
||||
|
||||
app.UseCustomOcelotMildd().Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
9
DouyinApi.Gateway/appsettings.gw.Development.json
Normal file
9
DouyinApi.Gateway/appsettings.gw.Development.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
51
DouyinApi.Gateway/appsettings.gw.json
Normal file
51
DouyinApi.Gateway/appsettings.gw.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"Logging": {
|
||||
"Debug": {
|
||||
"LogLevel": {
|
||||
"Default": "Warning"
|
||||
}
|
||||
},
|
||||
"Console": {
|
||||
"LogLevel": {
|
||||
"Default": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Debug"
|
||||
}
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"Startup": {
|
||||
"Cors": {
|
||||
"PolicyName": "CorsIpAccess",
|
||||
"EnableAllIPs": false,
|
||||
"IPs": "http://127.0.0.1:2364,http://localhost:2364"
|
||||
}
|
||||
},
|
||||
"Redis": {
|
||||
"Enable": false,
|
||||
"ConnectionString": "127.0.0.1:6379",
|
||||
"InstanceName": "" //前缀
|
||||
},
|
||||
"Audience": {
|
||||
"Secret": "sdfsdfsrty45634kkhllghtdgdfss345t678fs",
|
||||
"SecretFile": "C:\\my-file\\douyinapi.audience.secret.txt",
|
||||
"Issuer": "DouyinApi",
|
||||
"Audience": "wr"
|
||||
},
|
||||
"WhiteList": [
|
||||
{ "url": "/" },
|
||||
{ "url": "/illagal/****" },
|
||||
{ "url": "/api3/****" },
|
||||
{ "url": "/baseapi/swagger.json" },
|
||||
{ "url": "/swagger/v1/swagger.json" },
|
||||
{ "url": "/swagger/apiswg/blog-svc/swagger.json" }
|
||||
],
|
||||
"BlackList": [
|
||||
{ "url": "/favicon.ico" }
|
||||
],
|
||||
"Influxdb": {
|
||||
"Endpoint": "http://*******:9328",
|
||||
"uid": "root",
|
||||
"pwd": "*****",
|
||||
"dbname": "mndata"
|
||||
}
|
||||
}
|
||||
124
DouyinApi.Gateway/index.html
Normal file
124
DouyinApi.Gateway/index.html
Normal file
@@ -0,0 +1,124 @@
|
||||
|
||||
<!--1、版本号要与nuget包一致;2、id不能为空-->
|
||||
<script async id="mini-profiler" src="/mini-profiler-resources/includes.min.js?v=4.2.1+b27bea37e9" data-version="4.2.1+b27bea37e9" data-path="/mini-profiler-resources/" data-current-id="144b1192-acd3-4fe2-bbc5-6f1e1c6d53df" data-ids="87a1341b-995d-4d1d-aaba-8f2bfcfc9ca9,144b1192-acd3-4fe2-bbc5-6f1e1c6d53df" data-position="Left" data-scheme="Light" data-authorized="true" data-max-traces="15" data-toggle-shortcut="Alt+P" data-trivial-milliseconds="2.0" data-ignored-duplicate-execute-types="Open,OpenAsync,Close,CloseAsync">
|
||||
|
||||
</script>
|
||||
|
||||
<!-- HTML for static distribution bundle build -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<!--极速模式-->
|
||||
<meta name="renderer" content="webkit" />
|
||||
<meta name="force-rendering" content="webkit" />
|
||||
<title>%(DocumentTitle)</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" type="text/css" href="./swagger-ui.css">
|
||||
<link rel="icon" type="image/png" href="./logo/favicon-32x32.png" sizes="32x32" />
|
||||
<script src="/js/jquery-3.3.1.min.js"></script>
|
||||
<style>
|
||||
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.qqgroup {
|
||||
position: absolute;
|
||||
top: 67px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.info {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.download-contents {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
%(HeadContent)
|
||||
</head>
|
||||
<body>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0">
|
||||
<defs>
|
||||
<symbol viewBox="0 0 20 20" id="unlocked">
|
||||
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
|
||||
</symbol>
|
||||
<symbol viewBox="0 0 20 20" id="locked">
|
||||
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z" />
|
||||
</symbol>
|
||||
<symbol viewBox="0 0 20 20" id="close">
|
||||
<path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z" />
|
||||
</symbol>
|
||||
<symbol viewBox="0 0 20 20" id="large-arrow">
|
||||
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z" />
|
||||
</symbol>
|
||||
<symbol viewBox="0 0 20 20" id="large-arrow-down">
|
||||
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z" />
|
||||
</symbol>
|
||||
|
||||
<symbol viewBox="0 0 24 24" id="jump-to">
|
||||
<path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z" />
|
||||
</symbol>
|
||||
<symbol viewBox="0 0 24 24" id="expand">
|
||||
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z" />
|
||||
</symbol>
|
||||
</defs>
|
||||
</svg>
|
||||
<div id="swagger-ui"></div>
|
||||
<div id="footer" style="text-align: center;margin-bottom: 10px;">
|
||||
Copyright © 2018-2022 BCVP
|
||||
|
||||
<br><span id="poweredby">Powered by .NET 5.0.0 on Docker & CentOS 7</span>
|
||||
</div>
|
||||
<!-- Workaround for https://github.com/swagger-api/swagger-editor/issues/1371 -->
|
||||
<script>
|
||||
if (window.navigator.userAgent.indexOf("Edge") > -1) {
|
||||
console.log("Removing native Edge fetch in favor of swagger-ui's polyfill")
|
||||
window.fetch = undefined;
|
||||
}
|
||||
</script>
|
||||
<script src="./swagger-ui-bundle.js"></script>
|
||||
<script src="./swagger-ui-standalone-preset.js"></script>
|
||||
<script>
|
||||
var int = null;
|
||||
if (window.location.href.indexOf("docExpansion") < 0)
|
||||
window.location = window.location.href.replace("index.html", "index.html?docExpansion=none");
|
||||
window.onload = function () {
|
||||
var configObject = JSON.parse('%(ConfigObject)');
|
||||
var oauthConfigObject = JSON.parse('%(OAuthConfigObject)');
|
||||
|
||||
// Apply mandatory parameters
|
||||
configObject.dom_id = "#swagger-ui";
|
||||
configObject.presets = [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset];
|
||||
configObject.layout = "StandaloneLayout";
|
||||
|
||||
// If oauth2RedirectUrl isn't specified, use the built-in default
|
||||
if (!configObject.hasOwnProperty("oauth2RedirectUrl"))
|
||||
configObject.oauth2RedirectUrl = window.location.href.replace("index.html", "oauth2-redirect.html");
|
||||
|
||||
// Build a system
|
||||
const ui = SwaggerUIBundle(configObject);
|
||||
|
||||
// Apply OAuth config
|
||||
ui.initOAuth(oauthConfigObject);
|
||||
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
54
DouyinApi.Gateway/ocelot.Development.json
Normal file
54
DouyinApi.Gateway/ocelot.Development.json
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"Routes": [
|
||||
// blog-svc
|
||||
{
|
||||
"UpstreamPathTemplate": "/svc/blog/{url}",
|
||||
"UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ],
|
||||
"LoadBalancerOptions": {
|
||||
"Type": "RoundRobin"
|
||||
},
|
||||
"DownstreamPathTemplate": "/svc/blog/{url}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 9291
|
||||
}
|
||||
],
|
||||
"AddHeadersToRequest": {
|
||||
"user-phone": "Claims[user-phone] > value",
|
||||
"gw-sign": "Claims[gw-sign] > value"
|
||||
},
|
||||
"UpstreamHeaderTransform": {
|
||||
"custom-key": "blog.gateway"
|
||||
},
|
||||
"DownstreamHeaderTransform": {
|
||||
"trace-id": "Trace-Id"
|
||||
},
|
||||
"AuthenticationOptions": {
|
||||
"AuthenticationProviderKey": "GW"
|
||||
},
|
||||
"DelegatingHandlers": [
|
||||
"CustomResultHandler"
|
||||
]
|
||||
},
|
||||
// blog-svc-swagger
|
||||
{
|
||||
"UpstreamPathTemplate": "/swagger/apiswg/blog-svc/swagger.json",
|
||||
"UpstreamHttpMethod": [ "GET" ],
|
||||
"DownstreamPathTemplate": "/swagger/V2/swagger.json",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 9291
|
||||
}
|
||||
],
|
||||
"LoadBalancer": "RoundRobin"
|
||||
}
|
||||
|
||||
],
|
||||
"GlobalConfiguration": {
|
||||
"BaseUrl": "http://localhost:9000"
|
||||
}
|
||||
}
|
||||
3
DouyinApi.Gateway/ocelot.Production.json
Normal file
3
DouyinApi.Gateway/ocelot.Production.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
||||
3
DouyinApi.Gateway/ocelot.Staging.json
Normal file
3
DouyinApi.Gateway/ocelot.Staging.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
||||
3
DouyinApi.Gateway/ocelot.json
Normal file
3
DouyinApi.Gateway/ocelot.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user