<develop>:(Web 端)<无> Study_MicroService 初始化

parents

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

{
"Version": 1,
"WorkspaceRootPath": "D:\\Code\\ShopERP.Portal\\",
"Documents": [],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": []
}
]
}
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CoreCms.Net.Configuration\CoreCms.Net.Configuration.csproj" />
<ProjectReference Include="..\CoreCms.Net.IRepository\CoreCms.Net.IRepository.csproj" />
<ProjectReference Include="..\CoreCms.Net.IServices\CoreCms.Net.IServices.csproj" />
<ProjectReference Include="..\CoreCms.Net.Utility\CoreCms.Net.Utility.csproj" />
</ItemGroup>
</Project>

using System;
using System.Collections.Generic;
using System.Text;
using CoreCms.Net.Auth.HttpContextUser;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace CoreCms.Net.Auth
{
/// <summary>
/// 上下文启动
/// </summary>
public static class HttpContextSetup
{
public static void AddHttpContextSetup(this IServiceCollection services)
{
if (services == null) throw new ArgumentNullException(nameof(services));
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<IHttpContextUser, AspNetUser>();
}
}
}

using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using CoreCms.Net.Utility.Extensions;
using Microsoft.AspNetCore.Http;
namespace CoreCms.Net.Auth.HttpContextUser
{
public class AspNetUser : IHttpContextUser
{
private readonly IHttpContextAccessor _accessor;
public AspNetUser(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
public string Name => _accessor.HttpContext.User.Identity.Name;
public int ID => GetClaimValueByType("jti").FirstOrDefault().ObjectToInt();
public bool IsAuthenticated()
{
return _accessor.HttpContext.User.Identity.IsAuthenticated;
}
public string GetToken()
{
return _accessor.HttpContext.Request.Headers["Authorization"].ObjectToString().Replace("Bearer ", "");
}
public List<string> GetUserInfoFromToken(string ClaimType)
{
var jwtHandler = new JwtSecurityTokenHandler();
if (!string.IsNullOrEmpty(GetToken()))
{
JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(GetToken());
return (from item in jwtToken.Claims
where item.Type == ClaimType
select item.Value).ToList();
}
else
{
return new List<string>() { };
}
}
public IEnumerable<Claim> GetClaimsIdentity()
{
return _accessor.HttpContext.User.Claims;
}
public List<string> GetClaimValueByType(string ClaimType)
{
return (from item in GetClaimsIdentity()
where item.Type == ClaimType
select item.Value).ToList();
}
}
}

using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Text;
namespace CoreCms.Net.Auth.HttpContextUser
{
public interface IHttpContextUser
{
string Name { get; }
int ID { get; }
bool IsAuthenticated();
IEnumerable<Claim> GetClaimsIdentity();
List<string> GetClaimValueByType(string ClaimType);
string GetToken();
List<string> GetUserInfoFromToken(string ClaimType);
}
}

using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using CoreCms.Net.Configuration;
using CoreCms.Net.Utility.Extensions;
using Microsoft.IdentityModel.Tokens;
namespace CoreCms.Net.Auth.OverWrite
{
public class JwtHelper
{
/// <summary>
/// 颁发JWT字符串
/// </summary>
/// <param name="tokenModel"></param>
/// <returns></returns>
public static string IssueJwt(TokenModelJwt tokenModel)
{
string iss = AppSettingsConstVars.JwtConfigIssuer;
string aud = AppSettingsConstVars.JwtConfigAudience;
string secret = AppSettingsConstVars.JwtConfigSecretKey;
//var claims = new Claim[] //old
var claims = new List<Claim>
{
/*
* 特别重要:
1、这里将用户的部分信息,比如 uid 存到了Claim 中,如果你想知道如何在其他地方将这个 uid从 Token 中取出来,请看下边的SerializeJwt() 方法,或者在整个解决方案,搜索这个方法,看哪里使用了!
2、你也可以研究下 HttpContext.User.Claims ,具体的你可以看看 Policys/PermissionHandler.cs 类中是如何使用的。
*/
new Claim(JwtRegisteredClaimNames.Jti, tokenModel.Uid.ToString()),
new Claim(JwtRegisteredClaimNames.Iat, $"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"),
new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}") ,
//这个就是过期时间,目前是过期1000秒,可自定义,注意JWT有自己的缓冲过期时间
new Claim (JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddSeconds(1000)).ToUnixTimeSeconds()}"),
new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(1000).ToString()),
new Claim(JwtRegisteredClaimNames.Iss,iss),
new Claim(JwtRegisteredClaimNames.Aud,aud)
//new Claim(ClaimTypes.Role,tokenModel.Role),//为了解决一个用户多个角色(比如:Admin,System),用下边的方法
};
// 可以将一个用户的多个角色全部赋予;
// 作者:DX 提供技术支持;
claims.AddRange(tokenModel.Role.Split(',').Select(s => new Claim(ClaimTypes.Role, s)));
//秘钥 (SymmetricSecurityKey 对安全性的要求,密钥的长度太短会报出异常)
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var jwt = new JwtSecurityToken(
issuer: iss,
claims: claims,
signingCredentials: creds);
var jwtHandler = new JwtSecurityTokenHandler();
var encodedJwt = jwtHandler.WriteToken(jwt);
return encodedJwt;
}
/// <summary>
/// 解析
/// </summary>
/// <param name="jwtStr"></param>
/// <returns></returns>
public static TokenModelJwt SerializeJwt(string jwtStr)
{
var jwtHandler = new JwtSecurityTokenHandler();
JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr);
object role;
try
{
jwtToken.Payload.TryGetValue(ClaimTypes.Role, out role);
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
var tm = new TokenModelJwt
{
Uid = (jwtToken.Id).ObjectToInt(),
Role = role != null ? role.ObjectToString() : ""
};
return tm;
}
}
/// <summary>
/// 令牌
/// </summary>
public class TokenModelJwt
{
/// <summary>
/// Id
/// </summary>
public long Uid { get; set; }
/// <summary>
/// 角色
/// </summary>
public string Role { get; set; }
/// <summary>
/// 职能
/// </summary>
public string Work { get; set; }
}
}

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace CoreCms.Net.Auth.OverWrite
{
/// <summary>
/// 中间件
/// 原做为自定义授权中间件
/// 先做检查 header token的使用
/// </summary>
public class JwtTokenAuth
{
/// <summary>
///
/// </summary>
private readonly RequestDelegate _next;
/// <summary>
///
/// </summary>
/// <param name="next"></param>
public JwtTokenAuth(RequestDelegate next)
{
_next = next;
}
private void PreProceed(HttpContext next)
{
//Console.WriteLine($"{DateTime.Now} middleware invoke preproceed");
//...
}
private void PostProceed(HttpContext next)
{
//Console.WriteLine($"{DateTime.Now} middleware invoke postproceed");
//....
}
/// <summary>
///
/// </summary>
/// <param name="httpContext"></param>
/// <returns></returns>
public Task Invoke(HttpContext httpContext)
{
PreProceed(httpContext);
//检测是否包含'Authorization'请求头
if (!httpContext.Request.Headers.ContainsKey("Authorization"))
{
PostProceed(httpContext);
return _next(httpContext);
}
//var tokenHeader = httpContext.Request.Headers["Authorization"].ToString();
var tokenHeader = httpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
try
{
if (tokenHeader.Length >= 128)
{
//Console.WriteLine($"{DateTime.Now} token :{tokenHeader}");
TokenModelJwt tm = JwtHelper.SerializeJwt(tokenHeader);
//授权
//var claimList = new List<Claim>();
//var claim = new Claim(ClaimTypes.Role, tm.Role);
//claimList.Add(claim);
//var identity = new ClaimsIdentity(claimList);
//var principal = new ClaimsPrincipal(identity);
//httpContext.User = principal;
}
}
catch (Exception e)
{
Console.WriteLine($"{DateTime.Now} middleware wrong:{e.Message}");
}
PostProceed(httpContext);
return _next(httpContext);
}
}
}

using System;
using System.Collections.Generic;
using System.Text;
namespace CoreCms.Net.Auth.Policys
{
public class ApiResponse
{
public int Status { get; set; } = 404;
public object Value { get; set; } = "No Found";
public ApiResponse(StatusCode apiCode, object msg = null)
{
switch (apiCode)
{
case StatusCode.CODE401:
{
Status = 401;
Value = "很抱歉,您无权访问该接口,请确保已经登录!";
}
break;
case StatusCode.CODE403:
{
Status = 403;
Value = "很抱歉,您的访问权限等级不够,联系管理员!";
}
break;
case StatusCode.CODE500:
{
Status = 500;
Value = msg;
}
break;
}
}
}
public enum StatusCode
{
CODE401,
CODE403,
CODE404,
CODE500
}
}

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using CoreCms.Net.Model.ViewModels.UI;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
namespace CoreCms.Net.Auth.Policys
{
public class ApiResponseForAdminHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public ApiResponseForAdminHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
throw new NotImplementedException();
}
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
Response.ContentType = "application/json";
//Response.StatusCode = StatusCodes.Status401Unauthorized;
//await Response.WriteAsync(JsonConvert.SerializeObject(new ApiResponse(StatusCode.CODE401)));
var jm = new AdminUiCallBack();
jm.code = 401;
jm.data = 401;
jm.msg = "很抱歉,您无权访问该接口,请确保已经登录!";
await Response.WriteAsync(JsonConvert.SerializeObject(jm));
}
protected override async Task HandleForbiddenAsync(AuthenticationProperties properties)
{
Response.ContentType = "application/json";
//Response.StatusCode = StatusCodes.Status403Forbidden;
//await Response.WriteAsync(JsonConvert.SerializeObject(new ApiResponse(StatusCode.CODE403)));
var jm = new AdminUiCallBack();
jm.code = 403;
jm.msg = "很抱歉,您的访问权限等级不够,联系管理员!!";
await Response.WriteAsync(JsonConvert.SerializeObject(jm));
}
}
}

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using CoreCms.Net.Model.ViewModels.UI;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
namespace CoreCms.Net.Auth.Policys
{
public class ApiResponseForClientHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public ApiResponseForClientHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
throw new NotImplementedException();
}
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
{
Response.ContentType = "application/json";
//Response.StatusCode = StatusCodes.Status401Unauthorized;
//await Response.WriteAsync(JsonConvert.SerializeObject(new ApiResponse(StatusCode.CODE401)));
var jm = new WebApiCallBack();
jm.status = false;
jm.code = 14007;
jm.data = 14007;
jm.msg = "很抱歉,授权失效,请重新登录!";
await Response.WriteAsync(JsonConvert.SerializeObject(jm));
}
protected override async Task HandleForbiddenAsync(AuthenticationProperties properties)
{
Response.ContentType = "application/json";
//Response.StatusCode = StatusCodes.Status403Forbidden;
//await Response.WriteAsync(JsonConvert.SerializeObject(new ApiResponse(StatusCode.CODE403)));
var jm = new WebApiCallBack();
jm.status = false;
jm.code = 14007;
jm.data = 14007;
jm.msg = "很抱歉,授权失效,请重新登录!";
await Response.WriteAsync(JsonConvert.SerializeObject(jm));
}
}
}

using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace CoreCms.Net.Auth.Policys
{
/// <summary>
/// JWTToken生成类
/// </summary>
public class JwtToken
{
/// <summary>
/// 获取基于JWT的Token
/// </summary>
/// <param name="claims">需要在登陆的时候配置</param>
/// <param name="permissionRequirement">在startup中定义的参数</param>
/// <returns></returns>
public static dynamic BuildJwtToken(Claim[] claims, PermissionRequirement permissionRequirement)
{
var now = DateTime.Now;
// 实例化JwtSecurityToken
var jwt = new JwtSecurityToken(
issuer: permissionRequirement.Issuer,
audience: permissionRequirement.Audience,
claims: claims,
notBefore: now,
expires: now.Add(permissionRequirement.Expiration),
signingCredentials: permissionRequirement.SigningCredentials
);
// 生成 Token
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
//打包返回前台
var responseJson = new
{
success = true,
token = encodedJwt,
expires_in = permissionRequirement.Expiration.TotalSeconds,
token_type = "Bearer"
};
return responseJson;
}
}
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using CoreCms.Net.Configuration;
using CoreCms.Net.IServices;
using CoreCms.Net.Utility.Extensions;
using CoreCms.Net.Utility.Helper;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace CoreCms.Net.Auth.Policys
{
/// <summary>
/// 权限授权处理器
/// </summary>
public class PermissionForAdminHandler : AuthorizationHandler<PermissionRequirement>
{
/// <summary>
/// 验证方案提供对象
/// </summary>
public IAuthenticationSchemeProvider Schemes { get; set; }
private readonly ISysRoleMenuServices _sysRoleMenuServices;
private readonly IHttpContextAccessor _accessor;
/// <summary>
/// 构造函数注入
/// </summary>
/// <param name="schemes"></param>
/// <param name="navigationRepository"></param>
/// <param name="accessor"></param>
public PermissionForAdminHandler(IAuthenticationSchemeProvider schemes
, ISysRoleMenuServices sysRoleMenuServices
, IHttpContextAccessor accessor)
{
_accessor = accessor;
Schemes = schemes;
_sysRoleMenuServices = sysRoleMenuServices;
}
// 重写异步处理程序
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
var httpContext = _accessor.HttpContext;
if (!requirement.Permissions.Any())
{
var data = await _sysRoleMenuServices.RoleModuleMaps();
var list = new List<PermissionItem>();
if (Permissions.IsUseIds4)
{
list = (from item in data
orderby item.id
select new PermissionItem
{
Url = item.menu?.component,
RouteUrl = item.menu?.path,
Authority = item.menu?.authority,
Role = item.role?.id.ObjectToString(),
}).ToList();
}
else
{
list = (from item in data
orderby item.id
select new PermissionItem
{
Url = item.menu?.component,
RouteUrl = item.menu?.path,
Authority = item.menu?.authority,
Role = item.role?.roleCode,
}).ToList();
}
requirement.Permissions = list;
}
//请求Url
if (httpContext != null)
{
//
var questUrl = httpContext.Request.Path.Value.ToLower();
//判断请求是否停止
var handlers = httpContext.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
{
if (await handlers.GetHandlerAsync(httpContext, scheme.Name) is IAuthenticationRequestHandler handler && await handler.HandleRequestAsync())
{
context.Fail();
return;
}
}
//判断请求是否拥有凭据,即有没有登录
var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
if (defaultAuthenticate != null)
{
var result = await httpContext.AuthenticateAsync(defaultAuthenticate.Name);
//result?.Principal不为空即登录成功
if (result?.Principal != null)
{
httpContext.User = result.Principal;
// 获取当前用户的角色信息
var currentUserRoles = new List<string>();
// ids4和jwt切换
// ids4
if (Permissions.IsUseIds4)
{
currentUserRoles = (from item in httpContext.User.Claims
where item.Type == "role"
select item.Value).ToList();
}
else
{
// jwt
currentUserRoles = (from item in httpContext.User.Claims
where item.Type == requirement.ClaimType
select item.Value).ToList();
}
var isMatchRole = false;
var permisssionRoles = requirement.Permissions.Where(w => currentUserRoles.Contains(w.Role));
foreach (var item in permisssionRoles)
{
try
{
//权限中是否存在请求的url
if (Regex.Match(questUrl, item.Url.ObjectToString().ToLower()).Value == questUrl)
{
isMatchRole = true;
break;
}
}
catch (Exception)
{
// ignored
}
}
//验证权限
if (currentUserRoles.Count <= 0 || !isMatchRole)
{
context.Fail();
return;
}
var isExp = false;
// ids4和jwt切换
// ids4
if (Permissions.IsUseIds4)
{
isExp = (httpContext.User.Claims.SingleOrDefault(s => s.Type == "exp")?.Value) != null && DateHelper.StampToDateTime(httpContext.User.Claims.SingleOrDefault(s => s.Type == "exp")?.Value) >= DateTime.Now;
}
else
{
// jwt
isExp = (httpContext.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.Expiration)?.Value) != null && DateTime.Parse(httpContext.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.Expiration)?.Value) >= DateTime.Now;
}
if (isExp)
{
context.Succeed(requirement);
}
else
{
context.Fail();
return;
}
return;
}
else
{
context.Fail();
return;
}
}
else
{
context.Fail();
return;
}
//判断没有登录时,是否访问登录的url,并且是Post请求,并且是form表单提交类型,否则为失败
//if (!questUrl.Equals(requirement.LoginPath.ToLower(), StringComparison.Ordinal) && (!httpContext.Request.Method.Equals("POST") || !httpContext.Request.HasJsonContentType()))
//{
// context.Fail();
// return;
//}
}
context.Succeed(requirement);
}
}
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using CoreCms.Net.Utility.Extensions;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace CoreCms.Net.Auth.Policys
{
/// <summary>
/// 权限授权处理器
/// </summary>
public class PermissionForClientHandler : AuthorizationHandler<PermissionRequirement>
{
/// <summary>
/// 验证方案提供对象
/// </summary>
public IAuthenticationSchemeProvider Schemes { get; set; }
private readonly IHttpContextAccessor _accessor;
/// <summary>
/// 构造函数注入
/// </summary>
/// <param name="schemes"></param>
/// <param name="navigationRepository"></param>
/// <param name="accessor"></param>
public PermissionForClientHandler(IAuthenticationSchemeProvider schemes, IHttpContextAccessor accessor)
{
_accessor = accessor;
Schemes = schemes;
}
// 重写异步处理程序
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
var httpContext = _accessor.HttpContext;
if (!requirement.Permissions.Any())
{
//var data = await _navigationRepository.QueryAsync();
//var list = (from item in data
// where item.isLock == false
// orderby item.id
// select new PermissionItem
// {
// Url = item.linkUrl,
// Role = item.identificationCode,
// }).ToList();
//requirement.Permissions = list;
}
//请求Url
if (httpContext != null)
{
var questUrl = httpContext.Request.Path.Value.ToLower();
//判断请求是否停止
var handlers = httpContext.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
{
if (await handlers.GetHandlerAsync(httpContext, scheme.Name) is IAuthenticationRequestHandler handler && await handler.HandleRequestAsync())
{
context.Fail();
return;
}
}
//判断请求是否拥有凭据,即有没有登录
var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
if (defaultAuthenticate != null)
{
var result = await httpContext.AuthenticateAsync(defaultAuthenticate.Name);
//result?.Principal不为空即登录成功
if (result?.Principal != null)
{
// 将最新的角色和接口列表更新
// 这里暂时把代码移动到了Login获取token的api里,这样就不用每次都请求数据库,造成压力.
// 但是这样有个问题,就是如果修改了某一个角色的菜单权限,不会立刻更新,
// 需要让用户退出重新登录,如果你想实时更新,请把下边的注释打开即可.
//var data = await _roleModulePermissionServices.RoleModuleMaps();
//var list = (from item in data
// where item.IsDeleted == false
// orderby item.Id
// select new PermissionItem
// {
// Url = item.Module?.LinkUrl,
// Role = item.Role?.Name,
// }).ToList();
//requirement.Permissions = list;
httpContext.User = result.Principal;
//权限中是否存在请求的url
//if (requirement.Permissions.GroupBy(g => g.Url).Where(w => w.Key?.ToLower() == questUrl).Count() > 0)
//if (isMatchUrl)
if (true)
{
// 获取当前用户的角色信息
var currentUserRoles = (from item in httpContext.User.Claims
where item.Type == requirement.ClaimType
select item.Value).ToList();
var isMatchRole = false;
var permisssionRoles = requirement.Permissions.Where(w => currentUserRoles.Contains(w.Role));
foreach (var item in permisssionRoles)
{
try
{
if (Regex.Match(questUrl, item.Url?.ObjectToString().ToLower())?.Value == questUrl)
{
isMatchRole = true;
break;
}
}
catch (Exception)
{
// ignored
}
}
//验证权限
//if (currentUserRoles.Count <= 0 || requirement.Permissions.Where(w => currentUserRoles.Contains(w.Role) && w.Url.ToLower() == questUrl).Count() <= 0)
if (currentUserRoles.Count <= 0 || !isMatchRole)
{
context.Fail();
return;
}
}
//判断过期时间(这里仅仅是最坏验证原则,你可以不要这个if else的判断,因为我们使用的官方验证,Token过期后上边的result?.Principal 就为 null 了,进不到这里了,因此这里其实可以不用验证过期时间,只是做最后严谨判断)
if ((httpContext.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.Expiration)?.Value) != null && DateTime.Parse(httpContext.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.Expiration)?.Value) >= DateTime.Now)
{
context.Succeed(requirement);
}
else
{
context.Fail();
return;
}
return;
}
else
{
context.Fail();
return;
}
}
else
{
context.Fail();
return;
}
//判断没有登录时,是否访问登录的url,并且是Post请求,并且是form表单提交类型,否则为失败
//if (!questUrl.Equals(requirement.LoginPath.ToLower(), StringComparison.Ordinal) && (!httpContext.Request.Method.Equals("POST") || !httpContext.Request.HasJsonContentType()))
//{
// context.Fail();
// return;
//}
}
context.Succeed(requirement);
}
}
}

using System;
using System.Collections.Generic;
using System.Text;
namespace CoreCms.Net.Auth.Policys
{
/// <summary>
/// 用户或角色或其他凭据实体
/// </summary>
public class PermissionItem
{
/// <summary>
/// 用户或角色或其他凭据名称
/// </summary>
public virtual string Role { get; set; }
/// <summary>
/// 请求Url
/// </summary>
public virtual string Url { get; set; }
/// <summary>
/// 权限标识
/// </summary>
public virtual string Authority { get; set; }
/// <summary>
/// 路由标识Url
/// </summary>
public virtual string RouteUrl { get; set; }
}
}

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Authorization;
using Microsoft.IdentityModel.Tokens;
namespace CoreCms.Net.Auth.Policys
{
/// <summary>
/// 必要参数类
/// 继承 IAuthorizationRequirement,用于设计自定义权限处理器PermissionHandler
/// 因为AuthorizationHandler 中的泛型参数 TRequirement 必须继承 IAuthorizationRequirement
/// </summary>
public class PermissionRequirement : IAuthorizationRequirement
{
/// <summary>
/// 用户权限集合,一个订单包含了很多详情,
/// 同理,一个网站的认证发行中,也有很多权限详情(这里是Role和URL的关系)
/// </summary>
public List<PermissionItem> Permissions { get; set; }
/// <summary>
/// 无权限action
/// </summary>
public string DeniedAction { get; set; }
/// <summary>
/// 认证授权类型
/// </summary>
public string ClaimType { internal get; set; }
/// <summary>
/// 请求路径
/// </summary>
public string LoginPath { get; set; } = "/Api/Login";
/// <summary>
/// 发行人
/// </summary>
public string Issuer { get; set; }
/// <summary>
/// 订阅人
/// </summary>
public string Audience { get; set; }
/// <summary>
/// 过期时间
/// </summary>
public TimeSpan Expiration { get; set; }
/// <summary>
/// 签名验证
/// </summary>
public SigningCredentials SigningCredentials { get; set; }
/// <summary>
/// 构造
/// </summary>
/// <param name="deniedAction">拒约请求的url</param>
/// <param name="permissions">权限集合</param>
/// <param name="claimType">声明类型</param>
/// <param name="issuer">发行人</param>
/// <param name="audience">订阅人</param>
/// <param name="signingCredentials">签名验证实体</param>
/// <param name="expiration">过期时间</param>
public PermissionRequirement(string deniedAction, List<PermissionItem> permissions, string claimType, string issuer, string audience, SigningCredentials signingCredentials, TimeSpan expiration)
{
ClaimType = claimType;
DeniedAction = deniedAction;
Permissions = permissions;
Issuer = issuer;
Audience = audience;
Expiration = expiration;
SigningCredentials = signingCredentials;
}
}
}

using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using CoreCms.Net.Configuration;
using Microsoft.IdentityModel.Tokens;
namespace CoreCms.Net.Auth
{
public class TokenHelper
{
/// <summary>
/// Use the below code to generate symmetric Secret Key
/// var hmac = new HMACSHA256();
/// var key = Convert.ToBase64String(hmac.Key);
/// </summary>
private const string Secret = "db3OIsj+BXE9NZDy0t8W3TcNekrF+2d/1sFnWG4HnV8TZY30iTOdtVWJG8abWvB1GlOgJuQZdcF2Luqm/hccMw==";
public static string GenerateToken(string username, int expireMinutes = 120)
{ // 此方法用来生成 Token
var symmetricKey = Convert.FromBase64String(Secret); // 生成二进制字节数组
var tokenHandler = new JwtSecurityTokenHandler(); // 创建一个JwtSecurityTokenHandler类用来生成Token
var now = DateTime.UtcNow; // 获取当前时间
var tokenDescriptor = new SecurityTokenDescriptor // 创建一个 Token 的原始对象
{
Subject = new ClaimsIdentity(new[] // Token的身份证,类似一个人可以有身份证,户口本
{
new Claim(ClaimTypes.Name, username) // 可以创建多个
}),
Expires = now.AddMinutes(Convert.ToInt32(expireMinutes)), // Token 有效期
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(symmetricKey), SecurityAlgorithms.HmacSha256)
// 生成一个Token证书,第一个参数是根据预先的二进制字节数组生成一个安全秘钥,说白了就是密码,第二个参数是编码方式
};
var stoken = tokenHandler.CreateToken(tokenDescriptor); // 生成一个编码后的token对象实例
var token = tokenHandler.WriteToken(stoken); // 生成token字符串,给前端使用
return token;
}
public static ClaimsPrincipal GetPrincipal(string token)
{ // 此方法用解码字符串token,并返回秘钥的信息对象
try
{
var tokenHandler = new JwtSecurityTokenHandler(); // 创建一个JwtSecurityTokenHandler类,用来后续操作
var jwtToken = tokenHandler.ReadToken(token) as JwtSecurityToken; // 将字符串token解码成token对象
if (jwtToken == null)
return null;
var symmetricKey = Convert.FromBase64String(Secret); // 生成编码对应的字节数组
var validationParameters = new TokenValidationParameters() // 生成验证token的参数
{
RequireExpirationTime = true, // token是否包含有效期
ValidateIssuer = false, // 验证秘钥发行人,如果要验证在这里指定发行人字符串即可
ValidateAudience = false, // 验证秘钥的接受人,如果要验证在这里提供接收人字符串即可
IssuerSigningKey = new SymmetricSecurityKey(symmetricKey) // 生成token时的安全秘钥
};
SecurityToken securityToken; // 接受解码后的token对象
var principal = tokenHandler.ValidateToken(token, validationParameters, out securityToken);
return principal; // 返回秘钥的主体对象,包含秘钥的所有相关信息
}
catch
{
return null;
}
}
/// <summary>
/// 此方法用解码字符串token,并返回秘钥的信息对象
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
public static int GetUserIdBySecurityToken(string token)
{
//读取配置文件
var symmetricKeyAsBase64 = AppSettingsConstVars.JwtConfigSecretKey;
var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);
var signingKey = new SymmetricSecurityKey(keyByteArray);
var issuer = AppSettingsConstVars.JwtConfigIssuer;
var audience = AppSettingsConstVars.JwtConfigAudience;
try
{
var tokenHandler = new JwtSecurityTokenHandler(); // 创建一个JwtSecurityTokenHandler类,用来后续操作
var jwtToken = tokenHandler.ReadToken(token) as JwtSecurityToken; // 将字符串token解码成token对象
if (jwtToken == null)
return 0;
var validationParameters = new TokenValidationParameters() // 生成验证token的参数
{
ValidateIssuerSigningKey = true, //是否验证SecurityKey
IssuerSigningKey = signingKey, //拿到SecurityKey
ValidateIssuer = true, //是否验证Issuer
ValidIssuer = issuer,//发行人 //Issuer,这两项和前面签发jwt的设置一致
ValidateAudience = true, //是否验证Audience
ValidAudience = audience,//订阅人
ValidateLifetime = true,//是否验证失效时间
ClockSkew = TimeSpan.FromSeconds(60),
RequireExpirationTime = true,
};
SecurityToken securityToken; // 接受解码后的token对象
var principal = tokenHandler.ValidateToken(token, validationParameters, out securityToken);
if (securityToken == null || string.IsNullOrEmpty(securityToken.Id))
{
return 0;
}
return Convert.ToInt32(securityToken.Id); // 返回秘钥的主体对象,包含秘钥的所有相关信息
}
catch
{
return 0;
}
}
}
}
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\Administrator\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.13.2</NuGetToolVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<SourceRoot Include="C:\Users\Administrator\.nuget\packages\" />
<SourceRoot Include="C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages\" />
</ItemGroup>
</Project>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<Import Project="$(NuGetPackageRoot)system.text.json\8.0.0\buildTransitive\net6.0\System.Text.Json.targets" Condition="Exists('$(NuGetPackageRoot)system.text.json\8.0.0\buildTransitive\net6.0\System.Text.Json.targets')" />
<Import Project="$(NuGetPackageRoot)sqlitepclraw.lib.e_sqlite3\2.1.6\buildTransitive\net8.0\SQLitePCLRaw.lib.e_sqlite3.targets" Condition="Exists('$(NuGetPackageRoot)sqlitepclraw.lib.e_sqlite3\2.1.6\buildTransitive\net8.0\SQLitePCLRaw.lib.e_sqlite3.targets')" />
</ImportGroup>
</Project>
\ No newline at end of file
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("CoreCms.Net.Auth")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
[assembly: System.Reflection.AssemblyProductAttribute("CoreCms.Net.Auth")]
[assembly: System.Reflection.AssemblyTitleAttribute("CoreCms.Net.Auth")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// 由 MSBuild WriteCodeFragment 类生成。
e93889ae9c856360b9433fe256a31b2b7d40f637207ec43d97fb9818594ce034
is_global = true
build_property.TargetFramework = net8.0
build_property.TargetPlatformMinVersion =
build_property.UsingMicrosoftNETSdkWeb =
build_property.ProjectTypeGuids =
build_property.InvariantGlobalization =
build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = CoreCms.Net.Auth
build_property.ProjectDir = D:\Code\ShopERP.Portal\CoreCms.Net.Auth\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.EffectiveAnalysisLevelStyle = 8.0
build_property.EnableCodeStyleSeverity =
6a6b60772005f6e65553c17c059518ebcf0fa936888e103c2a25453b1cb75147
This source diff could not be displayed because it is too large. You can view the blob instead.
namespace CoreCms.Net.Caching.AutoMate.MemoryCache
{
/// <summary>
/// 简单的缓存接口,只有查询和添加,以后会进行扩展
/// </summary>
public interface ICachingProvider
{
object Get(string cacheKey);
void Set(string cacheKey, object cacheValue, int timeSpan);
}
}
using Microsoft.Extensions.Caching.Memory;
using System;
namespace CoreCms.Net.Caching.AutoMate.MemoryCache
{
/// <summary>
/// 实例化缓存接口ICaching
/// </summary>
public class MemoryCaching : ICachingProvider
{
//引用Microsoft.Extensions.Caching.Memory;这个和.net 还是不一样,没有了Httpruntime了
private readonly IMemoryCache _cache;
//还是通过构造函数的方法,获取
public MemoryCaching(IMemoryCache cache)
{
_cache = cache;
}
public object Get(string cacheKey)
{
return _cache.Get(cacheKey);
}
public void Set(string cacheKey, object cacheValue, int timeSpan)
{
_cache.Set(cacheKey, cacheValue, TimeSpan.FromSeconds(timeSpan * 60));
}
}
}
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using StackExchange.Redis;
namespace CoreCms.Net.Caching.AutoMate.RedisCache
{
/// <summary>
/// Redis缓存接口
/// </summary>
public interface IRedisOperationRepository
{
//获取 Reids 缓存值
Task<string> Get(string key);
//获取值,并序列化
Task<TEntity> Get<TEntity>(string key);
//保存
Task Set(string key, object value, TimeSpan cacheTime);
//判断是否存在
Task<bool> Exist(string key);
//移除某一个缓存值
Task Remove(string key);
//全部清除
Task Clear();
/// <summary>
/// 根据key获取RedisValue
/// </summary>
/// <param name="redisKey"></param>
/// <returns></returns>
Task<RedisValue[]> ListRangeAsync(string redisKey);
/// <summary>
/// 在列表头部插入值。如果键不存在,先创建再插入值
/// </summary>
/// <param name="redisKey"></param>
/// <param name="redisValue"></param>
/// <returns></returns>
Task<long> ListLeftPushAsync(string redisKey, string redisValue);
/// <summary>
/// 在列表尾部插入值。如果键不存在,先创建再插入值
/// </summary>
/// <param name="redisKey"></param>
/// <param name="redisValue"></param>
/// <returns></returns>
Task<long> ListRightPushAsync(string redisKey, string redisValue);
/// <summary>
/// 在列表尾部插入数组集合。如果键不存在,先创建再插入值
/// </summary>
/// <param name="redisKey"></param>
/// <param name="redisValue"></param>
/// <returns></returns>
Task<long> ListRightPushAsync(string redisKey, IEnumerable<string> redisValue);
/// <summary>
/// 移除并返回存储在该键列表的第一个元素 反序列化
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="redisKey"></param>
/// <returns></returns>
Task<T> ListLeftPopAsync<T>(string redisKey) where T : class;
/// <summary>
/// 移除并返回存储在该键列表的最后一个元素 反序列化
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="redisKey"></param>
/// <returns></returns>
Task<T> ListRightPopAsync<T>(string redisKey) where T : class;
/// <summary>
/// 移除并返回存储在该键列表的第一个元素
/// </summary>
/// <param name="redisKey"></param>
/// <returns></returns>
Task<string> ListLeftPopAsync(string redisKey);
/// <summary>
/// 移除并返回存储在该键列表的最后一个元素
/// </summary>
/// <param name="redisKey"></param>
/// <returns></returns>
Task<string> ListRightPopAsync(string redisKey);
/// <summary>
/// 列表长度
/// </summary>
/// <param name="redisKey"></param>
/// <returns></returns>
Task<long> ListLengthAsync(string redisKey);
/// <summary>
/// 返回在该列表上键所对应的元素
/// </summary>
/// <param name="redisKey"></param>
/// <param name="db"></param>
/// <returns></returns>
Task<IEnumerable<string>> ListRangeAsync(string redisKey, int db = -1);
/// <summary>
/// 根据索引获取指定位置数据
/// </summary>
/// <param name="redisKey"></param>
/// <param name="start"></param>
/// <param name="stop"></param>
/// <returns></returns>
Task<IEnumerable<string>> ListRangeAsync(string redisKey, int start, int stop);
/// <summary>
/// 删除List中的元素 并返回删除的个数
/// </summary>
/// <param name="redisKey"></param>
/// <param name="redisValue"></param>
/// <param name="type"></param>
/// <returns></returns>
Task<long> ListDelRangeAsync(string redisKey, string redisValue, long type = 0);
/// <summary>
/// 清空List
/// </summary>
/// <param name="redisKey"></param>
/// <returns></returns>
Task ListClearAsync(string redisKey);
/// <summary>
/// 有序集合/定时任务延迟队列用的多
/// </summary>
/// <param name="redisKey">key</param>
/// <param name="redisValue">元素</param>
/// <param name="score">分数</param>
Task SortedSetAddAsync(string redisKey, string redisValue, double score);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using StackExchange.Redis;
namespace CoreCms.Net.Caching.AutoMate.RedisCache
{
public class RedisOperationRepository : IRedisOperationRepository
{
private readonly ILogger<RedisOperationRepository> _logger;
private readonly ConnectionMultiplexer _redis;
private readonly IDatabase _database;
public RedisOperationRepository(ILogger<RedisOperationRepository> logger, ConnectionMultiplexer redis)
{
_logger = logger;
_redis = redis;
_database = redis.GetDatabase();
}
private IServer GetServer()
{
var endpoint = _redis.GetEndPoints();
return _redis.GetServer(endpoint.First());
}
public async Task Clear()
{
foreach (var endPoint in _redis.GetEndPoints())
{
var server = GetServer();
foreach (var key in server.Keys())
{
await _database.KeyDeleteAsync(key);
}
}
}
public async Task<bool> Exist(string key)
{
return await _database.KeyExistsAsync(key);
}
public async Task<string> Get(string key)
{
return await _database.StringGetAsync(key);
}
public async Task Remove(string key)
{
await _database.KeyDeleteAsync(key);
}
public async Task Set(string key, object value, TimeSpan cacheTime)
{
if (value != null)
{
//序列化,将object值生成RedisValue
await _database.StringSetAsync(key, JsonConvert.SerializeObject(value), cacheTime);
}
}
public async Task<TEntity> Get<TEntity>(string key)
{
var value = await _database.StringGetAsync(key);
if (value.HasValue)
{
//需要用的反序列化,将Redis存储的Byte[],进行反序列化
return JsonConvert.DeserializeObject<TEntity>(value);
}
else
{
return default;
}
}
/// <summary>
/// 根据key获取RedisValue
/// </summary>
/// <param name="redisKey"></param>
/// <returns></returns>
public async Task<RedisValue[]> ListRangeAsync(string redisKey)
{
return await _database.ListRangeAsync(redisKey);
}
/// <summary>
/// 在列表头部插入值。如果键不存在,先创建再插入值
/// </summary>
/// <param name="redisKey"></param>
/// <param name="redisValue"></param>
/// <returns></returns>
public async Task<long> ListLeftPushAsync(string redisKey, string redisValue)
{
return await _database.ListLeftPushAsync(redisKey, redisValue);
}
/// <summary>
/// 在列表尾部插入值。如果键不存在,先创建再插入值
/// </summary>
/// <param name="redisKey"></param>
/// <param name="redisValue"></param>
/// <returns></returns>
public async Task<long> ListRightPushAsync(string redisKey, string redisValue)
{
return await _database.ListRightPushAsync(redisKey, redisValue);
}
/// <summary>
/// 在列表尾部插入数组集合。如果键不存在,先创建再插入值
/// </summary>
/// <param name="redisKey"></param>
/// <param name="redisValue"></param>
/// <returns></returns>
public async Task<long> ListRightPushAsync(string redisKey, IEnumerable<string> redisValue)
{
var redislist = new List<RedisValue>();
foreach (var item in redisValue)
{
redislist.Add(item);
}
return await _database.ListRightPushAsync(redisKey, redislist.ToArray());
}
/// <summary>
/// 移除并返回存储在该键列表的第一个元素 反序列化
/// </summary>
/// <param name="redisKey"></param>
/// <returns></returns>
public async Task<T> ListLeftPopAsync<T>(string redisKey) where T : class
{
var cacheValue = await _database.ListLeftPopAsync(redisKey);
if (string.IsNullOrEmpty(cacheValue)) return null;
var res = JsonConvert.DeserializeObject<T>(cacheValue);
return res;
}
/// <summary>
/// 移除并返回存储在该键列表的最后一个元素 反序列化
/// 只能是对象集合
/// </summary>
/// <param name="redisKey"></param>
/// <returns></returns>
public async Task<T> ListRightPopAsync<T>(string redisKey) where T : class
{
var cacheValue = await _database.ListRightPopAsync(redisKey);
if (string.IsNullOrEmpty(cacheValue)) return null;
var res = JsonConvert.DeserializeObject<T>(cacheValue);
return res;
}
/// <summary>
/// 移除并返回存储在该键列表的第一个元素
/// </summary>
/// <param name="redisKey"></param>
/// <returns></returns>
public async Task<string> ListLeftPopAsync(string redisKey)
{
return await _database.ListLeftPopAsync(redisKey);
}
/// <summary>
/// 移除并返回存储在该键列表的最后一个元素
/// </summary>
/// <param name="redisKey"></param>
/// <returns></returns>
public async Task<string> ListRightPopAsync(string redisKey)
{
return await _database.ListRightPopAsync(redisKey);
}
/// <summary>
/// 列表长度
/// </summary>
/// <param name="redisKey"></param>
/// <returns></returns>
public async Task<long> ListLengthAsync(string redisKey)
{
return await _database.ListLengthAsync(redisKey);
}
/// <summary>
/// 返回在该列表上键所对应的元素
/// </summary>
/// <param name="redisKey"></param>
/// <returns></returns>
public async Task<IEnumerable<string>> ListRangeAsync(string redisKey, int db = -1)
{
var result = await _database.ListRangeAsync(redisKey);
return result.Select(o => o.ToString());
}
/// <summary>
/// 根据索引获取指定位置数据
/// </summary>
/// <param name="redisKey"></param>
/// <param name="start"></param>
/// <param name="stop"></param>
/// <returns></returns>
public async Task<IEnumerable<string>> ListRangeAsync(string redisKey, int start, int stop)
{
var result = await _database.ListRangeAsync(redisKey, start, stop);
return result.Select(o => o.ToString());
}
/// <summary>
/// 删除List中的元素 并返回删除的个数
/// </summary>
/// <param name="redisKey">key</param>
/// <param name="redisValue">元素</param>
/// <param name="type">大于零 : 从表头开始向表尾搜索,小于零 : 从表尾开始向表头搜索,等于零:移除表中所有与 VALUE 相等的值</param>
/// <returns></returns>
public async Task<long> ListDelRangeAsync(string redisKey, string redisValue, long type = 0)
{
return await _database.ListRemoveAsync(redisKey, redisValue, type);
}
/// <summary>
/// 清空List
/// </summary>
/// <param name="redisKey"></param>
public async Task ListClearAsync(string redisKey)
{
await _database.ListTrimAsync(redisKey, 1, 0);
}
/// <summary>
/// 有序集合/定时任务延迟队列用的多
/// </summary>
/// <param name="redisKey">key</param>
/// <param name="redisValue">元素</param>
/// <param name="score">分数</param>
public async Task SortedSetAddAsync(string redisKey, string redisValue, double score)
{
await _database.SortedSetAddAsync(redisKey, redisValue, score);
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
<PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="2.36.0" />
<PackageReference Include="StackExchange.Redis" Version="2.7.10" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CoreCms.Net.Configuration\CoreCms.Net.Configuration.csproj" />
<ProjectReference Include="..\CoreCms.Net.Utility\CoreCms.Net.Utility.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="AccressToken\" />
</ItemGroup>
</Project>
using System;
using System.Collections.Generic;
namespace CoreCms.Net.Caching.Manual
{
/// <summary>
/// 手动缓存操作接口
/// </summary>
public interface IManualCacheManager
{
/// <summary>
/// 验证缓存项是否存在
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
bool Exists(string key);
/// <summary>
/// 添加缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">缓存Value</param>
/// <param name="expiresIn">缓存时长(分钟)</param>
/// <returns></returns>
bool Set(string key, object value, int expiresIn = 0);
/// <summary>
/// 删除缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
void Remove(string key);
/// <summary>
/// 批量删除缓存
/// </summary>
/// <returns></returns>
void RemoveAll(IEnumerable<string> keys);
/// <summary>
/// 获取缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
T Get<T>(string key);
/// <summary>
/// 获取缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
object Get(string key);
/// <summary>
/// 获取缓存集合
/// </summary>
/// <param name="keys">缓存Key集合</param>
/// <returns></returns>
IDictionary<string, object> GetAll(IEnumerable<string> keys);
/// <summary>
/// 删除所有缓存
/// </summary>
void RemoveCacheAll();
/// <summary>
/// 删除匹配到的缓存
/// </summary>
/// <param name="pattern"></param>
/// <returns></returns>
void RemoveCacheRegex(string pattern);
/// <summary>
/// 搜索 匹配到的缓存
/// </summary>
/// <param name="pattern"></param>
/// <returns></returns>
IList<string> SearchCacheRegex(string pattern);
}
}

using CoreCms.Net.Caching;
using CoreCms.Net.Caching.Redis;
using CoreCms.Net.Configuration;
using CoreCms.Net.Utility.Extensions;
namespace CoreCms.Net.Caching.Manual
{
/// <summary>
/// 手动缓存调用
/// </summary>
public static partial class ManualDataCache
{
private static IManualCacheManager _instance = null;
/// <summary>
/// 静态实例,外部可直接调用
/// </summary>
public static IManualCacheManager Instance
{
get
{
if (_instance == null)
{
if (AppSettingsConstVars.RedisUseCache)
{
_instance = new RedisCacheManager();
}
else
{
_instance = new MemoryCacheManager();
}
}
return _instance;
}
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using CoreCms.Net.Caching.Manual;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
namespace CoreCms.Net.Caching.Manual
{
public class MemoryCacheManager : IManualCacheManager
{
private static volatile MemoryCache _memoryCache = new MemoryCache((IOptions<MemoryCacheOptions>)new MemoryCacheOptions());
/// <summary>
/// 验证缓存项是否存在
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public bool Exists(string key)
{
if (key == null)
throw new ArgumentNullException(nameof(key));
return _memoryCache.TryGetValue(key, out _);
}
/// <summary>
/// 添加缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">缓存Value</param>
/// <param name="expiresIn">缓存时间</param>
/// <returns></returns>
public bool Set(string key, object value, int expiresIn = 0)
{
if (key == null)
throw new ArgumentNullException(nameof(key));
if (value == null)
throw new ArgumentNullException(nameof(value));
if (expiresIn > 0)
{
_memoryCache.Set(key, value,
new MemoryCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromSeconds(120))
.SetAbsoluteExpiration(TimeSpan.FromMinutes(expiresIn)));
}
else
{
_memoryCache.Set(key, value,
new MemoryCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromSeconds(120))
.SetAbsoluteExpiration(TimeSpan.FromMinutes(1440)));
}
return Exists(key);
}
/// <summary>
/// 删除缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public void Remove(string key)
{
if (key == null)
throw new ArgumentNullException(nameof(key));
_memoryCache.Remove(key);
}
/// <summary>
/// 批量删除缓存
/// </summary>
/// <returns></returns>
public void RemoveAll(IEnumerable<string> keys)
{
if (keys == null)
throw new ArgumentNullException(nameof(keys));
keys.ToList().ForEach(item => _memoryCache.Remove(item));
}
/// <summary>
/// 获取缓存对象
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public T Get<T>(string key)
{
if (key == null)
throw new ArgumentNullException(nameof(key));
return _memoryCache.Get<T>(key);
}
/// <summary>
/// 获取缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public object Get(string key)
{
if (key == null)
throw new ArgumentNullException(nameof(key));
return _memoryCache.Get(key);
}
/// <summary>
/// 获取缓存集合
/// </summary>
/// <param name="keys">缓存Key集合</param>
/// <returns></returns>
public IDictionary<string, object> GetAll(IEnumerable<string> keys)
{
if (keys == null)
throw new ArgumentNullException(nameof(keys));
var dict = new Dictionary<string, object>();
keys.ToList().ForEach(item => dict.Add(item, _memoryCache.Get(item)));
return dict;
}
/// <summary>
/// 删除所有缓存
/// </summary>
public void RemoveCacheAll()
{
var l = GetCacheKeys();
foreach (var s in l)
{
Remove(s);
}
}
/// <summary>
/// 删除匹配到的缓存
/// </summary>
/// <param name="pattern"></param>
/// <returns></returns>
public void RemoveCacheRegex(string pattern)
{
IList<string> l = SearchCacheRegex(pattern);
foreach (var s in l)
{
Remove(s);
}
}
/// <summary>
/// 搜索 匹配到的缓存
/// </summary>
/// <param name="pattern"></param>
/// <returns></returns>
public IList<string> SearchCacheRegex(string pattern)
{
var cacheKeys = GetCacheKeys();
var l = cacheKeys.Where(k => Regex.IsMatch(k, pattern)).ToList();
return l.AsReadOnly();
}
/// <summary>
/// 获取所有缓存键
/// </summary>
/// <returns></returns>
public List<string> GetCacheKeys()
{
const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
var entries = _memoryCache.GetType().GetField("_entries", flags).GetValue(_memoryCache);
var cacheItems = entries as IDictionary;
var keys = new List<string>();
if (cacheItems == null) return keys;
foreach (DictionaryEntry cacheItem in cacheItems)
{
keys.Add(cacheItem.Key.ToString());
}
return keys;
}
}
}

using System;
using System.Collections.Generic;
using System.Linq;
using CoreCms.Net.Caching.Manual;
using CoreCms.Net.Configuration;
using CoreCms.Net.Utility.Extensions;
using StackExchange.Redis;
namespace CoreCms.Net.Caching.Redis
{
public class RedisCacheManager : IManualCacheManager
{
private readonly string _redisConnenctionString;
public volatile ConnectionMultiplexer RedisConnection;
private readonly object _redisConnectionLock = new object();
#region 构造函数-获取 Redis 实例
/// <summary>
/// 构造函数-获取 Redis 实例
/// </summary>
/// <exception cref="ArgumentException"></exception>
public RedisCacheManager()
{
string redisConfiguration = AppSettingsConstVars.RedisConfigConnectionString;//获取连接字符串
if (string.IsNullOrWhiteSpace(redisConfiguration))
{
throw new ArgumentException("redis config is empty", nameof(redisConfiguration));
}
_redisConnenctionString = redisConfiguration;
RedisConnection = GetRedisConnection();
}
/// <summary>
/// 核心代码,获取连接实例
/// 通过双if 加lock的方式,实现单例模式
/// </summary>
/// <returns></returns>
private ConnectionMultiplexer GetRedisConnection()
{
//如果已经连接实例,直接返回
if (RedisConnection != null && RedisConnection.IsConnected)
{
return RedisConnection;
}
//加锁,防止异步编程中,出现单例无效的问题
lock (_redisConnectionLock)
{
if (RedisConnection != null)
{
//释放redis连接
RedisConnection.Dispose();
}
try
{
RedisConnection = ConnectionMultiplexer.Connect(_redisConnenctionString);
}
catch (Exception)
{
throw new Exception("Redis服务未启用,请开启该服务,并且请注意端口号,Redis默认使用6379端口号。");
}
}
return RedisConnection;
}
#endregion
#region 判断key是否存在
/// <summary>
/// 判断key是否存在
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public bool Exists(string key)
{
return RedisConnection.GetDatabase().KeyExists(key);
}
#endregion
#region 添加缓存
/// <summary>
/// 添加缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">缓存Value</param>
/// <param name="expiresIn">缓存时间</param>
/// <returns></returns>
public bool Set(string key, object value, int expiresIn = 0)
{
if (value != null)
{
//序列化,将object值生成RedisValue
if (expiresIn > 0)
{
return RedisConnection.GetDatabase().StringSet(key, SerializeExtensions.Serialize(value), TimeSpan.FromMinutes(expiresIn));
}
else
{
return RedisConnection.GetDatabase().StringSet(key, SerializeExtensions.Serialize(value));
}
}
return false;
}
#endregion
#region 删除缓存
/// <summary>
/// 删除缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public void Remove(string key)
{
RedisConnection.GetDatabase().KeyDelete(key);
}
/// <summary>
/// 批量删除缓存
/// </summary>
/// <returns></returns>
public void RemoveAll(IEnumerable<string> keys)
{
foreach (var key in keys)
{
RedisConnection.GetDatabase().KeyDelete(key);
}
}
#endregion
#region 获取缓存对象
/// <summary>
/// 获取缓存对象
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public T Get<T>(string key)
{
var value = RedisConnection.GetDatabase().StringGet(key);
if (value.HasValue)
{
//需要用的反序列化,将Redis存储的Byte[],进行反序列化
return SerializeExtensions.Deserialize<T>(value);
}
return default;
}
public object Get(string key)
{
return RedisConnection.GetDatabase().StringGet(key);
}
public IDictionary<string, object> GetAll(IEnumerable<string> keys)
{
if (keys == null)
throw new ArgumentNullException(nameof(keys));
var dict = new Dictionary<string, object>();
keys.ToList().ForEach(item => dict.Add(item, RedisConnection.GetDatabase().StringGet(item)));
return dict;
}
#endregion
#region 移除所有的缓存
/// <summary>
/// 移除所有的缓存
/// </summary>
public void RemoveCacheAll()
{
foreach (var endPoint in GetRedisConnection().GetEndPoints())
{
var server = GetRedisConnection().GetServer(endPoint);
foreach (var key in server.Keys())
{
RedisConnection.GetDatabase().KeyDelete(key);
}
}
}
public void RemoveCacheRegex(string pattern)
{
var script = "return redis.call('keys',@pattern)";
var prepared = LuaScript.Prepare(script);
var redisResult = RedisConnection.GetDatabase().ScriptEvaluate(prepared, new { pattern });
if (!redisResult.IsNull)
{
RedisConnection.GetDatabase().KeyDelete((RedisKey[])redisResult); //删除一组key
}
}
#endregion
#region 通过 Lua 脚本操作 Redis
/// <summary>
/// 通过 Lua 脚本操作 Redis
/// </summary>
/// <param name="pattern"></param>
/// <returns></returns>
public IList<string> SearchCacheRegex(string pattern)
{
var list = new List<String>();
var script = "return redis.call('keys',@pattern)";
var prepared = LuaScript.Prepare(script);
var dataBase = RedisConnection.GetDatabase();
if (dataBase != null)
{
var redisResult = dataBase.ScriptEvaluate(prepared, new { pattern });
if (!redisResult.IsNull)
{
foreach (var key in (RedisKey[])redisResult)
{
list.Add(dataBase.StringGet(key));
}
}
}
return list;
}
#endregion
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using SqlSugar;
namespace CoreCms.Net.Caching.SqlSugar
{
public class SqlSugarMemoryCache : ICacheService
{
MemoryCacheHelper cache = new MemoryCacheHelper();
public void Add<V>(string key, V value)
{
cache.Set(key, value);
}
public void Add<V>(string key, V value, int cacheDurationInSeconds)
{
cache.Set(key, value, cacheDurationInSeconds);
}
public bool ContainsKey<V>(string key)
{
return cache.Exists(key);
}
public V Get<V>(string key)
{
return cache.Get<V>(key);
}
public IEnumerable<string> GetAllKey<V>()
{
return cache.GetCacheKeys();
}
public V GetOrCreate<V>(string cacheKey, Func<V> create, int cacheDurationInSeconds = int.MaxValue)
{
if (cache.Exists(cacheKey))
{
return cache.Get<V>(cacheKey);
}
else
{
var result = create();
cache.Set(cacheKey, result, cacheDurationInSeconds);
return result;
}
}
public void Remove<V>(string key)
{
cache.Remove(key);
}
}
public class MemoryCacheHelper
{
private static readonly Microsoft.Extensions.Caching.Memory.MemoryCache Cache = new Microsoft.Extensions.Caching.Memory.MemoryCache(new MemoryCacheOptions());
/// <summary>
/// 验证缓存项是否存在
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public bool Exists(string key)
{
if (key == null)
throw new ArgumentNullException(nameof(key));
return Cache.TryGetValue(key, out _);
}
/// <summary>
/// 添加缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">缓存Value</param>
/// <param name="expiresSliding">滑动过期时长(如果在过期时间内有操作,则以当前时间点延长过期时间)</param>
/// <param name="expiressAbsoulte">绝对过期时长</param>
/// <returns></returns>
public bool Set(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte)
{
if (key == null)
throw new ArgumentNullException(nameof(key));
if (value == null)
throw new ArgumentNullException(nameof(value));
Cache.Set(key, value,
new MemoryCacheEntryOptions().SetSlidingExpiration(expiresSliding)
.SetAbsoluteExpiration(expiressAbsoulte));
return Exists(key);
}
/// <summary>
/// 添加缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">缓存Value</param>
/// <param name="expiresIn">缓存时长</param>
/// <param name="isSliding">是否滑动过期(如果在过期时间内有操作,则以当前时间点延长过期时间)</param>
/// <returns></returns>
public bool Set(string key, object value, TimeSpan expiresIn, bool isSliding = false)
{
if (key == null)
throw new ArgumentNullException(nameof(key));
if (value == null)
throw new ArgumentNullException(nameof(value));
Cache.Set(key, value,
isSliding
? new MemoryCacheEntryOptions().SetSlidingExpiration(expiresIn)
: new MemoryCacheEntryOptions().SetAbsoluteExpiration(expiresIn));
return Exists(key);
}
/// <summary>
/// 添加缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">缓存Value</param>
/// <returns></returns>
public void Set(string key, object value)
{
Set(key, value, TimeSpan.FromDays(1));
}
/// <summary>
/// 添加缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">缓存Value</param>
/// <param name="ts"></param>
/// <returns></returns>
public void Set(string key, object value, TimeSpan ts)
{
Set(key, value, ts, false);
}
/// <summary>
/// 添加缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="value">缓存Value</param>
/// <param name="ts"></param>
/// <returns></returns>
public void Set(string key, object value, int seconds)
{
var ts = TimeSpan.FromSeconds(seconds);
Set(key, value, ts, false);
}
/// <summary>
/// 删除缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public void Remove(string key)
{
if (key == null)
throw new ArgumentNullException(nameof(key));
Cache.Remove(key);
}
/// <summary>
/// 批量删除缓存
/// </summary>
/// <returns></returns>
public void RemoveAll(IEnumerable<string> keys)
{
if (keys == null)
throw new ArgumentNullException(nameof(keys));
keys.ToList().ForEach(item => Cache.Remove(item));
}
#region 获取缓存
/// <summary>
/// 获取缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public T Get<T>(string key)
{
if (key == null)
throw new ArgumentNullException(nameof(key));
return Cache.Get<T>(key);
}
/// <summary>
/// 获取缓存
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public object Get(string key)
{
if (key == null)
throw new ArgumentNullException(nameof(key));
return Cache.Get(key);
}
/// <summary>
/// 获取缓存集合
/// </summary>
/// <param name="keys">缓存Key集合</param>
/// <returns></returns>
public IDictionary<string, object> GetAll(IEnumerable<string> keys)
{
if (keys == null)
throw new ArgumentNullException(nameof(keys));
var dict = new Dictionary<string, object>();
keys.ToList().ForEach(item => dict.Add(item, Cache.Get(item)));
return dict;
}
#endregion
/// <summary>
/// 删除所有缓存
/// </summary>
public void RemoveCacheAll()
{
var l = GetCacheKeys();
foreach (var s in l)
{
Remove(s);
}
}
/// <summary>
/// 删除匹配到的缓存
/// </summary>
/// <param name="pattern"></param>
/// <returns></returns>
public void RemoveCacheRegex(string pattern)
{
IList<string> l = SearchCacheRegex(pattern);
foreach (var s in l)
{
Remove(s);
}
}
/// <summary>
/// 搜索 匹配到的缓存
/// </summary>
/// <param name="pattern"></param>
/// <returns></returns>
public IList<string> SearchCacheRegex(string pattern)
{
var cacheKeys = GetCacheKeys();
var l = cacheKeys.Where(k => Regex.IsMatch(k, pattern)).ToList();
return l.AsReadOnly();
}
/// <summary>
/// 获取所有缓存键
/// </summary>
/// <returns></returns>
public List<string> GetCacheKeys()
{
const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
var coherentState = Cache.GetType().GetField("_coherentState", flags).GetValue(Cache);
var entries = coherentState.GetType().GetField("_entries", flags).GetValue(coherentState);
var cacheItems = entries as IDictionary;
var keys = new List<string>();
if (cacheItems == null) return keys;
foreach (DictionaryEntry cacheItem in cacheItems)
{
keys.Add(cacheItem.Key.ToString());
}
return keys;
}
}
}
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CoreCms.Net.Caching.Redis;
using StackExchange.Redis;
using CoreCms.Net.Configuration;
namespace CoreCms.Net.Caching.SqlSugar
{
public class SqlSugarRedisCache : ICacheService
{
readonly RedisCacheManager _service = null;
public SqlSugarRedisCache()
{
_service = new RedisCacheManager(); ;
}
public void Add<TV>(string key, TV value)
{
_service.Set(key, value);
}
public void Add<TV>(string key, TV value, int cacheDurationInSeconds)
{
_service.Set(key, value, cacheDurationInSeconds);
}
public bool ContainsKey<TV>(string key)
{
return _service.Exists(key);
}
public TV Get<TV>(string key)
{
return _service.Get<TV>(key);
}
public IEnumerable<string> GetAllKey<TV>()
{
return _service.SearchCacheRegex("SqlSugarDataCache.*");
}
public TV GetOrCreate<TV>(string cacheKey, Func<TV> create, int cacheDurationInSeconds = int.MaxValue)
{
if (this.ContainsKey<TV>(cacheKey))
{
return this.Get<TV>(cacheKey);
}
else
{
var result = create();
this.Add(cacheKey, result, cacheDurationInSeconds);
return result;
}
}
public void Remove<TV>(string key)
{
_service.Remove(key);
}
}
}
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\Administrator\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.13.2</NuGetToolVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<SourceRoot Include="C:\Users\Administrator\.nuget\packages\" />
<SourceRoot Include="C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages\" />
</ItemGroup>
</Project>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<Import Project="$(NuGetPackageRoot)system.text.json\8.0.0\buildTransitive\net6.0\System.Text.Json.targets" Condition="Exists('$(NuGetPackageRoot)system.text.json\8.0.0\buildTransitive\net6.0\System.Text.Json.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\8.0.0\buildTransitive\net6.0\Microsoft.Extensions.Logging.Abstractions.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\8.0.0\buildTransitive\net6.0\Microsoft.Extensions.Logging.Abstractions.targets')" />
<Import Project="$(NuGetPackageRoot)sqlitepclraw.lib.e_sqlite3\2.1.6\buildTransitive\net8.0\SQLitePCLRaw.lib.e_sqlite3.targets" Condition="Exists('$(NuGetPackageRoot)sqlitepclraw.lib.e_sqlite3\2.1.6\buildTransitive\net8.0\SQLitePCLRaw.lib.e_sqlite3.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.extensions.options\8.0.0\buildTransitive\net6.0\Microsoft.Extensions.Options.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.options\8.0.0\buildTransitive\net6.0\Microsoft.Extensions.Options.targets')" />
</ImportGroup>
</Project>
\ No newline at end of file
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("CoreCms.Net.Caching")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
[assembly: System.Reflection.AssemblyProductAttribute("CoreCms.Net.Caching")]
[assembly: System.Reflection.AssemblyTitleAttribute("CoreCms.Net.Caching")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// 由 MSBuild WriteCodeFragment 类生成。
2b2d1b2add3298eefe8377856ab729551d184068c8b91ac3ecff8cd6ca54ec1c
is_global = true
build_property.TargetFramework = net8.0
build_property.TargetPlatformMinVersion =
build_property.UsingMicrosoftNETSdkWeb =
build_property.ProjectTypeGuids =
build_property.InvariantGlobalization =
build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = CoreCms.Net.Caching
build_property.ProjectDir = D:\Code\ShopERP.Portal\CoreCms.Net.Caching\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.EffectiveAnalysisLevelStyle = 8.0
build_property.EnableCodeStyleSeverity =
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment