• .NET6之MiniAPI(九):基于角色的身份验证和授权


    身份验证是这样一个过程:由用户提供凭据,然后将其与存储在操作系统、数据库、应用或资源中的凭据进行比较。 在授权过程中,如果凭据匹配,则用户身份验证成功,可执行已向其授权的操作。 授权指判断允许用户执行的操作的过程。也可以将身份验证理解为进入空间(例如服务器、数据库、应用或资源)的一种方式,而授权是用户可以对该空间(服务器、数据库或应用)内的哪些对象执行哪些操作。
    微软官方文档

    asp.net core支持 多 种授 权 ,本篇 重点说明 JW T的基于角色 授权方式。

    基于JWT角色身份验证和授权,思路是在登录时分发加密的Token,在访问资源时带有这个Token,服务端要验证这个Token是不是自己分发的,如果是,再验证访问范围是否正确,本篇的范围就是那些资源是那种角色访问,得到Token的用户当前是那种角色,也就是角色和资源的匹配。

    用asp.net core实现步骤:

    1、appsettings.json中配置JWT参

    2、添加身份认证和授权服务和中间件

    3、定义生成Token的方法和验证Toekn参数的方法

    4、登录时验证身份并分发Toekn

     

    using Microsoft.AspNetCore.Authentication.JwtBearer;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.IdentityModel.Tokens;
    using System.IdentityModel.Tokens.Jwt;
    using System.Security.Claims;
    using System.Text;
    
    var builder = WebApplication.CreateBuilder();
    //获取JWT参数,并注入到服务容器
    var jwtConfig = new JWTConfig();
    builder.Configuration.GetSection("JWTConfig").Bind(jwtConfig);
    builder.Services.AddSingleton(jwtConfig);
    //添加JJWT方式的身份认证和授权,
    builder.Services
        .AddAuthorization()
        .AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, opt =>
        {
            opt.RequireHttpsMetadata = false;
            opt.TokenValidationParameters = JwtToken.CreateTokenValidationParameters(jwtConfig);
        });
    
    var app = builder.Build();
    //使用身份认证和授权的中间件
    app.UseAuthentication();
    app.UseAuthorization();
    
    app.MapGet("/hellosystem", (ILogger<Program> logger, HttpContext context) =>
    {
        var message = $"hello,system,{context.User?.Identity?.Name}";
        logger.LogInformation(message);
    
        return message;
    }).RequireAuthorization(new RoleData { Roles = "system" });
    
    app.MapGet("/helloadmin", (ILogger<Program> logger, HttpContext context) =>
    {
        var message = $"hello,admin,{context.User?.Identity?.Name}";
        logger.LogInformation(message);
        return message;
    }).RequireAuthorization(new RoleData { Roles = "admin" });
    
    app.MapGet("/helloall", (ILogger<Program> logger, HttpContext context) =>
    {
        var message = $"hello,all roles,{context.User?.Identity?.Name}";
        logger.LogInformation(message);
        return message;
    }).RequireAuthorization(new RoleData { Roles = "admin,system" });
    
    
    //登录成功,并分发Token
    app.MapPost("/login", [AllowAnonymous] (ILogger<Program> logger, LoginModel login, JWTConfig jwtConfig) =>
    {
        logger.LogInformation("login");
        if (login.UserName == "gsw" && login.Password == "111111")
        {
            var now = DateTime.UtcNow;
            var claims = new Claim[] {
                    new Claim(ClaimTypes.Role, "admin"),
                    new Claim(ClaimTypes.Name, "桂素伟"),
                    new Claim(ClaimTypes.Sid, login.UserName),
                    new Claim(ClaimTypes.Expiration, now.AddSeconds(jwtConfig.Expires).ToString())
                    };
            var token = JwtToken.BuildJwtToken(claims, jwtConfig);
            return token;
        }
        else
        {
            return "username or password is error";
        }
    });
    
    app.Run();
    //登录实体
    public class LoginModel
    {
        public string? UserName { get; set; }
        public string? Password { get; set; }
    }
    //JWT配置
    public class JWTConfig
    {
        public string? Secret { get; set; }
        public string? Issuer { get; set; }
        public string? Audience { get; set; }
        public int Expires { get; set; }
    }
    //JWT操作类型
    public class JwtToken
    {
        //获取Token
        public static dynamic BuildJwtToken(Claim[] claims, JWTConfig jwtConfig)
        {
            var now = DateTime.UtcNow;
            var jwt = new JwtSecurityToken(
                issuer: jwtConfig.Issuer,
                audience: jwtConfig.Audience,
                claims: claims,
                notBefore: now,
                expires: now.AddSeconds(jwtConfig.Expires),
                signingCredentials: GetSigningCredentials(jwtConfig)
            );
            var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
            var response = new
            {
                Status = true,
                AccessToken = encodedJwt,
                ExpiresIn = now.AddSeconds(jwtConfig.Expires),
                TokenType = "Bearer"
            };
            return response;
        }
    
        static SigningCredentials GetSigningCredentials(JWTConfig jwtConfig)
        {
            var keyByteArray = Encoding.ASCII.GetBytes(jwtConfig?.Secret!);
            var signingKey = new SymmetricSecurityKey(keyByteArray);
            return new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
        }
        //验证Token的参数
        public static TokenValidationParameters CreateTokenValidationParameters(JWTConfig jwtConfig)
        {
            var keyByteArray = Encoding.ASCII.GetBytes(jwtConfig?.Secret!);
            var signingKey = new SymmetricSecurityKey(keyByteArray);
            return new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = signingKey,
                ValidateIssuer = true,
                ValidIssuer = jwtConfig?.Issuer,
                ValidateAudience = true,
                ValidAudience = jwtConfig?.Audience,
                ClockSkew = TimeSpan.Zero,
                RequireExpirationTime = true,
            };
        }
    }
    //mini api添加验证授权的参数类型
    public class RoleData : IAuthorizeData
    {
        public string? Policy { get; set; }
        public string? Roles { get; set; }
        public string? AuthenticationSchemes { get; set; }
    }

     

    验证结果:

    1、没有登录,返回401

    2、登录,取token

     

    3、正确访问

    4、没有授权访问,返回403

     

  • 相关阅读:
    浅析C++中的this指针
    转:函数指针数组的妙用(I)
    函数指针与函数指针数组的使用方法
    指针函数与函数指针的区别
    二叉树的镜像
    树的子结构
    Irrlicht 3D Engine 笔记系列 之 教程5- User Interface
    Android获取当前连接的wifi名称
    dlopen 方式调用 Linux 的动态链接库
    Appium基于安卓的各种FindElement的控件定位方法实践和建议
  • 原文地址:https://www.cnblogs.com/webenh/p/15829247.html
Copyright © 2020-2023  润新知