• ASP.NET Core 3.1中使用JWT身份认证


    0、引言

    若不清楚什么是JWT的请先了解下什么是JWT

    1、关于Authentication与Authorization

    我相信在aspnet core中刚接触甚至用了段时间这两个概念的时候都是一头雾水的,傻傻分不清。
    认证(Authentication)和授权(Authorization)在概念上比较的相似,且又有一定的联系,因此很容易混淆。
    认证(Authentication)是指验证用户身份的过程,即当用户要访问受保护的资源时,将其信息(如用户名和密码)发送给服务器并由服务器验证的过程。
    授权(Authorization)是验证一个已通过身份认证的用户是否有权限做某件事情的过程。
    有过RBAC的开发经验者来说这里可以这么通俗的来理解:认证是验证一个用户是否“合法”(一般就是检查数据库中是否有这么个用户),授权是验证这个用户是否有做事情的权限(简单理解成RBAC中的用户权限)。

    2、整个认证流程是怎样的?

    整个HTTP请求流程
    从图中可以看到整个认证、授权的流程,先进行身份验证 ,验证通过后将Token放回给客户端,客户端访问资源的时候请求头中添加Token信息,服务器进行验证并于授权是否能够访问该资源。

    3、开始JWT身份认证

    3.1 安装JwtBearer包

    在.csproj项目中添加JWT包(这里添加有很多种方式,自行选择)

    <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.3" />
    

    3.2 安装Swashbuckle.AspNetCore包

    这里便于进行测试,引入Swagger工具。

    <PackageReference Include="Swashbuckle.AspNetCore" Version="5.3.1" />
    

    3.3 添加身份认证相关服务到容器中

    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    }).AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = false,
            ValidateAudience = false,
            ValidateLifetime = false,
            ValidateIssuerSigningKey = true,
            ValidIssuer = "jonny",
            ValidAudience = "jonny",
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("secretsecretsecret"))
        };
    });
    

    说明

    配置项 类型 说明
    ValidateIssuerSigningKey bool 是否调用对签名securityToken的SecurityKey进行验证。
    ValidIssuer string 将用于检查令牌的发行者是否与此发行者相同。
    ValidateIssuer bool 是否验证发行者
    ValidAudience string 检查令牌的受众群体是否与此受众群体相同。
    ValidateAudience bool 在令牌验证期间验证受众 。
    ValidateLifetime bool 验证生命周期。

    3.4 添加Swagger服务到容器中

    services.AddSwaggerGen(options =>
    {
        options.SwaggerDoc("openapi", new Microsoft.OpenApi.Models.OpenApiInfo
        {
            Title = "统一身份认证API",
            Description = "身份认证和授权详解",
            Version = "v1"
        });
        var scheme = new OpenApiSecurityScheme()
        {
            Scheme = JwtBearerDefaults.AuthenticationScheme,
            BearerFormat = "JWT",
            In = ParameterLocation.Header,
            //头名称
            Name = ApiKeyConstants.HeaderName,
            Type = SecuritySchemeType.ApiKey,
            Description = "Bearer Token"
    };
    options.AddSecurityDefinition(JwtBearerDefaults.AuthenticationScheme, scheme);
    options.AddSecurityRequirement(new OpenApiSecurityRequirement()
     {
         {
             new OpenApiSecurityScheme
             {
                 Reference = new OpenApiReference
                 {
                     Type = ReferenceType.SecurityScheme,
                     Id = "Bearer"
                 }
             },
             new string[] {}
         }
     });
    });
    

    aspnet core 3.x swagger与2.x有细微的差别,例如swagger中加入jwt和以前就有一定的差别。

    swagger加入身份认证后出现了认证按钮。
    在这里插入图片描述

    3.5 将身份认证加入到管道中

    //身份认证中间件(踩坑:授权中间件必须在认证中间件之前)
    app.UseAuthentication();
    

    3.x中身份认证一定要在UseRouting和UseEndpoints之间

    3.6 将swagger加入到管道中

    app.UseSwagger();
    app.UseSwaggerUI(options =>
    {
         options.RoutePrefix = string.Empty;
         //配置swagger端点
         options.SwaggerEndpoint("swagger/openapi/swagger.json", "openapi v1");
    });
    

    3.7 在需要授权的资源上加入Authorize

    例如:

    [HttpGet("role")]
    [Authorize(Roles = "admin")]
    public IEnumerable<Claim> GetRole()
    {
        return HttpContext.User.FindAll(c => c.Type == ClaimTypes.Role);
    }
    

    这里默认使用角色授权机制

    4 、测试

    4.1 请求资源

    这时会返回401,因为没有进行身份认证
    在这里插入图片描述

    4.2 调用登录获取token

    在这里插入图片描述

    4.3 将token添加到Header中

    在这里插入图片描述

    4.4 再次请求

    这时返回403,是因为使用的jonny账户登录的没有admin权限。
    在这里插入图片描述

    4.5 切换admin账户登录

    在这里插入图片描述
    重复上面的4.3、4.4步骤 。再次测试。这时就能正常访问。
    在这里插入图片描述

    5、登录逻辑代码

    我这里就不做过多的解释 ,直接将相关创建JTW代码等贴出来。

    public interface ICustomAuthenticationManager
    {
        string Authenticate(string username, string password);
    
        IDictionary<string, string> Tokens { get; }
    }
    public class CustomAuthenticationManager : ICustomAuthenticationManager
    {
        private readonly IDictionary<string, string> users = new Dictionary<string, string>
        {
            { "admin", "admin" },
            { "jonny", "jonny" },
            { "xhl", "xhl" },
            { "james", "james" }
        };
    
        public IDictionary<string, string> Tokens { get; } = new Dictionary<string, string>();
    
        public string Authenticate(string username, string password)
        {
            var claimsIdentity = new ClaimsIdentity(new[]{
                new Claim(ClaimTypes.Name,username)
            });
            if (!users.Any(u => u.Key == username && u.Value == password))
            {
                return null;
            }
            if (username == "admin")
            {
                claimsIdentity.AddClaims(new[]
                {
                    new Claim( ClaimTypes.Email, "xhl.jonny@gmail.com"),
                    new Claim( "ManageId", "admin"),
                    new Claim(ClaimTypes.Role,"admin")
                });
            }
            var handler = new JwtSecurityTokenHandler();
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = claimsIdentity,
                Expires = DateTime.Now.AddMinutes(3),
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes("secretsecretsecret")), SecurityAlgorithms.HmacSha256),
            };
            var securityToken = handler.CreateToken(tokenDescriptor);
            var token = handler.WriteToken(securityToken);
            Tokens.Add(token, username);
            return token;
        }
    }
    

    上面使用内存数据进行逻辑验证 ,实际中需要使用数据库查询验证等。

    今天的JWT身份认证就介绍完了 ,下一篇文章将介绍授权。角色授权、身份授权(Claim)、自定义策略授权。

  • 相关阅读:
    TCP四次握手断开连接(十一)
    Go-函数
    Go-数据类型以及变量,常量
    GO语言介绍以及开发环境配置
    Socket与WebSocket以及http与https重新总结
    希尔排序
    第19课
    第18课
    外传篇3 动态内存申请的结果
    外传篇2 函数的异常规格说明
  • 原文地址:https://www.cnblogs.com/cqxhl/p/12993257.html
Copyright © 2020-2023  润新知