第46章 自定义静态与数据库动态授权依赖注入的定义实现
1 数据库动态授权表授权原理
2 准备工作
2.1 重构Program.cs
using Framework.Infrastructure.Extensions;
var builder = WebApplication.CreateBuilder(args);
//如果启动项中不存在“appsettings.json”文件,则通过.Net(Core)的内置方法自动新建“appsettings.json”文件。
builder.Configuration.AddJsonFile("appsettings.json", true, true);
//把当前程序中所有继承了“IConfig”接口的具体实现类的实例,依赖注入到.Net(Core)内置依赖注入容器实例中,如果需要并把这些数据持久化存储到"appsettings.json"文件。
builder.Services.ConfigureApplicationSettings(builder);
//在调用以“AddScoped”注入的实例时必须的预先定义以下的定义注入到内置容器中,否则会出现逻辑异常:“Cannot consume scoped service...”
builder.Host.UseDefaultServiceProvider(options =>
{
options.ValidateScopes = false;
options.ValidateOnBuild = true;
});
//把具体现类和中间件注入到内置依赖注入容器后,并把.NetCore框架内置依赖注入容器接口实例所存储的当前程序中的具体现类和中间件的实例通过“Engine”(引擎)单例实例存储到单例类的字典属性成员实例中。
//注意:从依赖注入到.Net(Core)框架内置容器中,获取“IServiceProvider”接口实例,必须定义在最后,
//否则“GetServices”/“GetRequiredService”方法将有可能不能获取取1个指定类的实例,因为该类的实例还没有依赖注入到.Net(Core)框架内置容器中。
builder.Services.ConfigureApplicationServices(builder);
var app = builder.Build();
app.MapControllers();
// 通过.NetCore框架的内置管道接口实例,实例化“IServiceProvider”接口实例,同时把继承于“IStartup”所有具体现类中的中间件实例集成到.NetCore框架内置管道中。
app.ConfigureRequestPipeline();
app.Run();
2.2 Services.Security.PermissionService.GetPermissionRoleByNewAsync
/// <summary>
/// 【异步获取新建权限角色映射】
/// <remarks>
/// 摘要:
/// 通过新建操作构建所有的限角色映射实例,这些构建后的实例存储到列表实例中,从而为授权操作提供数据支撑。
/// </remarks>
/// <returns>
/// 列表实例,该实例存储着新构建的所有的限角色映射实例。
/// </returns>
/// </summary>
public async Task<IList<PermissionRole>> GetPermissionRoleByNewAsync()
{
return await _permissionRoleRepository.GetEntitiesAsync(async () =>
{
var query = from permissionRole in _permissionRoleRepository.Table
join permission in _permissionRepository.Table on permissionRole.PermissionId equals permission.Id
join role in _roleRepository.Table on permissionRole.RoleId equals role.Id
select new PermissionRole { PermissionId = permission.Id, RoleId = role.Id, PermissionSingle = permission, RoleSingle = role };
return await query.ToListAsync();
}, cache => _staticCacheManager.PrepareKeyForDefaultCache(new("permission.all.permissionRoleByNew")));
}
3 自定义静态与数据库动态授权依赖注入的定义实现
3.1 Framework.Filters.PermissionsPolicy
namespace Framework.Filters
{
/// <summary>
/// 【授权策略--类】
/// <remarks>
/// 摘要:
/// 通过该实体类中的属性成员, 为数据库动态授权设定1个指定常量。
/// </remarks>
/// </summary>
public class PermissionsPolicy
{
/// <summary>
/// 【数据库动态授权策略】
/// <remarks>
/// 摘要:
/// 为数据库动态授权设定1个指定常量。
/// 注意:
/// 授权必须被限定为常量(const),否则在标记时会出现语法异常。
/// </remarks>
/// </summary>
public const string Name = "PermissionByDatabase";
}
}
3.2 Framework.Filters.PermissionPolicyItem
namespace Framework.Filters
{
/// <summary>
/// 【授权策略项--类】
/// <remarks>
/// 摘要:
/// 通过该实体类中的属性成员, 为数据库动态授权提供数据支撑,或对已经被授权的权限、角色、菜单进行存储。
/// </remarks>
/// </summary>
public class PermissionPolicyItem
{
/// <summary>
/// 【菜单编号】
/// <remarks>
/// 摘要:
/// 获取/设置菜单实体1个指定实例的长整型编号值,该编号值为指定菜单的数据库动态授权的验证提供数据支撑。
/// </remarks>
/// </summary>
public long? MenuId { get; set; }
/// <summary>
/// 【角色名】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定的角色名,该角色名用于对数据库动态授权的验证提供数据支撑。
/// </remarks>
/// </summary>
public virtual string RoleName { get; set; }
/// <summary>
/// 【URL】
/// <remarks>
/// 摘要:
/// 获取/设置1个相对路由字符串(由1个指定控制器中1个指行为方法拼接而且成),该路由字符串用于对数据库动态授权的验证提供数据支撑。
/// </remarks>
/// </summary>
public string Url { get; set; }
}
}
3.3 Framework.Filters.PermissionPolicyRequirement
using Microsoft.AspNetCore.Authorization;
using Microsoft.IdentityModel.Tokens;
namespace Framework.Filters
{
/// <summary>
/// 【授权策略必要数据--类】
/// <remarks>
/// 摘要:
/// 通过该实体类中的属性成员,为自定义依赖注入数据库动态授权中间件的实现提供数据支撑,或对已经被授权的权限、角色、菜单进行存储。
/// </remarks>
/// </summary>
public class PermissionPolicyRequirement : IAuthorizationRequirement
{
#region 拷贝构造方法
/// <param name="deniedAction">当拒绝1个指定用户的授权操作后,1个即将要跳转的控制器行为方法,该行为方法为拒绝授权操作提供解释性信息。</param>
/// <param name="permissionPolicyItemList">列表实例,该实例存储着授权策略项类的1/n实例。</param>
/// <param name="claimType">授权单元类型的1指定实例(这里特指:角色授权单元类型,即通过验证1个指定用户所属的所有角色实例,来验证是否具有权限)。</param>
/// <param name="issuer">用于生成所有令牌(Token)字符串实例,提供数据支撑的“签发机关”。</param>
/// <param name="audience">用于生成所有令牌(Token)字符串实例,提供数据支撑的“订阅者”。</param>
/// <param name="signingCredentials">1个指定令牌(Token)字符串的凭据/证书实例(令牌(Token)字符串的凭据/证书实例与HTTPS协议运行所需要的CA证书基本原理一样,都是通过哈希(Hash)算法生成的)。</param>
/// <param name="expiration">1个指定用户的令牌(Token)字符串的过期时间(数据库动态授权的过期时间)。</param>
/// <summary>
/// 【拷贝构造方法】
/// <remarks>
/// 摘要:
/// 通过拷贝构造方法实例化该类中的同1类型的属性成员。
/// </remarks>
/// </summary>
public PermissionPolicyRequirement(string deniedAction,
List<PermissionPolicyItem> permissionPolicyItemList,
string claimType,
string issuer,
string audience,
SigningCredentials signingCredentials,
TimeSpan expiration)
{
ClaimType = claimType;
DeniedAction = deniedAction;
PermissionPolicyItemList = permissionPolicyItemList;
Issuer = issuer;
Audience = audience;
Expiration = expiration;
SigningCredentials = signingCredentials;
}
#endregion
#region 属性
/// <summary>
/// 【授权策略项列表】
/// <remarks>
/// 摘要:
/// 获取/设置列表实例,该实例存储着授权策略项类的1/n实例。
/// </remarks>
/// </summary>
public List<PermissionPolicyItem> PermissionPolicyItemList { get; set; }
/// <summary>
/// 【拒绝行为方法】
/// <remarks>
/// 摘要:
/// 当拒绝1个指定用户的授权操作后,获取/设置1个即将要跳转的控制器行为方法,该行为方法为拒绝授权操作提供解释性信息。
/// </remarks>
/// </summary>
public string DeniedAction { get; set; }
/// <summary>
/// 【授权单元类型】
/// <remarks>
/// 摘要:
/// 获取/设置授权单元类型的1指定实例(这里特指:角色授权单元类型,即通过验证1个指定用户所属的所有角色实例,来验证是否具有权限)。
/// </remarks>
/// </summary>
public string ClaimType { internal get; set; }
/// <summary>
/// 【登录路径】
/// <remarks>
/// 摘要:
/// 获取/设置登录控制器行为方法的路由字符串,当访问1个需要授权才能访问的控制器行为方法时,如果用户没有执行登录操作,或该用户不具有访问权限,为跳转到登录页面提供数据支撑。
/// </remarks>
/// </summary>
public string LoginPath { get; set; } = "/Customer/Login";
/// <summary>
/// 【签发机关】
/// <remarks>
/// 摘要:
/// 获取/设置用于生成所有令牌(Token)字符串实例,提供数据支撑的“签发机关”。
/// </remarks>
/// </summary>
public string Issuer { get; set; }
/// <summary>
/// 【订阅者】
/// <remarks>
/// 摘要:
/// 获取/设置用于生成所有令牌(Token)字符串实例,提供数据支撑的“订阅者”。
/// </remarks>
/// </summary>
public string Audience { get; set; }
/// <summary>
/// 【过期时间】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定用户的令牌(Token)字符串的过期时间(数据库动态授权的过期时间)。
/// </remarks>
/// </summary>
public TimeSpan Expiration { get; set; }
/// <summary>
/// 【凭据/证书】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定令牌(Token)字符串的凭据/证书实例(令牌(Token)字符串的凭据/证书实例与HTTPS协议运行所需要的CA证书基本原理一样,都是通过哈希(Hash)算法生成的)。
/// </remarks>
/// </summary>
public SigningCredentials SigningCredentials { get; set; }
#endregion
}
}
3.4 Framework.Filters.PermissionPolicyHandler
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Services.Security;
using System.Security.Claims;
using System.Text.RegularExpressions;
namespace Framework.Filters
{
/// <summary>
/// 【授权策略处理器--类】
/// <remarks>
/// 摘要:
/// 通过该实体类中的方法成员来验证1个指定用户所属的所有角色实例都不拥有访问指定控制器行为方法的权限,如果没有则跳转到登录页面或授权拒绝页面;如有则执行指定控制器行为方法。
/// </remarks>
/// </summary>
public class PermissionPolicyHandler : AuthorizationHandler<PermissionPolicyRequirement>
{
#region 拷贝构造方法与变量
/// <summary>
/// 【授权方案】
/// <remarks>
/// 摘要:
/// 获取/设置身份认证方案提供程序接口的1个指定实例(这里特指:JwtBearer身份认证)。
/// </remarks>
/// </summary>
public IAuthenticationSchemeProvider Schemes { get; set; }
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IPermissionService _permissionService;
/// <summary>
/// 【拷贝构建方法】
/// <remarks>
/// 摘要:
/// 依赖注入容器通过拷贝构造方法,实例化该类中的变量成员。
/// </remarks>
/// </summary>
public PermissionPolicyHandler(IAuthenticationSchemeProvider schemes,
IHttpContextAccessor httpContextAccessor,
IPermissionService permissionService)
{
Schemes = schemes;
_httpContextAccessor = httpContextAccessor;
_permissionService = permissionService;
}
#endregion
#region 方法--接口实现--覆写
/// <param name="context">.Net(Core)框架内置依赖注入容器实例。</param>
/// <param name="requirement">.Net(Core)框架内置依赖注入容器实例。</param>
/// <summary>
/// 【异步必要处理】
/// <remarks>
/// 摘要:
/// 通过该方法来验证1个指定用户所属的所有角色实例都不拥有访问指定控制器行为方法的权限,如果没有则跳转到登录页面或授权拒绝页面;如有则执行指定控制器行为方法。
/// </remarks>
/// </summary>
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionPolicyRequirement requirement)
{
var httpContext = _httpContextAccessor.HttpContext;
if (!requirement.PermissionPolicyItemList.Any())
{
List<PermissionPolicyItem> _permissionItemList = new List<PermissionPolicyItem>();
var data = await _permissionService.GetPermissionRoleByNewAsync();
_permissionItemList = data.Select(d => new PermissionPolicyItem
{
RoleName = d.RoleSingle.Name,
Url = d.PermissionSingle.Url,
MenuId = d.PermissionSingle.MenuId,
}).ToList();
if (httpContext != null)
{
var questUrl = httpContext.Request.Path.Value!.ToLower();
// 整体结构类似认证中间件UseAuthentication的逻辑,具体查看开源地址
// https://github.com/dotnet/aspnetcore/blob/master/src/Security/Authentication/Core/src/AuthenticationMiddleware.cs
httpContext.Features.Set<IAuthenticationFeature>(
new AuthenticationFeature
{
OriginalPath = httpContext.Request.Path,
OriginalPathBase = httpContext.Request.PathBase
});
// Give any IAuthenticationRequestHandler schemes a chance to handle the request
// 主要作用是: 判断当前是否需要进行远程验证,如果是就进行远程验证
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;
// 获取1个指定用户所属的所有角色实例。
var currentUserRoles = new List<string>();
currentUserRoles = (from item in httpContext.User.Claims
where item.Type == requirement.ClaimType
select item.Value).ToList();
//验证1个指定用户所属的所有角色实例都不拥有访问指定控制器行为方法的权限。
bool _isMatchRole = false;
var permisssionRoles = _permissionItemList.Where(w => currentUserRoles.Contains(w.RoleName));
foreach (var item in permisssionRoles)
{
try
{
//验证1个指定用户所属的所有角色实例是否有访问当前控制器行为方法的权限。
if (Regex.Match(questUrl, item.Url.ToLower().Trim()).Value == questUrl)
{
_isMatchRole = true;
break;
}
}
catch (Exception)
{
}
}
//如果1个指定用户没有相应的角色实例,或该指定用户所属的所有角色实例都不拥有访问指定控制器行为方法的权限。
if (currentUserRoles.Count <= 0 || !_isMatchRole)
{
context.Fail();
return;
}
//验证1个指定用户当前Token(令牌)是否已经过期。
bool _isExpiration = false;
_isExpiration = (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 (!_isExpiration)
{
context.Fail(new AuthorizationFailureReason(this, "授权已过期,请重新授权"));
return;
}
context.Succeed(requirement);
return;
}
}
//判断没有登录时,是否访问登录的url,并且是Post请求,并且是form表单提交类型,否则为失败。
if (!questUrl.Equals(requirement.LoginPath.ToLower(), StringComparison.Ordinal) && (!httpContext.Request.Method.Equals("POST")
|| !httpContext.Request.HasFormContentType))
{
context.Fail();
return;
}
}
context.Succeed(requirement);
}
}
#endregion
}
}
4 抽离自定义静态与数据库动态授权依赖注入
4.1 Framework.Infrastructure.Extensions.ServiceCollectionExtensions. AddAuthorizationByDatabase
/// <param name="services">.Net(Core)框架内置依赖注入容器实例。</param>
/// <summary>
/// 【添加自定义数据库动态授权中间件】
/// <remarks>
/// 摘要:
/// 抽离自定义数据库动态授权中间件实例的依赖注入操作,为当前程序支持数据库动态授权提供方法支撑。
/// </remarks>
/// </summary>
public static void AddAuthorizationByDatabase(this IServiceCollection services)
{
//写死的授权策略(静态授权策略),使用方式例如:[Authorize(Policy = "OnlyInAdministrator")]
//说明:静态授权策略由于定义实现相对简单,一般于测试,在工程性程序中基本不会被使用。
services.AddAuthorization(options =>
{
// "包含所有权限,即用户不受权限限制(映射角色:Administrator,Register,Guest= [AllowAnonymous])"
options.AddPolicy("PermissionAll", policy => policy.RequireRole("Administrator", "Register", "Guest").Build());
options.AddPolicy("AdministratorAndRegister", policy => policy.RequireRole("Administrator", "Register"));
options.AddPolicy("OnlyInAdministrator", policy => policy.RequireRole("Administrator"));
options.AddPolicy("OnlyInRegister", policy => policy.RequireRole("Register"));
});
//从单例实例的字典成员实例中获取当前程序所有配置相关数据。
AppSettings _appSettings = Singleton<AppSettings>.Instance;
//从应用配置类实例中获取JwtBearer身份认证相关数据。
JwtBearerConfig _jwtBearerConfig = _appSettings.Get<JwtBearerConfig>();
//获取秘钥。
SymmetricSecurityKey _symmetricSecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtBearerConfig.SecretKey));
//获取1个指定令牌(Token)字符串的凭据/证书实例(令牌(Token)字符串的凭据/证书实例与HTTPS协议运行所需要的CA证书基本原理一样,都是通过哈希(Hash)算法生成的)。
SigningCredentials _signingCredentials = new SigningCredentials(_symmetricSecurityKey, SecurityAlgorithms.HmacSha256);
//自定义数据库动态授权策略。
var permission = new List<PermissionPolicyItem>();
// 设定授权策略必要数据类的1个指定实例
var permissionPolicyRequirement = new PermissionPolicyRequirement(
"/api/denied",//当拒绝1个指定用户的授权操作后,1个即将要跳转的控制器行为方法,该行为方法为拒绝授权操作提供解释性信息(目前无用)
permission,
ClaimTypes.Role,//基于角色的授权
_jwtBearerConfig.Issuer,//发行人
_jwtBearerConfig.Audience,//听众
_signingCredentials,//签名凭据
expiration: TimeSpan.FromSeconds(60 * 60)//数据库动态授权的过期时间
);
//1个指定的控制器行为方法通过对授权表动态的对验证授权权策略.
services.AddAuthorization(options =>
{
//调用数据库动态授权定义实现。
options.AddPolicy(PermissionsPolicy.Name,
policy => policy.Requirements.Add(permissionPolicyRequirement));
});
services.AddSingleton<IAuthorizationHandler, PermissionPolicyHandler>();
// 将授权必要类注入生命周期内
services.AddSingleton(permissionPolicyRequirement);
}
4.2 重构Framework.Infrastructure.AuthorizationStartup.ConfigureServices
/// <param name="services">.Net(Core)框架内置依赖注入容器实例。</param>
/// <param name="configuration">.NetCore框架内置配置接口实例(存储着当前程序中所有*.json文件中的数据)。</param>
/// <summary>
/// 【配置服务】
/// <remarks>
/// 摘要:
/// 把自定义依赖注入数据库动态授权中间件注入到内置依赖注入容器中。
/// </remarks>
/// </summary>
public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
services.AddAuthorizationByDatabase();
}
注意:
必须进行重构否则错误状态码为:500
4.3 重构WebApi.Controllers.CustomerController
using Core.Configuration;
using Core.Domain.Customers;
using Core.Infrastructure;
using Framework.Filters;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using Services.Customers;
using Services.Security;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using WebApi.Models;
namespace WebApi.Controllers
{
[Route("[controller]/[action]")]
[ApiController]
public class CustomerController : ControllerBase
{
#region 拷贝构造方法与变量
private readonly ICustomerService _customerService;
private readonly IEncryptionService _encryptionService;
private readonly IPermissionService _permissionService;
/// <summary>
/// 【拷贝构建方法】
/// <remarks>
/// 摘要:
/// 依赖注入容器通过拷贝构造方法,实例化该类中的变量成员。
/// </remarks>
/// </summary>
public CustomerController(
ICustomerService customerService,
IEncryptionService encryptionService,
IPermissionService permissionService)
{
_customerService = customerService;
_encryptionService = encryptionService;
_permissionService = permissionService;
}
#endregion
/// <param name="login">登录模型记录的1个指定实例。</param>
/// <summary>
/// 【登录--无需权限】
/// </summary>
/// <remarks>
/// 摘要:
/// 通过登录操作获取1个指定用户的1个指定令牌(Token)字符串实例,为访问指定权限的Api提供数据支撑。
/// </remarks>
/// <returns>
/// 返回:
/// 1个指定用户的1个指定令牌(Token)字符串实例。
/// </returns>
[HttpPost]
public async Task<MessageModel<TokenViewModel>> Login([FromBody] LoginModel login)
{
if (await _customerService.GetCustomerByEmailAsync(login.Email) != null)
{
if (await _customerService.ValidateCustomerAsync(login.Email, login.Password))
{
Customer _customer = await _customerService.GetCustomerByEmailAsync(login.Email);
string _roleList = await _customerService.GetRoleNameByCustomerIdAsync(_customer.Id);
var claims = new List<Claim> {
new Claim(ClaimTypes.Email, _customer.Email),
new Claim(JwtRegisteredClaimNames.Jti, _customer.Id.ToString()),
new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(TimeSpan.FromSeconds(60 * 60).TotalSeconds).ToString()) };
claims.AddRange(_roleList.Split(',').Select(s => new Claim(ClaimTypes.Role, s)));
//从单例实例的字典成员实例中获取当前程序所有配置相关数据。
AppSettings _appSettings = Singleton<AppSettings>.Instance;
//从应用配置类实例中获取JwtBearer身份认证相关数据。
JwtBearerConfig _jwtBearerConfig = _appSettings.Get<JwtBearerConfig>();
//获取秘钥。
SymmetricSecurityKey _symmetricSecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_jwtBearerConfig.SecretKey));
//通过1个指定的加密算法名称,实例化。
SigningCredentials _signingCredentials = new SigningCredentials(_symmetricSecurityKey, SecurityAlgorithms.HmacSha256);
JwtSecurityToken _jwtSecurityToken = new JwtSecurityToken(
issuer: _jwtBearerConfig.Issuer,
audience: _jwtBearerConfig.Audience,
claims: claims,
notBefore: DateTime.Now,
expires: DateTime.Now.Add(TimeSpan.FromSeconds(60 * 60)),
signingCredentials: _signingCredentials
);
string _token = new JwtSecurityTokenHandler().WriteToken(_jwtSecurityToken);
TokenViewModel _tokenViewModel = new TokenViewModel
{
Success = true,
Token = _token,
ExpiresIn = TimeSpan.FromSeconds(60 * 60).TotalSeconds,
TokenType = "Bearer"//基于JwtBearer身份认证方式。
};
return MessageModel<TokenViewModel>.GetSuccess("成功登录!", _tokenViewModel);
}
}
return MessageModel<TokenViewModel>.Fail("登录失败!", 500);
}
//[Authorize(Policy = "OnlyInAdministrator")]
[Authorize(PermissionsPolicy.Name)]
[HttpPost]
public async Task<MessageModel<bool>> Index([FromBody] PaginationModel pagination)
{
return MessageModel<bool>.GetSuccess("成功获取所有用户!", true);
}
//[Authorize(Policy = "AdministratorAndRegister")]
[Authorize(PermissionsPolicy.Name)]
[HttpGet]
public async Task<MessageModel<bool>> Info()
{
return MessageModel<bool>.GetSuccess("成功获取单个用户!", true);
}
[HttpGet]
public async Task<MessageModel<bool>> CreateGet()
{
return MessageModel<bool>.GetSuccess("成功添加单个用户显示!", true);
}
[HttpPost]
public async Task<MessageModel<bool>> CreatePost()
{
return MessageModel<bool>.GetSuccess("成功添加单个用户提交!", true);
}
[HttpGet]
public async Task<MessageModel<bool>> EditGet()
{
return MessageModel<bool>.GetSuccess("成功修改单个用户显示!", true);
}
[HttpPut]
public async Task<MessageModel<bool>> EditPut()
{
return MessageModel<bool>.GetSuccess("成功修改单个用户提交!", true);
}
[HttpGet]
public async Task<MessageModel<bool>> ChangePasswordGet()
{
return MessageModel<bool>.GetSuccess("成功密码修改显示!", true);
}
[HttpPut]
public async Task<MessageModel<bool>> ChangePasswordPut()
{
return MessageModel<bool>.GetSuccess("成功密码修改提交!", true);
}
[HttpDelete]
public async Task<MessageModel<bool>> Delete()
{
return MessageModel<bool>.GetSuccess("成功删除单个用户!", true);
}
[HttpDelete]
public async Task<MessageModel<bool>> DeleteSelected()
{
return MessageModel<bool>.GetSuccess("成功删除所选用户!", true);
}
}
}
5 调试
6 授权的横切特性调用与方法内置调用
授权的调用方式有两种:
第一种就是上述例子中通过横切特性来实现对控制器行为方法的权限过滤。
第二种是定义一个布尔型的授权方法,而控制器行为方法通过内置调用该布尔型的授权方法实现权限过滤。
不管是第一种权限过滤方案,还第二种权限过滤方案,其实质都是对控制器行为方法进行权限限定,其区别是:
控制器行为方法可以对权限限定进行集中管理(细粒度更大),即把横切特性标记到控件器上,这种方式更加简便,以最少修改代码的方式实现权限限定;但是第二种方案就做不到。但第二种方案也有自己的优势,即控制器行为方法可以直接返回确且的权限限定操作信息,这是是第一种方案就做不到的。
对以上功能更为具体实现和注释见:230218_040shopDemo(自定义静态与数据库动态授权依赖注入的定义实现)。
相关文章:
第46章 自定义静态与数据库动态授权依赖注入的定义实现
1 数据库动态授权表授权原理 2 准备工作 2.1 重构Program.cs using Framework.Infrastructure.Extensions; var builder WebApplication.CreateBuilder(args); //如果启动项中不存在“appsettings.json”文件,则通过.Net(Core)的内置方法自动新建“appsettings.…...
Go语言面试题
请解释 Go 语言中的 goroutine 是什么。请解释 Go 语言中的 channel 是什么,并举例说明它的用途。请解释 Go 语言中的 interface 是什么,并举例说明它的用途。请解释 Go 语言中的 map 和 slice 是什么,并举例说明它们的用途。请解释 Go 语言中…...
Kubernetes入门级教程
Kubernetes入门级教程1. Introduction1.1 概述1.2 关键字介绍2. Cluster Install2.1 Big Data -- Postgres3. 基础知识3.1 Pod3.2 控制器3.3 通讯模式3.4 服务发现4. Command4.0 编辑文件4.1 在宿主机执行命令4.2 创建资源对象4.3 查询资源对象4.4 查询资源描述4.5 修改资源4.6…...
15个顶级思维模型
今天给大家分享几个思维模型,无论是数分、数开还是其他岗位,都会有所启发。 查理芒格认为,每个学科都是从一个独特的角度去切入了解这个世界,都是一个摸象的瞎子。 要超越普通人的认知决策,就必须掌握多个核心思维模…...
外贸谷歌优化,外贸google SEO优化费用是多少?
本文主要分享关于做外贸网站的谷歌seo成本到底需要投入多少这一件事。 本文由光算创作,有可能会被剽窃和修改,我们佛系对待这种行为吧。 那么外贸google SEO优化费用是多少? 答案是:2w~25w。 好,看到这个答案先别激…...
华为OD机试 - 统计匹配的二元组个数(Python) | 机试题算法思路
最近更新的博客 华为OD机试 - 招聘(Python) | 备考思路,刷题要点,答疑 【新解法】华为OD机试 - 五键键盘 | 备考思路,刷题要点,答疑 【新解法】华为OD机试 - 热点网络统计 | 备考思路,刷题要点,答疑 【新解法】华为OD机试 - 路灯照明 | 备考思路,刷题要点,答疑 【新解…...
Java 日志简介
目录1、Slf4j2、Log4j3、LogBack4、Logback 优点5、ELK1、Slf4j slf4j 的全称是 Simple Loging Facade For Java,即它仅仅是一个为 Java 程序提供日志输出的统一接口,并不是一个具体的日志实现方案,就比如 JDBC 一样,只是一种规则…...
HTTPS协议原理---详解六个加密方案
目录 一、HTTPS 1.加密与解密 2.我们为什么要加密? 3.常见加密方式 ①对称加密 ②非对称加密 4.数据摘要 5.数字签名 二、HTTPS的加密方案 1.只是用对称加密 2.只使用非对称加密 3.双方都使用非对称加密 4.非对称加密+对称加密 中间人攻…...
纯css实现坤坤经典动作-“铁山靠”
背景 2023年2月16日,晴,今天没有工作,一直在掘金摸鱼,摸的我好累。 不行!我得找点有意义的事情做! 此时间,我发的一条沸点竟然有小黑子给我评论,\ 我看到之后气不打一处来ÿ…...
Linux 操作系统原理 — NUMA 体系结构
目录 文章目录 目录NUMA 体系结构NUMA 的基本概念查看 Host 的 NUMA TopologyBash 脚本DPDK 脚步NUMA 体系结构 NUMA(Non-Uniform Memory Access,非一致性存储器访问)的设计理念是将 CPU 和 Main Memory 进行分区自治(Local NUMA node),又可以跨区合作(Remote NUMA nod…...
cesium学习记录01
1,将右弦GISer的cesium实战系列的大部分功能(25-110) 都又跟着走了一遍(大部分是CTRL CCTRL V) 2,代码SVN地址(用户名:liu 密码:123) (如果我没有遗漏上传…...
Linux延时队列工作原理与实现
当进程要获取某些资源(例如从网卡读取数据)的时候,但资源并没有准备好(例如网卡还没接收到数据),这时候内核必须切换到其他进程运行,直到资源准备好再唤醒进程。 waitqueue (等待队列) 就是内核…...
【Python】scipy稀疏矩阵的奇异值分解svds
文章目录基本原理scipy实现测试基本原理 当AAA是方阵时,可以很容易地进行特征分解:AWΣW−1AW\Sigma W^{-1}AWΣW−1,其中Σ\SigmaΣ是AAA的特征值组成的对角矩阵。如果WWW由标准正交基组成,则W−1WTW^{-1}W^TW−1WT,…...
网络安全等级保护基础知识汇总
等保 全称是网络安全等级保护,分为两个阶段 等保1.0 1994年国务院147令《中华人民共和国计算机信息系统安全保护条例》 等保2.0 2017年 网络安全法,21条规定的 国家实行网络安全等级保护制度,等保进入了有法可依阶段。 2019年国标22239-2019版…...
ros1使用过程中遇到的问题记录
Failed to fetch current robot state如果使用的是moveit助手生成的demo.launch文件启动机械臂的话,应该是其他在运行的自己写的节点代码中少了spin函数,因为getCurrentPose函数依赖于spin,也可以使用AsyncSpinner。具体看下面这个链接https:…...
centos7给已有分区进行扩容
1、背景 最近我在虚拟机上安装软件,发现磁盘空间不足,通过上网查找资料,发现可以通过如下方法进行磁盘扩容,此处进行记录一下。 2、实现扩容 1、虚拟机上添加一个新的硬盘 2、查看我们刚刚加入的硬盘 此处我们可以看到/dev/nvm…...
package.json
{"name": "project-name", 项目名字"version": "0.1.0", 版本号"private": true, 项目包,不需要发版"scripts": { 脚本"serve": "vue-cli-service serve", 运行命令后缀是 se…...
【项目精选】户籍管理系统(视频+论文+源码)
点击下载源码 当今社会人们生活质量越来越高,人们对生活品质的追求不断提升,对于孩子求学,变更住所等情况时有发生,因此对于户籍变动管理就显得十分重要,管理用户的户籍信息可以有效防止信息错乱,信息管理过…...
【IP技术】网络安全防护措施
网络安全威胁造成的形式主要包含运用系统软件缺点或侧门,运用网络防火墙安全隐患,内部结构客户的泄密、泄露和毁坏,动态口令进攻和拒绝服务式攻击等。针对该网络安全威胁,现阶段的预防措施主要有五种:1.访问控制技术&a…...
基于AIOT技术的智慧教室智能物联管控系统设计与实现(提纲)
摘要随着物联网技术的不断发展和智能化的不断推进,智慧教室已经成为现代教育中不可或缺的一部分。本文提出了一种基于AIOT技术的智慧教室智能物联管控系统设计与实现方案,该方案集成了物联网技术、人工智能技术、大数据技术和云计算技术等先进技术&#…...
C 指针的深造
C 指针1 关于内存那点事2 指针的概念3 指针变量的定义方法4 指针的分类5 指针和变量的关系6 指针和数组元素之间的关系7 指针数组8 指针的指针9 字符串和指针9.1 字符串的定义9.2 字符串的可修改性:9.3 初始化赋值9.4 使用时赋值9.5 字符串和指针总结10 数组指针11 …...
大数据之-Nifi-应用场景2-2_设置putfile处理器自动创建目标文件夹_以及存在重复文件时自动覆盖---大数据之Nifi工作笔记0006
上一节我们留了两个问题,一个是,如果我们没有创建putfile要写入的目标文件夹,会报错吗? 可以看到我们putfile目标文件夹是上面这个目录 我们来试一试,如果目标文件夹不存在,putfile处理器会自动创建吗 首先我们删除这个target目标文件夹 然后我们进入cd source目录,源文件夹目…...
buuctf Web 下
9.[ACTF2020 新生赛]Exec 访问url: http://cc3c6c27-e2df-4665-baba-1d9a32dc963e.node3.buuoj.cn/ 首页如下: 直接ping ip可以得到结果 常见管道符 1、|(就是按位或),直接执行|后面的语句 127.0.0.1 | cat /flag…...
【项目精选】javaEE土地档案管理系统(源码+论文+视频)
技术:java、jsp、struts、spring、hibernate 数据库:oracle 集成开发工具:eclipse 点击下载源码 本土地项目管理系统在可行性研究的基础上,是为了进一步明确土地项目管理系统的软件需求,以便安排项目规划和进度&#x…...
JVM那些事——垃圾回收和内存分配
内存分配 默认情况下新生代和老年区的内存比例是1:2,新生代中Eden区和Survivor区的比例是8:1。 对象优先分配在Eden区。大对象直接进入老年区。通过-XX:PertenureizeThreshold参数设置临界值。长期存活的对象进入老年区。对象每熬过一次Minor GC,年龄1&…...
什么牌的运动耳机比较好、运动耳机排行榜10强
现在运动健身的潮流持续不下,而且人们长期坐于办公室办公,严重影响身体的健康,这时不论是去健身房锻炼,还是户外跑步都是非常必要的了,而蓝牙耳机作为运动必备的一款数码产品,更是受到了大家的青睐…...
华为OD机试题 - N 进制减法(JavaScript)
最近更新的博客 2023新华为OD机试题 - 斗地主(JavaScript)2023新华为OD机试题 - 箱子之形摆放(JavaScript)2023新华为OD机试题 - 考古学家(JavaScript)2023新华为OD机试题 - 相同数字的积木游戏 1(JavaScript)2023新华为OD机试题 - 最多等和不相交连续子序列(JavaScri…...
MyBatis 之三(查询操作 占位符#{} 与 ${}、like查询、resultMap、association、collection)
文章目录1. 参数占位符 #{} 和 ${} 的区别2. ${} 的优点3. SQL 注入问题4. like 查询5. 返回字典映射:resultMap6. 一对一查询:association7. 一对多查询:collection回顾一下,在上一篇 MyBatis 之二(增、删、改操作&am…...
【云原生之Docker实战】使用Docker部署Web在线聊天室Rocket.Chat
【云原生之Docker实战】使用Docker部署Web在线聊天室Rocket.Chat 一、Rocket.Chat介绍二、检查本地系统环境1.检查系统版本2.检查docker版本3.检查docker状态4.检查docker compose版本三、下载Rocket.Chat镜像四、部署Rocket.Chat1.创建部署目录2.编辑docker-compose.yaml文件3…...
阿里一面:谈一下你对DDD的理解?2W字,帮你实现DDD自由
说在前面 在微服务的应用开发中,DDD 用得越来越普及。 在40岁老架构师 尼恩的读者交流群(50)中,DDD是一个非常、非常高频的交流话题。 最近,有小伙伴面试阿里时,遇到一个面试题: 谈谈你对DDD的理解? 小伙…...
网站关键字优化地点/百度广告推广电话
从选择Java GPL许可方式开源,到扩展X86产品线,到现在的股票代码由“SUNW”改变为“JAVA”,施瓦兹正在向我们展现一个全新的、灵活的而不是食古不化的Sun。IT 需要的是创新、创新、再创新,任何固执己见的家伙注定要被淘汰。在大家都…...
团购网站建设方案/滕州网站建设优化
这几天在慢慢的巩固android基础方面的知识,对activity的生命周期和appliaction总是有点晕,所以特别做了点思考。 1 首先附一张activity的生命周期图 比较形象的说明了activity启动的时候所调用的方法,但当activity被不同的方式结束时…...
wordpress摘要字数的插件/深圳市前十的互联网推广公司
原文地址:http://ec.zdnet.com.cn/managesoft/2009/1104/1503211.shtml 凡是比较或者评测的文章,必然会得到很多的非议,因为每个人的标准都不一样,而且本人也不可能对每个系统都研究透彻。本文整理了18家比较常见的OA产品…...
网站开发与程序开发/上优化seo
T-CODE: MASS 批量更改MASS_CHARVAL 特征的批量维护MASS_EINE 信息记录的成批维护MASS_EKKO 采购订单的成批维护MASS_MARC 后勤/配送的成批维护MASS_MEAN 全球贸易项目编号的批量维护MASS_VENDOR 供应商的成批维护MASSD 批量维护MASSOBJ 维护大量维护对象MASSS2V 将系统变式复制…...
做卖东西的网站/适合小学生摘抄的新闻2022年
💂 个人网站:【海拥】【摸鱼游戏】【神级源码资源网】🤟 前端学习课程:👉【28个案例趣学前端】【400个JS面试题】💅 想寻找共同学习交流、摸鱼划水的小伙伴,请点击【摸鱼学习交流群】 随着数字化时代的到来…...
与国外公司合作网站建设上海公司/网站seo优化分析
原文:在WPF中自定义你的绘制(四) 在WPF中自定义你的绘制(四) 周银辉1,利用路径绘制图形(PathGeometry)有时我们需要绘制的图形可…...