10分钟简单学习net core集成jwt权限认证,快速接入项目落地使用 (下)

简介: 10分钟简单学习net core集成jwt权限认证,快速接入项目落地使用 (下)

创建常量类Const

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace jwtWebAPI
{
    public class Const
    {
        /// <summary>
        /// 这里为了演示,写死一个密钥。实际生产环境可以从配置文件读取,这个是用网上工具随便生成的一个密钥(md5或者其他都可以)
        /// </summary>
        public const string SecurityKey = "48754F4C58F9EA428FE09D714E468211";
        /// <summary>
        /// 站点地址(颁发者、接受者),这里测试和当前本地运行网站相同,实际发到正式环境应为域名地址
        /// </summary>
        public const string Domain = "https://localhost:44345";
        /// <summary>
        /// 受理人,之所以弄成可变的是为了用接口动态更改这个值以模拟强制Token失效
        /// 真实业务场景可以在数据库或者redis存一个和用户id相关的值,生成token和验证token的时候获取到持久化的值去校验
        /// 如果重新登陆,则刷新这个值
        /// </summary>
        public static string ValidAudience;
    }
}

JWT登录授权测试成功

把程序编译运行起来,打开postman,输入地址,首先测试不需要任何授权的

 

 

正确地返回了数据,那么接下来我们测试JWT的流程。

首先我们什么都不加调用接口:https://localhost:44345/api/values2,注意,我创建的时候是https的,大家注意看是http还是https

 

返回了状态码401,也就是未经授权:访问由于凭据无效被拒绝。 说明JWT校验生效了,我们的接口收到了保护。

调用模拟登陆授权接口:https://localhost:44345/api/auth?userName=xiongze&pwd=123456

这里的用户密码是随便写的,因为我们模拟登陆只是校验了下非空,因此写什么都能通过。

 

然后我们得到了一个xxx.yyy.zzz 格式的 token 值。我们把token复制出来。

在刚才401的接口(https://localhost:44345/api/values2)请求header中添加JWT的参数,把我们的token加上去

再次调用我们的模拟数据接口,但是这次我们加了一个header,KEY:Authorization     Value:Bearer Tokne的值

这里需要注意 Bearer 后面是有一个空格的,然后就是我们上一步获取到的token,

 

得到返回值,正确授权成功,我们是支持自定义返回参数的,上面代码里面有相关内容,比如用户名这些不敏感的信息可以带着返回。

等token设置的过期时间到了,或者重新生成了新的Token,没有及时更新,那么我们的授权也到期,401,

升级操作:接口权限隔离

上面的操作是所有登录授权成功的角色都可以进行调用所有接口,那么我们现在想要进行接口隔离限制,

也就是说,虽然授权登录了,但是我这个接口是指定权限访问的。

比如说:删除接口只能管理员角色操作,那么其他角色虽然授权登录了,但是没有权限调用删除接口。

我们在原来的操作进行改造升级看一下。

添加类

新建一个AuthManagement文件夹,添加PolicyRequirement类PolicyHandler类

PolicyRequirement类:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace jwtWebAPI.AuthManagement
{
    /// <summary>
    /// 权限承载实体
    /// </summary>
    public class PolicyRequirement : IAuthorizationRequirement
    {
        /// <summary>
        /// 用户权限集合
        /// </summary>
        public List<UserPermission> UserPermissions { get; private set; }
        /// <summary>
        /// 无权限action
        /// </summary>
        public string DeniedAction { get; set; }
        /// <summary>
        /// 构造
        /// </summary>
        public PolicyRequirement()
        {
            //没有权限则跳转到这个路由
            DeniedAction = new PathString("/api/nopermission");
            //用户有权限访问的路由配置,当然可以从数据库获取
            UserPermissions = new List<UserPermission> {
                              new UserPermission {  Url="/api/values3", UserName="admin"},
                          };
        }
    }
    /// <summary>
    /// 用户权限承载实体
    /// </summary>
    public class UserPermission
    {
        /// <summary>
        /// 用户名
        /// </summary>
        public string UserName { get; set; }
        /// <summary>
        /// 请求Url
        /// </summary>
        public string Url { get; set; }
    }
}

PolicyHandler类(注意2.x和3.x的区别)

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
namespace jwtWebAPI.AuthManagement
{
    public class PolicyHandler : AuthorizationHandler<PolicyRequirement>
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        public PolicyHandler(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PolicyRequirement requirement)
        {
            //赋值用户权限
            var userPermissions = requirement.UserPermissions;
            var httpContext = _httpContextAccessor.HttpContext;
            //请求Url
            var questUrl = httpContext.Request.Path.Value.ToUpperInvariant();
            //是否经过验证
            var isAuthenticated = httpContext.User.Identity.IsAuthenticated;
            if (isAuthenticated)
            {
                if (userPermissions.GroupBy(g => g.Url).Any(w => w.Key.ToUpperInvariant() == questUrl))
                {
                    //用户名
                    var userName = httpContext.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.NameIdentifier).Value;
                    if (userPermissions.Any(w => w.UserName == userName && w.Url.ToUpperInvariant() == questUrl))
                    {
                        context.Succeed(requirement);
                    }
                    else
                    {
                        ////无权限跳转到拒绝页面
                        //httpContext.Response.Redirect(requirement.DeniedAction);
                        return Task.CompletedTask;
                    }
                }
                else
                {
                    context.Succeed(requirement);
                }
            }
            return Task.CompletedTask;
        }
    }
}

添加指定角色

AuthController 控制器的GetToken授权加入自定义的参数,如下

new Claim("Role", userName)  //这里是角色,我使用登录账号admin代替

 

AuthController 控制器里面添加无权限访问的方法

[AllowAnonymous]
[HttpGet]
[Route("api/nopermission")]
public IActionResult NoPermission()
{
     return Forbid("No Permission!");
}

修改Startup配置

在startup.cs的ConfigureServices 方法里面添加策略鉴权模式、添加JWT Scheme、注入授权Handler

修改后的文件如下

using jwtWebAPI.AuthManagement;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace jwtWebAPI
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        public IConfiguration Configuration { get; }
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services
                //添加策略鉴权模式
                .AddAuthorization(options =>
                {
                    options.AddPolicy("Permission", policy => policy.Requirements.Add(new PolicyRequirement()));
                })
                //添加JWT Scheme
                .AddAuthentication(s =>
                {
                    s.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    s.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                    s.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                //添加jwt验证:
                .AddJwtBearer(options => {
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateLifetime = true,//是否验证失效时间
                        ClockSkew = TimeSpan.FromSeconds(30),  //时间偏移量(允许误差时间)
                        ValidateAudience = true,//是否验证Audience(验证之前的token是否失效)
                        //ValidAudience = Const.GetValidudience(),//Audience
                        //这里采用动态验证的方式,在重新登陆时,刷新token,旧token就强制失效了
                        AudienceValidator = (m, n, z) =>
                        {
                            return m != null && m.FirstOrDefault().Equals(Const.ValidAudience);
                        },
                        ValidateIssuer = true,//是否验证Issuer(颁发者)
                        ValidAudience = Const.Domain,//Audience    【Const是新建的一个常量类】  接收者 
                        ValidIssuer = Const.Domain,//Issuer,这两项和前面签发jwt的设置一致      颁发者
                        ValidateIssuerSigningKey = true,//是否验证SecurityKey
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Const.SecurityKey))//拿到秘钥SecurityKey
                    };
                    options.Events = new JwtBearerEvents
                    {
                        OnAuthenticationFailed = context =>
                        {
                            //Token expired
                            if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                            {
                                context.Response.Headers.Add("Token-Expired", "true");
                            }
                            return Task.CompletedTask;
                        }
                    };
                });
            //注入授权Handler
            services.AddSingleton<IAuthorizationHandler, PolicyHandler>();
            //注入获取HttpContext
            services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.AddControllers();
        }
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        { 
            //添加jwt验证
            app.UseAuthentication();
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseHttpsRedirection();
            app.UseRouting();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

添加api访问的方法

ValuesController控制器添加指定权限访问的方法,如下:

   /**
        * 这个接口必须用admin
        **/
        [HttpGet]
        [Route("api/values3")]
        [Authorize("Permission")]
        public ActionResult<IEnumerable<string>> values3()
        {
            //这是获取自定义参数的方法
            var auth = HttpContext.AuthenticateAsync().Result.Principal.Claims;
            var userName = auth.FirstOrDefault(t => t.Type.Equals(ClaimTypes.NameIdentifier))?.Value;
            var role = auth.FirstOrDefault(t => t.Type.Equals("Role"))?.Value;
            return new string[] { "访问成功:这个接口有管理员权限才可以访问", $"userName={userName}", $"Role={role}" };
        }

不同权限测试访问

我们同样的方法去模拟登录,https://localhost:44345/api/auth?userName=xiongze&pwd=123

注意,账号先不用admin登录,然后用返回的token去请求我们刚刚添加的指定权限访问的接口,这个时候是没有权限访问的,因为这个是admin权限访问。

我们同样的方法去模拟登录,https://localhost:44345/api/auth?userName=admin&pwd=123

访问成功。

源码下载地址

Gitee:https://gitee.com/xiongze/jwtWebAPI.git

参考文献

相关文章
|
23天前
|
数据可视化 网络协议 C#
C#/.NET/.NET Core优秀项目和框架2024年3月简报
公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的介绍、功能特点、使用方式以及部分功能截图等(打不开或者打开GitHub很慢的同学可以优先查看公众号推文,文末一定会附带项目和框架源码地址)。注意:排名不分先后,都是十分优秀的开源项目和框架,每周定期更新分享(欢迎关注公众号:追逐时光者,第一时间获取每周精选分享资讯🔔)。
|
2月前
|
关系型数据库 分布式数据库 数据库
成都晨云信息技术完成阿里云PolarDB数据库产品生态集成认证
近日,成都晨云信息技术有限责任公司(以下简称晨云信息)与阿里云PolarDB PostgreSQL版数据库产品展开产品集成认证。测试结果表明,晨云信息旗下晨云-站群管理系统(V1.0)与阿里云以下产品:开源云原生数据库PolarDB PostgreSQL版(V11),完全满足产品兼容认证要求,兼容性良好,系统运行稳定。
|
3月前
|
存储 开发框架 NoSQL
ASP.NET WEB——项目中Cookie与Session的用法
ASP.NET WEB——项目中Cookie与Session的用法
39 0
|
3月前
|
开发框架 前端开发 .NET
ASP.NET WEB——项目创建与文件上传操作
ASP.NET WEB——项目创建与文件上传操作
48 0
|
4月前
|
XML 开发框架 .NET
ASP.NET COR3.1 集成日志插件NLog
ASP.NET COR3.1 集成日志插件NLog
35 0
|
5月前
|
Java 测试技术 数据安全/隐私保护
SpringCloud微服务之最全JWT学习教程03
SpringCloud微服务之最全JWT学习教程03
90 0
|
4月前
|
开发框架 安全 .NET
IIS服务器发布ASP.NET项目
对于云服务器,程序员一般不会陌生,如果项目需要发布到现网,那么服务器是必不可缺的一项硬性条件,那么如何在云服务器上部署一个项目,需要做哪些配置准备,下面就由本文档为大家讲解,本篇以IIS服务器发布ASP.NET项目为例。
48 1
|
5天前
|
安全 关系型数据库 MySQL
node实战——后端koa结合jwt连接mysql实现权限登录(node后端就业储备知识)
node实战——后端koa结合jwt连接mysql实现权限登录(node后端就业储备知识)
15 3
|
23天前
|
JSON 安全 关系型数据库
SpringCloud Gateway 实现自定义全局过滤器 + JWT权限验证
SpringCloud Gateway 实现自定义全局过滤器 + JWT权限验证
|
3月前
|
算法 BI API
C#/.NET/.NET Core优秀项目和框架2024年1月简报
C#/.NET/.NET Core优秀项目和框架2024年1月简报