ASP.NET Core 使用 JWT 自定义角色/策略授权需要实现的接口

目录

  • ① 存储角色/用户所能访问的 API

  • ② 实现 IAuthorizationRequirement 接口

  • ③ 实现 TokenValidationParameters

  • ④ 生成 Token

  • ⑤ 实现服务注入和身份认证配置

  • ⑥ 实现登陆

  • ⑦ 添加 API 授权策略

  • ⑧ 实现自定义授权校验

  • ⑨ 一些有用的代码

① 存储角色/用户所能访问的 API

例如 使用 List<ApiPermission>存储角色的授权 API 列表。

可有可无。

可以把授权访问的 API 存放到 Token 中,Token 也可以只存放角色信息和用户身份信息。

    /// &lt;summary&gt;/// API/// &lt;/summary&gt;public class ApiPermission{/// &lt;summary&gt;/// API名称/// &lt;/summary&gt;public virtual string Name { get; set; }/// &lt;summary&gt;/// API地址/// &lt;/summary&gt;public virtual string Url { get; set; }}

② 实现 IAuthorizationRequirement 接口

IAuthorizationRequirement 接口代表了用户的身份信息,作为认证校验、授权校验使用。

事实上,IAuthorizationRequirement 没有任何要实现的内容。

namespace Microsoft.AspNetCore.Authorization
{//// 摘要://     Represents an authorization requirement.public interface IAuthorizationRequirement{}
}

实现 IAuthorizationRequirement ,可以任意定义需要的属性,这些会作为自定义验证的便利手段。

    //IAuthorizationRequirement 是 Microsoft.AspNetCore.Authorization 接口/// &lt;summary&gt;/// 用户认证信息必要参数类/// &lt;/summary&gt;public class PermissionRequirement : IAuthorizationRequirement{/// &lt;summary&gt;/// 用户所属角色/// &lt;/summary&gt;public Role Roles { get;  set; } = new Role();public void SetRolesName(string roleName){Roles.Name = roleName;}/// &lt;summary&gt;/// 无权限时跳转到此API/// &lt;/summary&gt;public string DeniedAction { get; set; }/// &lt;summary&gt;/// 认证授权类型/// &lt;/summary&gt;public string ClaimType { internal get; set; }/// &lt;summary&gt;/// 未授权时跳转/// &lt;/summary&gt;public string LoginPath { get; set; } = "/Account/Login";/// &lt;summary&gt;/// 发行人/// &lt;/summary&gt;public string Issuer { get; set; }/// &lt;summary&gt;/// 订阅人/// &lt;/summary&gt;public string Audience { get; set; }/// &lt;summary&gt;/// 过期时间/// &lt;/summary&gt;public TimeSpan Expiration { get; set; }/// &lt;summary&gt;/// 颁发时间/// &lt;/summary&gt;public long IssuedTime { get; set; }/// &lt;summary&gt;/// 签名验证/// &lt;/summary&gt;public SigningCredentials SigningCredentials { get; set; }/// &lt;summary&gt;/// 构造/// &lt;/summary&gt;/// &lt;param name="deniedAction"&gt;无权限时跳转到此API&lt;/param&gt;/// &lt;param name="userPermissions"&gt;用户权限集合&lt;/param&gt;/// &lt;param name="deniedAction"&gt;拒约请求的url&lt;/param&gt;/// &lt;param name="permissions"&gt;权限集合&lt;/param&gt;/// &lt;param name="claimType"&gt;声明类型&lt;/param&gt;/// &lt;param name="issuer"&gt;发行人&lt;/param&gt;/// &lt;param name="audience"&gt;订阅人&lt;/param&gt;/// &lt;param name="issusedTime"&gt;颁发时间&lt;/param&gt;/// &lt;param name="signingCredentials"&gt;签名验证实体&lt;/param&gt;public PermissionRequirement(string deniedAction, Role Role, string claimType, string issuer, string audience, SigningCredentials signingCredentials,long issusedTime, TimeSpan expiration){ClaimType = claimType;DeniedAction = deniedAction;Roles = Role;Issuer = issuer;Audience = audience;Expiration = expiration;IssuedTime = issusedTime;SigningCredentials = signingCredentials;}}

③ 实现 TokenValidationParameters

Token 的信息配置

        public static TokenValidationParameters GetTokenValidationParameters(){var tokenValida = new TokenValidationParameters{// 定义 Token 内容ValidateIssuerSigningKey = true,IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AuthConfig.SecurityKey)),ValidateIssuer = true,ValidIssuer = AuthConfig.Issuer,ValidateAudience = true,ValidAudience = AuthConfig.Audience,ValidateLifetime = true,ClockSkew = TimeSpan.Zero,RequireExpirationTime = true};return tokenValida;}

④ 生成 Token

用于将用户的身份信息(Claims)和角色授权信息(PermissionRequirement)存放到 Token 中。

        /// &lt;summary&gt;/// 获取基于JWT的Token/// &lt;/summary&gt;/// &lt;param name="username"&gt;&lt;/param&gt;/// &lt;returns&gt;&lt;/returns&gt;public static dynamic BuildJwtToken(Claim[] claims, PermissionRequirement permissionRequirement){var now = DateTime.UtcNow;var jwt = new JwtSecurityToken(issuer: permissionRequirement.Issuer,audience: permissionRequirement.Audience,claims: claims,notBefore: now,expires: now.Add(permissionRequirement.Expiration),signingCredentials: permissionRequirement.SigningCredentials);var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);var response = new{Status = true,access_token = encodedJwt,expires_in = permissionRequirement.Expiration.TotalMilliseconds,token_type = "Bearer"};return response;}

⑤ 实现服务注入和身份认证配置

从别的变量导入配置信息,可有可无

            // 设置用于加密 Token 的密钥// 配置角色权限 var roleRequirement = RolePermission.GetRoleRequirement(AccountHash.GetTokenSecurityKey());// 定义如何生成用户的 Tokenvar tokenValidationParameters = RolePermission.GetTokenValidationParameters();

配置 ASP.NET Core 的身份认证服务

需要实现三个配置

  • AddAuthorization 导入角色身份认证策略

  • AddAuthentication 身份认证类型

  • AddJwtBearer Jwt 认证配置

            // 导入角色身份认证策略services.AddAuthorization(options =&gt;{options.AddPolicy("Permission",policy =&gt; policy.Requirements.Add(roleRequirement));// ↓ 身份认证类型}).AddAuthentication(options =&gt;{options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;// ↓ Jwt 认证配置}).AddJwtBearer(options =&gt;{options.TokenValidationParameters = tokenValidationParameters;options.SaveToken = true;options.Events = new JwtBearerEvents(){// 在安全令牌通过验证和ClaimsIdentity通过验证之后调用// 如果用户访问注销页面OnTokenValidated = context =&gt;{if (context.Request.Path.Value.ToString() == "/account/logout"){var token = ((context as TokenValidatedContext).SecurityToken as JwtSecurityToken).RawData;}return Task.CompletedTask;}};});

注入自定义的授权服务 PermissionHandler

注入自定义认证模型类 roleRequirement

            // 添加 httpcontext 拦截services.AddSingleton&lt;IAuthorizationHandler, PermissionHandler&gt;();services.AddSingleton(roleRequirement);

添加中间件

貌似这两个不区分先后顺序

            app.UseAuthorization();app.UseAuthentication();

⑥ 实现登陆

可以在颁发 Token 时把能够使用的 API 存储进去,但是这种方法不适合 API 较多的情况。

可以存放 用户信息(Claims)和角色信息,后台通过角色信息获取授权访问的 API 列表。

        /// &lt;summary&gt;/// 登陆/// &lt;/summary&gt;/// &lt;param name="username"&gt;用户名&lt;/param&gt;/// &lt;param name="password"&gt;密码&lt;/param&gt;/// &lt;returns&gt;Token信息&lt;/returns&gt;[HttpPost("login")]public JsonResult Login(string username, string password){var user = UserModel.Users.FirstOrDefault(x =&gt; x.UserName == username &amp;&amp; x.UserPossword == password);if (user == null)return new JsonResult(new ResponseModel{Code = 0,Message = "登陆失败!"});// 配置用户标识var userClaims = new Claim[]{new Claim(ClaimTypes.Name,user.UserName),new Claim(ClaimTypes.Role,user.Role),new Claim(ClaimTypes.Expiration,DateTime.Now.AddMinutes(_requirement.Expiration.TotalMinutes).ToString()),};_requirement.SetRolesName(user.Role);// 生成用户标识var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);identity.AddClaims(userClaims);var token = JwtToken.BuildJwtToken(userClaims, _requirement);return new JsonResult(new ResponseModel{Code = 200,Message = "登陆成功!请注意保存你的 Token 凭证!",Data = token});}

⑦ 添加 API 授权策略

    [Authorize(Policy = "Permission")]

⑧ 实现自定义授权校验

要实现自定义 API 角色/策略授权,需要继承 AuthorizationHandler<TRequirement>

里面的内容是完全自定义的, AuthorizationHandlerContext 是认证授权的上下文,在此实现自定义的访问授权认证。

也可以加上自动刷新 Token 的功能。

    /// &lt;summary&gt;/// 验证用户信息,进行权限授权Handler/// &lt;/summary&gt;public class PermissionHandler : AuthorizationHandler&lt;PermissionRequirement&gt;{protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,PermissionRequirement requirement){List&lt;PermissionRequirement&gt; requirements = new List&lt;PermissionRequirement&gt;();foreach (var item in context.Requirements){requirements.Add((PermissionRequirement)item);}foreach (var item in requirements){// 校验 颁发和接收对象if (!(item.Issuer == AuthConfig.Issuer ?item.Audience == AuthConfig.Audience ?true : false : false)){context.Fail();}// 校验过期时间var nowTime = DateTimeOffset.Now.ToUnixTimeSeconds();var issued = item.IssuedTime +Convert.ToInt64(item.Expiration.TotalSeconds);if (issued &lt; nowTime)context.Fail();// 是否有访问此 API 的权限var resource = ((Microsoft.AspNetCore.Routing.RouteEndpoint)context.Resource).RoutePattern;var permissions = item.Roles.Permissions.ToList();var apis = permissions.Any(x =&gt; x.Name.ToLower() == item.Roles.Name.ToLower() &amp;&amp; x.Url.ToLower() == resource.RawText.ToLower());if (!apis)context.Fail();context.Succeed(requirement);// 无权限时跳转到某个页面//var httpcontext = new HttpContextAccessor();//httpcontext.HttpContext.Response.Redirect(item.DeniedAction);}context.Succeed(requirement);return Task.CompletedTask;}}

⑨ 一些有用的代码

将字符串生成哈希值,例如密码。

为了安全,删除字符串里面的特殊字符,例如 "'$

    public static class AccountHash{// 获取字符串的哈希值public static string GetByHashString(string str){string hash = GetMd5Hash(str.Replace("\"", String.Empty).Replace("\'", String.Empty).Replace("$", String.Empty));return hash;}/// &lt;summary&gt;/// 获取用于加密 Token 的密钥/// &lt;/summary&gt;/// &lt;returns&gt;&lt;/returns&gt;public static SigningCredentials GetTokenSecurityKey(){var securityKey = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AuthConfig.SecurityKey)), SecurityAlgorithms.HmacSha256);return securityKey;}private static string GetMd5Hash(string source){MD5 md5Hash = MD5.Create();byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(source));StringBuilder sBuilder = new StringBuilder();for (int i = 0; i &lt; data.Length; i++){sBuilder.Append(data[i].ToString("x2"));}return sBuilder.ToString();}}

签发 Token

PermissionRequirement 不是必须的,用来存放角色或策略认证信息,Claims 应该是必须的。

    /// &lt;summary&gt;/// 颁发用户Token/// &lt;/summary&gt;public class JwtToken{/// &lt;summary&gt;/// 获取基于JWT的Token/// &lt;/summary&gt;/// &lt;param name="username"&gt;&lt;/param&gt;/// &lt;returns&gt;&lt;/returns&gt;public static dynamic BuildJwtToken(Claim[] claims, PermissionRequirement permissionRequirement){var now = DateTime.UtcNow;var jwt = new JwtSecurityToken(issuer: permissionRequirement.Issuer,audience: permissionRequirement.Audience,claims: claims,notBefore: now,expires: now.Add(permissionRequirement.Expiration),signingCredentials: permissionRequirement.SigningCredentials);var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);var response = new{Status = true,access_token = encodedJwt,expires_in = permissionRequirement.Expiration.TotalMilliseconds,token_type = "Bearer"};return response;}

表示时间戳


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/314260.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

EOJ Monthly 2019.11 E. 数学题(反演 + 杜教筛 + 拉格朗日插值)

EOJ Monthly 2019.11 ∑i1n∑a11i∑a21i∑a31i⋯∑ak−1i∑aki[gcd(a1,a2,a3,…,ak−1,ak,i)1]∑i1n∑d∣iμ(d)⌊id⌋k∑d1nμ(d)∑d∣i⌊id⌋k∑d1nμ(d)∑t1ndtk\sum_{i 1} ^{n} \sum_{a_1 1} ^{i} \sum_{a_2 1} ^{i} \sum_{a_3 1} ^{i} \dots \sum_{a_{k - 1}} ^{i} \s…

打表

打表 众所周知&#xff0c;打表是一项非常非常重要的技能QAQ 寻找通项&#xff1a;直接观察一元函数或者一些dp值或者sg值的规律注意二进制&#xff1a;有些规律不在10进制下&#xff0c;而是存在于二进制下观察递推关系&#xff1a;有时候无法得到有用的通项&#xff0c;但是…

关于 .Net Core runtimeconfig 文件说明

项目的bin\debug\netcoreapp${Version}下面能够找到这个${AppName}.runtimeconfig.json文件&#xff0c;简单来说&#xff0c;它就是用来定义应用程序所用的共享框架&#xff08;.Net Core App&#xff09;以及运行时选项 的一个文件。一个简单的例子{ "runtimeOptions&q…

F. Cowmpany Cowmpensation(树形dp + 拉格朗日插值)

F. Cowmpany Cowmpensation 首先一般dp推导dp[i][j]∏u∈soni∑k1jdp[v][k]dp[i][j] \prod\limits_{u \in son_i} \sum\limits_{k 1} ^{j} dp[v][k]dp[i][j]u∈soni​∏​k1∑j​dp[v][k] 这个是毫无疑问的&#xff0c;然后我们考虑如何得到d≥nd \geq nd≥n的情况。 我们…

P4055 [JSOI2009]游戏(二分图匹配+博弈)

P4055 [JSOI2009]游戏 对于一个网格&#xff0c;上面有一些障碍物不能走&#xff0c;A选择起点&#xff0c;然后B走到相邻的四个格子之一&#xff0c;然后轮流移动不能移动的一方失败。求解所有可以赢的初始位置。 首先对于这个问题要想到网格图上博弈&#xff0c;两个人走的…

net core 3.0 之Grpc新特性小试牛刀

作者&#xff1a; 相信微服务大家伙都有听说和知道&#xff0c;好处弊端咱也不多说了&#xff0c;Grpc算是一个比较全面的微服务框架&#xff0c;也得到微软的支持总结下来就是&#xff0c;跨平台&#xff0c;可靠&#xff0c;通信快&#xff0c;扩展性强&#xff0c;网络消耗小…

Fraction Construction Problem(拓展欧几里德)

Fraction Construction Problem 思路 cd−efab\frac{c}{d} - \frac{e}{f} \frac{a}{b}dc​−fe​ba​ a<b&f<ba < b \& f < ba<b&f<b 1≤1,e≤410121 \leq 1, e \leq 4 \times10 ^{12}1≤1,e≤41012 当b 1时&#xff0c;一定无解。 gcd(a, b…

CF1168D Anagram Paths(由必要到充分/虚树)

CF1168D Anagram Paths 对于这道题首先有一个关键的性质&#xff0c;那就是对于一个树&#xff0c;它是可重排的&#xff0c;当且仅当在树上任意一个节点&#xff0c;所有字母在相关联的字符串中出现次数最大值之和小于当前点到叶子的距离。这个性质可以通过归纳证明&#xff0…

基于Coravel定时任务之计算总页数

在物联网系统中&#xff0c;需要计算底端所有设备的总数&#xff0c;除以分页每页显示数量&#xff0c;进行一个总页数的显示。包括状态&#xff0c;告警&#xff0c;日志等等数据都需要对应的总页数的显示。2.1 TaskSchedulerTaskScheduler库只支持.net&#xff0c;且需要结合…

拉格朗日插值

拉格朗日插值 简要阐述 结论给定n1n 1n1个点最多可以得到一个nnn次多项式的表达式&#xff0c;并且f(x)∑i1nyi∏j∤ix−xjxi−yjf(x) \sum_{i 1} ^{n} y_i \prod\limits_{j \nmid i}\frac{x - x_j}{x_i - y_j}f(x)∑i1n​yi​j∤i∏​xi​−yj​x−xj​​我们随便往里带入…

A Story of One Country (Hard)(中途相遇法/启发式分裂)

A Story of One Country (Hard) https://www.luogu.com.cn/problem/solution/CF1181E2 首先考虑暴力的做法&#xff0c;就是每次排序然后寻找分割点&#xff0c;对分割点左右两边分治处理&#xff0c;但是这样的复杂度是 最坏情况下O(n2logn)O(n^2logn)O(n2logn) 然后我们考…

Docker系列之AspNetCore Runtime VS .NetCore Runtime VS SDK(四)

接下来我们就要慢慢步入在.NET Core中使用Docker的殿堂了&#xff0c;如题在开始之前&#xff0c;我们需要搞清楚一些概念&#xff0c;要不然看到官方提供如下一系列镜像&#xff0c;我们会一脸懵逼&#xff0c;不知道到底要使用哪一个。AspNetCore Runtime VS .NetCore Runti…

HDU 6750 Function(莫比乌斯反演)(2020百度之星初赛1)

Function 推式子 S(n)∑i1n∑d∣id[gcd(d,id)1]∑d1nd∑d∣i[gcd(d,id)1]∑d1nd∑i1nd[gcd(d,i)1]∑d1nd∑i1nd∑k∣gcd(d,i)μ(k)∑k1nμ(k)k∑d1nkd∑i1nk2dtk2d∑t1nnt∑k2∣tμ(k)ktk2∑k1nμ(k)k∑k2∣tnttk2itk2∑k1nμ(k)k∑i1nk2nik2iS(n) \sum_{i 1} ^{n} \sum_{d …

P5170 【模板】类欧几里得算法

P5170 【模板】类欧几里得算法 https://www.cnblogs.com/bztMinamoto/p/10334354.html 对于类欧几里得算法&#xff0c;本质上可以理解为一条直线下的整点个数&#xff0c;是一个矩形区域。 然后将整数部分提出&#xff0c;就可以使得这个直线斜率较小&#xff0c;那么我们就可…

.NET Core跨平台部署于Docker(Centos)- 视频教程

.NET Core跨平台部署于Docker,Docker部署于Centos中&#xff0c;演示跨平台特性&#xff01;以下视频教程&#xff0c;请带上耳机开始聆听往期教程&#xff1a;.NET开发框架(一)-框架介绍与视频演示.NET开发框架(二)-框架功能简述.NET开发框架(三)-高可用服务器端设计.NET开发框…

矩阵快速幂各类题型总结(一般,共轭,1 * n, 矩阵简化)

Reading comprehension 这个不难找出递推式f[n]f[n−1]2f[n−2]1f[n] f[n - 1] 2f[n - 2] 1f[n]f[n−1]2f[n−2]1。 /*Author : lifehappy */ #pragma GCC optimize(2) #pragma GCC optimize(3) #include <bits/stdc.h>using namespace std;typedef long long ll;co…

【清华集训2014】Sum)(类欧几里得算法)

【清华集训2014】Sum 然后本质上我们需要求解的就是那个带根号式子的奇偶性&#xff0c;然后我们发现这个式子很像是类欧几里得算法&#xff0c;求解一个斜率为无理数直线下的整点个数&#xff0c;然后我们直接对于一般形式求解&#xff0c;那么就是每次利用整数部分将斜率减小…

一本让我多花2倍时间读的书

这里是Z哥的个人公众号每周五11&#xff1a;45 按时送达当然了&#xff0c;也会时不时加个餐&#xff5e;我的第「87」篇原创敬上Hi&#xff0c;大家好&#xff0c;我是Z哥。熟悉我的小伙伴应该知道&#xff0c;我平时看书大多都很快&#xff0c;之前还把自己的速读技巧分享给了…

P3355 骑士共存问题(网络流)

P3355 骑士共存问题 经典的最大独立集问题&#xff0c;最大独立集就是最小点覆盖的补集&#xff0c;因为最小点覆盖等于最大匹配&#xff0c;所以最大独立集等于点数减去最大匹配。

[NOI2005]月下柠檬树 (自适应辛普森)

P4207 [NOI2005]月下柠檬树 如图&#xff0c;我们要求的面积就是这些圆形跟梯形的组合&#xff0c;由于投射到地面上&#xff0c;显然有h′htanθh \frac{h}{tan \theta}h′tanθh​&#xff0c;由此我们就可以开始推导这个f(x)f(x)f(x)函数了。 所以转换为我们要推导出直线a…