仓储-build

This commit is contained in:
tongfei
2023-10-18 10:56:19 +08:00
parent 05ee79a04f
commit b477662e67
10 changed files with 825 additions and 5 deletions

View File

@@ -0,0 +1,187 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using WMS.Web.Core.Help;
using WMS.Web.Domain.IService.Public;
using WMS.Web.Domain.Mappers;
using WMS.Web.Domain.Options;
using WMS.Web.Domain.Services.Public;
namespace WMS.Web.Repositories.DependencyInjection
{
/// <summary>
/// App服务
/// </summary>
public class AppBuilder
{
/// <summary>
/// 服务集合
/// </summary>
public IServiceCollection Services { get; }
public IConfiguration Configuration { get; }
public IWebHostEnvironment WebHostEnvironment { get; }
/// <summary>
/// asf 服务
/// </summary>
/// <param name="services"></param>
public AppBuilder(IServiceCollection services, IConfiguration configuration, IWebHostEnvironment webHostEnvironment)
{
Services = services;
Configuration = configuration;
WebHostEnvironment = webHostEnvironment;
}
/// <summary>
/// 编译服务
/// </summary>
public void Build()
{
this.AddCors();
this.InitRedis();
this.AddSwagger();
this.AddOther();
this.AddConfigOptions();
this.AddServiceRepositories();
}
/// <summary>
/// 其它功能注入AutoMapper等其它
/// </summary>
private void AddOther()
{
Services.AddMvc().AddNewtonsoftJson(opt =>
{
//// 忽略循环引用
//opt.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
opt.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
//// 不使用驼峰
//opt.SerializerSettings.ContractResolver = new DefaultContractResolver();
});
//AutoMapper映射关系
Services.AddAutoMapper(typeof(AppMapper).Assembly);
}
/// <summary>
/// 跨域注入
/// </summary>
private void AddCors()
{
// 全局跨域注入
Services.AddCors(opt =>
{
string[] urls = Configuration.GetSection("AllowedCores").Value.Split(',');
opt.AddPolicy("AllowAllOrigin", builder =>
{
builder.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
.WithOrigins(urls);
});
});
}
/// <summary>
/// 初始化redis
/// </summary>
private void InitRedis()
{
var option = Configuration.GetSection("AppOptions").Get<AppOptions>();
var option_soa = Configuration.GetSection("SoaOptions").Get<SoaOptions>();
//初始化redis
RedisClient.redisClient.InitConnect(option.RedisConnectionString);
Services.AddHttpClient("ops_client", c =>
{
c.BaseAddress = new Uri(option_soa.Url);
});
}
/// <summary>
/// 注册swagger
/// </summary>
private void AddSwagger()
{
if (WebHostEnvironment.IsDevelopment())
{
// 注册Swagger服务
Services.AddSwaggerGen(c =>
{
// 添加文档信息
c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
{
Title = "WMS",
Version = "v1",
Description = "WMS-Api"
});
#region xml信息
// 使用反射获取xml文件。并构造出文件的路径
//// 启用xml注释. 该方法第二个参数启用控制器的注释默认为false.
foreach (var file in Directory.GetFiles(AppContext.BaseDirectory, "WMS.*.xml")) c.IncludeXmlComments(file, true);
#endregion
#region swagger验证功能
//添加一个必须的全局安全信息和AddSecurityDefinition方法指定的方案名称一致即可CoreAPI。
var securit = new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference=new OpenApiReference { Type=ReferenceType.SecurityScheme,Id= "WMSAPI" }
},
new string[] { }
}
};
c.AddSecurityRequirement(securit);
c.AddSecurityDefinition("WMSAPI", new OpenApiSecurityScheme
{
Description = "JWT授权(数据将在请求头中进行传输) 在下方输入Bearer {token} 即可",
Name = "Authorization",//jwt默认的参数名称
In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
Type = SecuritySchemeType.ApiKey
});
#endregion
});
}
}
/// <summary>
/// 系统配置
/// </summary>
private void AddConfigOptions()
{
//系统配置注册
Services.AddOptions<AppOptions>();
Services.Configure<AppOptions>(Configuration.GetSection("AppOptions"));
Services.AddOptions<SoaOptions>();
Services.Configure<SoaOptions>(Configuration.GetSection("SoaOptions"));
Services.AddOptions<HttpOptions>();
Services.Configure<HttpOptions>(Configuration.GetSection("HttpOptions"));
}
/// <summary>
/// 注入服务层
/// </summary>
/// <param name="builder"></param>
private void AddServiceRepositories()
{
Services.AddSingleton<RedisClientService>();
Services.AddTransient<HttpClientHelp>();
//注入服务
Services.AddTransient<ILoginService, LoginService>();
Services.AddTransient<IHttpClientService, HttpClientService>();
Services.AddTransient<ISingleDataService, SingleDataService>();
}
}
}

View File

@@ -0,0 +1,34 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Text;
using WMS.Web.Repositories.Configuration;
using WMS.Web.Repositories.DependencyInjection;
namespace Microsoft.Extensions.DependencyInjection
{
public static class AppBuilderExtensions
{
/// <summary>
/// 配置数据库
/// </summary>
/// <param name="builder"></param>
/// <param name="configureDbContext"></param>
/// <returns></returns>
public static AppBuilder AddDbContext(this AppBuilder builder, Action<DbContextOptionsBuilder> configureDbContext)
{
builder.Services.AddDbContext<RepositoryDbContext>(configureDbContext);
builder.Services.AddRepositories();
return builder;
}
/// <summary>
/// 注入仓储层
/// </summary>
/// <param name="builder"></param>
private static void AddRepositories(this IServiceCollection services)
{
services.AddHttpContextAccessor();
}
}
}

View File

@@ -0,0 +1,168 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WMS.Web.Core.Dto.Login;
using WMS.Web.Domain.IService.Public;
using WMS.Web.Domain.Services.Public;
namespace WMS.Web.Repositories.DependencyInjection
{
/// <summary>
/// token 黑名单中间件
/// </summary>
public class AuthorizationTokenSecurityPolicy
{
private readonly RequestDelegate _next;
private readonly IMemoryCache _cache;
private DateTime _refreshTime = DateTime.Now.AddYears(-5);
private readonly RedisClientService _redisClientService;
/// <summary>
/// token黑名单中间件
/// </summary>
/// <param name="next"></param>
/// <param name="cache"></param>
public AuthorizationTokenSecurityPolicy(RequestDelegate next, IMemoryCache cache, RedisClientService redisClientService)
{
_next = next;
_cache = cache;
_redisClientService = redisClientService;
}
/// <summary>
/// 事件
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task Invoke(HttpContext context, ILoginService loginService)
{
string authorization = context.Request.Headers["Authorization"];
string path = context.Request.Path.Value.ToLower();
string[] pathlist = path.Split('/');
bool isLogin = pathlist.Where(x => x == "login" || x.ToLower() == "heart" || x.ToLower() == "test").Any();
if (isLogin)
{
context.Response.StatusCode = 200;
await _next(context);
return;
}
if (string.IsNullOrEmpty(authorization))
{
context.Response.StatusCode = 401;
var result = JsonConvert.SerializeObject(new { status = 401, data = string.Empty, message = "授权失败,请重新登录" });
context.Response.ContentType = "application/json;charset=utf-8";
await context.Response.WriteAsync(result);
return;
}
else
{
string token = string.Empty;
if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
{
token = authorization.Substring("Bearer ".Length).Trim();
}
//判断前端过来的token
if (string.IsNullOrEmpty(token))
{
context.Response.StatusCode = 401;
var result = JsonConvert.SerializeObject(new { status = 401, data = string.Empty, message = "授权失败,请重新登录" });
context.Response.ContentType = "application/json;charset=utf-8";
await context.Response.WriteAsync(result);
return;
}
//1.验证是否有登录数据缓存
var logininfo = _redisClientService.GetStringKey<LoginInDto>($"ops_login_{token}");
if (logininfo == null)
{
context.Response.StatusCode = 401;
var result = JsonConvert.SerializeObject(new { status = 401, data = string.Empty, message = "授权失败,请重新登录" });
context.Response.ContentType = "application/json;charset=utf-8";
await context.Response.WriteAsync(result);
return;
}
else
{
//2.判断缓存的token 过期时间
if (!string.IsNullOrEmpty(logininfo.TokenInfo.Token) && logininfo.TokenInfo.Expired.AddMinutes(-10) <= DateTime.Now)
{
var refreshtokeninfo = RefreshToken(logininfo.TokenInfo.Token, logininfo.TokenInfo.RefreshToken, loginService);
if (!refreshtokeninfo)
{
RemoveRedisLoginInfo(token);
}
}
//3.验证检查是否有黑名单的token缓存
string blacktoken = _redisClientService.GetStringKey($"ops_black_token_{logininfo.UserInfo.UcId}");
if (string.IsNullOrEmpty(blacktoken))
{
context.Response.StatusCode = 200;
await _next(context);
}
else
{
RemoveRedisLoginInfo(token);
context.Response.StatusCode = 403;
var result = JsonConvert.SerializeObject(new { status = 403, data = string.Empty, message = "拒绝用户证书试图访问此web站点请与站点管理员联系以建立用户证书权限" });
context.Response.ContentType = "application/json;charset=utf-8";
await context.Response.WriteAsync(result);
}
}
if (context.Response.StatusCode == 401 || context.Response.StatusCode == 403)
{
//调用单点的退出接口
var dto = new LoginOutDto()
{
UcId = logininfo.UserInfo.UcId.ToString(),
SessionId = logininfo.UserInfo.SeesionId,
Token = logininfo.TokenInfo.Token,
AccessToken = logininfo.TokenInfo.PhpToken,
ExpiresIn = logininfo.TokenInfo.Expired
};
var res = await loginService.LoginOutAsync(dto);
return;
}
}
}
/// <summary>
/// 过期或者失效移出token
/// </summary>
/// <param name="token"></param>
private void RemoveRedisLoginInfo(string token)
{
if (!string.IsNullOrEmpty(token))
{
var info = _redisClientService.GetStringKey<LoginInDto>($"ops_login_{token}");
if (info != null)
{
_redisClientService.RemoveStringKey($"ops_login_{token}");
}
}
}
/// <summary>
/// 刷新token
/// </summary>
/// <param name="RefreshToken"></param>
/// <returns></returns>
private bool RefreshToken(string OldToken, string RefreshToken, ILoginService _loginService)
{
var res = _loginService.RefreshToken(OldToken, RefreshToken);
if (!res.Result.Success)
return false;
else
return true;
}
}
}

View File

@@ -0,0 +1,85 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
namespace WMS.Web.Repositories.DependencyInjection
{
/// <summary>
/// 统一全局错误拦截
/// </summary>
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate next;
private readonly ILogger<ErrorHandlingMiddleware> _logger;
/// <summary>
/// 统一错误处理中间件
/// </summary>
public ErrorHandlingMiddleware(RequestDelegate next, ILogger<ErrorHandlingMiddleware> logger)
{
this.next = next;
_logger = logger;
}
private string bodyStr = "";
/// <summary>
/// 激活
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task Invoke(HttpContext context)
{
try
{
//默认 请求body只能读取一次 所以在这里需要手动把他设置成读取多次 并且 不能使用using 释放
context.Request.EnableBuffering();
StreamReader requestReader = new StreamReader(context.Request.Body, Encoding.UTF8);
bodyStr = await requestReader.ReadToEndAsync();
context.Request.Body.Seek(0, SeekOrigin.Begin);
await next(context);
}
catch (Exception ex)
{
// var statusCode = context.Response.StatusCode;
var statusCode = 500;
if (ex is ArgumentException) statusCode = 200;
_logger.LogError($"统一拦截异常处理: {ex.Message}, StackTrace:{ex.StackTrace},Path:{context.Request.Path},Parame:{bodyStr}");
await HandleExceptionAsync(context, statusCode, "服务器错误,不能执行此请求,请稍后重试,若问题一直存在,请与站点管理员联系");
// await HandleExceptionAsync(context, statusCode, "服务器错误");
}
finally
{
var statusCode = context.Response.StatusCode;
var msg = "";
// if (statusCode == 401)
// {
// msg = "未授权";
// }
if (statusCode == 404)
msg = "服务器暂无响应,请稍后重试,若问题一直存在,请与站点管理员联系";
else if (statusCode == 502) msg = "网关出错,请与站点管理员联系";
// else if (statusCode != 200)
// {
// msg = "未知错误";
// }
if (!string.IsNullOrWhiteSpace(msg)) await HandleExceptionAsync(context, statusCode, msg);
}
}
private static Task HandleExceptionAsync(HttpContext context, int statusCode, string msg)
{
string data = null;
var result = JsonConvert.SerializeObject(new { status = statusCode, result = data, message = msg });
context.Response.StatusCode = statusCode;
context.Response.ContentType = "application/json;charset=utf-8";
return context.Response.WriteAsync(result);
}
}
}

View File

@@ -0,0 +1,40 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Text;
using WMS.Web.Repositories.DependencyInjection;
namespace Microsoft.Extensions.DependencyInjection
{
public static class ServiceCollectionExtensions
{
/// <summary>
/// 添加app框架
/// </summary>
/// <param name="services"></param>
/// <param name="startupAction"></param>
/// <returns></returns>
public static IServiceCollection AddApp(this IServiceCollection services, IConfiguration configuration, IWebHostEnvironment webHostEnvironment, Action<AppBuilder> startupAction)
{
services.AddAppCore(configuration, webHostEnvironment, startupAction);
return services;
}
/// <summary>
/// 添加app核心服务
/// </summary>
/// <param name="services"></param>
/// <param name="startupAction">ASF启动配置函数</param>
/// <returns></returns>
internal static IServiceCollection AddAppCore(this IServiceCollection services, IConfiguration configuration, IWebHostEnvironment webHostEnvironment, Action<AppBuilder> startupAction)
{
var builder = new AppBuilder(services, configuration, webHostEnvironment);
startupAction?.Invoke(builder);
builder.Build();
return services;
}
}
}