ASP.NET CORE WEBAPI 登录 JWT 鉴权 ,接口权限验证

JWT的简单使用

介绍

当今Web开发中,API的使用越来越广泛,而API的安全性也变得越来越重要。其中,JWT(JSON Web Token)鉴权和授权是一种常见的解决方案。

本篇文章将会介绍JWT鉴权和授权的原理、实现方式以及注意事项。

什么是JWT?

JWT是一种基于JSON格式的开放标准(RFC7519),用于在网络上传递声明信息的一种简洁、自包含的安全方式。JWT通常被用来在各个系统之间传递身份认证信息和用户授权信息。

安装相关 NuGet 包

在开始使用 JWT 进行授权鉴权之前,需要先安装 Microsoft.AspNetCore.Authentication.JwtBearer NuGet 包。可以使用 Visual Studio 的 NuGet 管理器或者命令行工具进行安装。

Install-Package Microsoft.AspNetCore.Authentication.JwtBearer

JWT的组成部分

JWT由三个部分组成:Header(头部)、Payload(负载)和Signature(签名)。

头部(Header)

头部通常由两部分组成:令牌类型(即JWT)和指定该令牌所使用的签名算法。例如:

{
  "alg": "HS256",
  "typ": "JWT"
}
负载(Payload)

负载通常包含了需要传递的声明信息,声明信息由键值对组成。例如:

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

其中,“sub”表示主题(subject),可以是用户ID或其他标识符;“name”表示用户名;“iat”表示令牌发行时间。

签名(Signature)

签名是对Header和Payload的内容进行数字签名后得到的一串字符串。签名用于验证JWT是否被篡改或伪造。例如:

HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)

其中,“secret”为使用该令牌的服务器端保存的密钥。

JWT的优点

  • 简洁:由于JWT采用了JSON格式,而JSON是一种轻量级的数据格式,因此JWT非常适合在多个服务之间传递信息。
  • 自包含:JWT包含了所有必要的信息,因此不需要像Session那样在服务器端存储用户状态。
  • 安全:JWT使用数字签名来保证消息完整性和真实性,并可以对负载进行加密处理。

JWT鉴权

JWT鉴权是指通过JWT来验证用户身份和权限。在使用JWT鉴权时,客户端将用户凭证(例如用户名和密码)发送给服务器,在服务器验证用户凭证有效后,生成一个JWT并将其返回给客户端。客户端在以后的请求中携带这个JWT,服务器通过验证JWT的签名和有效期等信息来验证用户身份和授权信息。

JWT鉴权流程
  1. 用户登录,向服务器提交身份凭证(例如用户名、密码)。
  2. 服务器验证身份凭证的有效性。
  3. 如果身份凭证有效,服务器生成一个JWT并将其返回给客户端。
  4. 客户端在以后的请求中携带JWT。
  5. 服务器从JWT中解析出用户ID等信息,并根据信息来验证用户身份和授权信息。
JWT鉴权实现
配置appsettings.json

我们需要在appsettings.json文件中配置JWT的相关信息。在您的ASP.NET Core项目中,找到appsettings.json文件,并添加以下配置:

"Authentication": {
    "SecretKey": "yourIssuer",
    "Issuer": "yourAudience",
    "Audience": "yourSecretKey"
  },
添加 JWT 鉴权服务

在ASP.NET Core中,可以使用JwtBearer认证方案来验证JWT。首先,在Startup.cs文件中添加以下代码:

 public void ConfigureServices(IServiceCollection services)
{
    // 添加JWT身份验证服务
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
         .AddJwtBearer(options =>
         {
             var secretByte = Encoding.UTF8.GetBytes(Configuration["Authentication:SecretKey"]);  // 从appsettings.json读取Jwt配置
             options.TokenValidationParameters = new TokenValidationParameters()
             {
                 ValidateIssuer = true,
                 ValidIssuer = Configuration["Authentication:Issuer"],

                 ValidateAudience = true,
                 ValidAudience = Configuration["Authentication:Audience"],

                 ValidateLifetime = true,
            
                 ValidateIssuerSigningKey = true,
                 IssuerSigningKey = new SymmetricSecurityKey(secretByte)
             };
         });
    // 其他服务配置
}


public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // 其他中间件配置...

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

上面代码中,我们向依赖注入容器中注册了一个身份验证方案,名称为 JwtBearerDefaults.AuthenticationScheme,表示使用 JWT 进行身份验证。然后,我们使用 AddJwtBearer 扩展方法,将 JWT 鉴权服务添加到应用程序中。

在 AddJwtBearer 方法中,我们需要配置 TokenValidationParameters 来验证 JWT。其中,ValidateIssuer、ValidIssuer、ValidateAudience、ValidAudience 和 ValidateLifetime 属性用于验证 JWT 中的发行人、接收方、有效期等信息。IssuerSigningKey 属性表示密钥,用于对 JWT 进行数字签名。最后,ValidateIssuerSigningKey 属性用于验证 JWT 的签名是否正确。

生成JWT

在ASP.NET Core中,可以使用JwtSecurityToken类来创建JWT。例如:

var signingAlgorithm = SecurityAlgorithms.HmacSha256;

var claims = new[]
{
    new Claim(JwtRegisteredClaimNames.Sub,"user_id"),
    new Claim(ClaimTypes.Role,"Admin"),
    new Claim("UserId","12"),
};

var secretByte = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]);
var signingKey = new SymmetricSecurityKey(secretByte);
var signingCredentials = new SigningCredentials(signingKey, signingAlgorithm);

var token = new JwtSecurityToken(
    issuer: _configuration["Authentication:Issuer"],
    audience: _configuration["Authentication:Audience"],
    claims,
    notBefore: DateTime.UtcNow,
    expires: DateTime.UtcNow.AddDays(1),
    signingCredentials
);

var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);

在这里,我们首先创建了一个声明(Claims)列表,其中包含用户ID、角色信息。然后,我们指定了JWT的过期时间和签名算法,并使用SymmetricSecurityKey类来指定密钥。最后,我们使用JwtSecurityTokenHandler类将token转换为字符串形式的jwt。

验证JWT
public bool ValidateAccessToken(string token)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.UTF8.GetBytes(_jwtConfig.SecretKey);
        try
        {
            tokenHandler.ValidateToken(token, new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                ValidIssuer = _jwtConfig.Issuer,
                ValidAudience = _jwtConfig.Audience,
                IssuerSigningKey = new SymmetricSecurityKey(key)
            }, out var validatedToken);
        }
        catch (Exception)
        {
            return false;
        }
        return true;
    }
}

上面代码中,我们使用 JwtSecurityTokenHandler 类来验证 JWT 的真实性和完整性。其中,我们使用 TokenValidationParameters 来配置验证参数,包括是否验证 JWT 发行人、接收方、有效期等信息,以及使用哪个密钥对其进行数字签名。如果验证通过,则返回 true,否则返回 false。

JWT授权

JWT授权是指根据JWT中包含的声明信息来验证用户是否具有访问特定资源的权限。在使用JWT授权时,我们在JWT中添加了一些声明信息,例如用户所属角色、权限等,服务器可以通过这些信息来验证用户是否有权访问特定资源。

JWT授权流程
  1. 用户登录,向服务器提交身份凭证(例如用户名、密码)。
  2. 服务器验证身份凭证的有效性。
  3. 如果身份凭证有效,服务器生成一个JWT并将其返回给客户端。
  4. 客户端在以后的请求中携带JWT。
  5. 服务器从JWT中解析出用户信息和声明信息,并根据信息来验证用户是否有权访问特定资源。
JWT授权实现
用户登录

创建传入DTO:LoginDto 、返回DTO:LoginOutDto类

public class LoginDto
{
    public string UserName { get; set; }

    public string Pwd { get; set; }
}

public class LoginOutDto
{
    public int Code { get; set; }

    public string Msg { get; set; }

    public string access_token { get; set; }

    public string refresh_token { get; set; }

}

在用户登录时,我们需要对用户提供的用户名和密码进行验证,并生成访问令牌和刷新令牌。下面是一个简单的示例,演示如何在ASP.NET Core中实现用户登录验证,并生成JWT令牌。

[HttpPost("login")]
public LoginOutDto Login([FromBody] LoginDto input)
{
    var dto = new LoginOutDto();
    try
    {
        if (input.UserName != "admin" || input.Pwd != "bb123456")
        {
            dto.Code = 500;
            dto.Msg = "用户名或密码不正确";
            dto.access_token = string.Empty;
            return dto;
        }

        // 生成访问令牌
        var accessToken = _jwtService.GenerateAccessToken();

        // 生成刷新令牌
        var refreshToken = _jwtService.GenerateRefreshToken();

        dto.Code = 200;
        dto.Msg = "登录成功";
        dto.access_token = accessToken;
        dto.refresh_token = refreshToken;
        
    }
    catch (Exception ex)
    {

        dto.Code = 500;
        dto.Msg = "登录失败:" + ex.Message;
    }

    return dto;

}

在上面的示例中,我们通过调用_jwtService.GenerateAccessToken和_jwtService.GenerateRefreshToken方法来生成访问令牌和刷新令牌,并将刷新令牌保存到数据库或其他持久化存储中,以便后续使用。

刷新令牌

在用户登录后,访问令牌会在一定时间后过期,此时用户需要使用刷新令牌来获取新的访问令牌,而无需重新登录。下面是一个简单的示例,演示如何在ASP.NET Core中实现刷新令牌功能。

[HttpPost("refresh")]
public IActionResult RefreshToken(UserModel model)
{
    // 验证刷新令牌是否有效
    var isValidRefreshToken = ValidateAccessToken(model.RefreshToken);

    if (!isValidRefreshToken)
    {
        return BadRequest(new { message = "Invalid refresh token" });
    }

    // 生成新的访问令牌
    var accessToken = _jwtService.GenerateAccessToken(model);

    // 返回新的访问令牌给客户端
    return Ok(new
    {
        access_token = accessToken
    });
}

在上面的示例中,我们通过调用_jwtService.GenerateAccessToken方法来生成新的访问令牌,并将其返回给客户端。在生成新的访问令牌时,我们可以使用之前保存的用户信息,例如用户名等

完整代码

下面是一个包含生成 JWT,解析 JWT,鉴权,授权和策略的完整示例。请注意,此示例仅供参考,请根据实际需求进行修改。

Startup.cs
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.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
             .AddJwtBearer(options =>
             {
                 var secretByte = Encoding.UTF8.GetBytes(Configuration["Authentication:SecretKey"]);
                 options.TokenValidationParameters = new TokenValidationParameters()
                 {
                     ValidateIssuer = true,
                     ValidIssuer = Configuration["Authentication:Issuer"],

                     ValidateAudience = true,
                     ValidAudience = Configuration["Authentication:Audience"],

                     ValidateLifetime = true,

                     ValidateIssuerSigningKey = true,
                     IssuerSigningKey = new SymmetricSecurityKey(secretByte)
                 };
             });
        
        // 注入IJwtService服务
        services.AddSingleton<IJwtService, JwtService>();

        services.AddControllers();
        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new OpenApiInfo { Title = "JWT.Demo", Version = "v1" });
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseSwagger();
            app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "JWT.Demo v1"));
        }

        app.UseHttpsRedirection();

        app.UseRouting();

        // 身份验证
        app.UseAuthentication();

        // 授权
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}
IJwtService&JwtService
public interface IJwtService
{
    /// <summary>
    /// 生成JWT
    /// </summary>
    /// <returns></returns>
    string GenerateAccessToken();

    /// <summary>
    /// 刷新Token
    /// </summary>
    /// <returns></returns>
    string GenerateRefreshToken();

    /// <summary>
    /// 验证JWT
    /// </summary>
    /// <param name="token"></param>
    /// <returns></returns>
    bool ValidateAccessToken(string token);
}

public class JwtService : IJwtService
{
    private readonly IConfiguration _configuration;

    public JwtService(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    /// <summary>
    /// 生成JWT
    /// </summary>
    /// <returns></returns>
    public string GenerateAccessToken()
    {
        var signingAlgorithm = SecurityAlgorithms.HmacSha256;

        var claims = new[]
        {
            new Claim(JwtRegisteredClaimNames.Sub,"user_id"),
            new Claim(ClaimTypes.Role,"Admin"),
            new Claim("UserId","12"),
        };

        var secretByte = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]);
        var signingKey = new SymmetricSecurityKey(secretByte);
        var signingCredentials = new SigningCredentials(signingKey, signingAlgorithm);

        var token = new JwtSecurityToken(
            issuer: _configuration["Authentication:Issuer"],
            audience: _configuration["Authentication:Audience"],
            claims,
            notBefore: DateTime.UtcNow,
            expires: DateTime.UtcNow.AddDays(1),
            signingCredentials
        );

        var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);

        return tokenStr;
    }

    /// <summary>
    /// 刷新Token
    /// </summary>
    /// <returns></returns>
    public string GenerateRefreshToken()
    {
        var randomNumber = new byte[32];
        using (var rng = new RNGCryptoServiceProvider())
        {
            rng.GetBytes(randomNumber);
            return Convert.ToBase64String(randomNumber);
        }
    }

    /// <summary>
    /// 验证JWT
    /// </summary>
    /// <param name="token"></param>
    /// <returns></returns>
    public bool ValidateAccessToken(string token)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]);
        try
        {
            tokenHandler.ValidateToken(token, new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                ValidIssuer = _configuration["Authentication:Issuer"],
                ValidAudience = _configuration["Authentication:Audience"],
                IssuerSigningKey = new SymmetricSecurityKey(key)
            }, out var validatedToken);
        }
        catch (Exception)
        {
            return false;
        }
        return true;
    }
}
AuthenticateController.cs
[ApiController]
[Route("auth")]
public class AuthenticateController : ControllerBase
{
    private readonly IConfiguration _configuration;
    private readonly IJwtService _jwtService;

    public AuthenticateController(IConfiguration configuration, IJwtService jwtService)
    {
        _configuration = configuration;
        _jwtService = jwtService;
    }

    [HttpPost("login")]
    public LoginOutDto Login([FromBody] LoginDto input)
    {
        var dto = new LoginOutDto();
        try
        {
            if (input.UserName != "admin" || input.Pwd != "bb123456")
            {
                dto.Code = 500;
                dto.Msg = "用户名或密码不正确";
                dto.access_token = string.Empty;
                return dto;
            }

            // 生成访问令牌
            var accessToken = _jwtService.GenerateAccessToken();

            // 生成刷新令牌
            var refreshToken = _jwtService.GenerateRefreshToken();

            dto.Code = 200;
            dto.Msg = "登录成功";
            dto.access_token = accessToken;
            dto.refresh_token = refreshToken;
            
        }
        catch (Exception ex)
        {

            dto.Code = 500;
            dto.Msg = "登录失败:" + ex.Message;
        }

        return dto;

    }

    [HttpPost("refresh")]
    public IActionResult RefreshToken(string token)
    {
        // 验证刷新令牌是否有效
        var isValidRefreshToken = _jwtService.ValidateAccessToken(token);

        if (!isValidRefreshToken)
        {
            return BadRequest(new { message = "Invalid refresh token" });
        }

        // 生成新的访问令牌
        var accessToken = _jwtService.GenerateAccessToken();

        // 返回新的访问令牌给客户端
        return Ok(new
        {
            access_token = accessToken
        });
    }
}

注意事项

  • 密钥管理:在使用JWT时,密钥是非常重要的,泄露密钥会导致安全问题。因此,密钥的生成、存储和更新都必须谨慎处理。

  • 过期时间:在生成JWT时,要指定合适的过期时间,避免JWT过期后仍然可以使用。

  • 签名算法:签名算法的选择很重要,不同的签名算法具有不同的安全性和效率。建议采用HMAC+

  • SHA256或RSA算法。

    • 不要存储敏感信息:JWT虽然安全,但仍然存在被盗用的可能性。因此,在生成JWT时,应避免将敏感信息(例如密码、信用卡号等)存储在负载中。
    • 使用HTTPS:在使用JWT时,建议采用HTTPS协议来保证通讯的安全性。
    • 谨慎处理“记住我”功能:在实现“记住我”功能时,需要谨慎处理,避免密钥泄露或用户凭证被盗用。

结论

在.NET 5 中使用 JWT 进行授权鉴权是一种安全、可靠的身份验证方式。通过添加 JWT 鉴权服务、使用 Authorize 属性启用 JWT 授权、生成和验证 JWT、使用 UseAuthentication 和 UseAuthorization 中间件来启用身份验证和授权,并为不同的 API 设置不同的授权策略,可以轻松地实现 JWT 的授权鉴权功能。

原文链接:https://www.cnblogs.com/l-monstar/archive/2023/04/21/17337768.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:ASP.NET CORE WEBAPI 登录 JWT 鉴权 ,接口权限验证 - Python技术站

(0)
上一篇 2023年4月19日
下一篇 2023年4月22日

相关文章

  • 详解c#索引(Index)和范围(Range)

    详解 C# 索引(Index)和范围(Range) 简介 C# 8.0 引入了一个新的功能,即索引和范围。 索引允许您通过使用[]操作符来访问元素的位置,而不是使用集合实例的元素本身。 而范围允许我们轻松地指定一个集合的范围,以便我们可以在此范围内迭代集合元素。如果您的工作与集合打交道,这些是您需要掌握的基本技能。 索引 索引是用于访问特定元素的整数。 索引…

    C# 2023年5月15日
    00
  • 从ASP.NET得到Microsoft Word文档的代码

    要从ASP.NET得到Microsoft Word文档,可以采用以下几个步骤: 使用C#代码生成需要的word文件内容,可以使用第三方库如NPOI或EPPlus,也可以使用Word应用程序库。 示例1:使用NPOI创建一个简单的word文件 using NPOI.XWPF.UserModel; using System.IO; public void Cre…

    C# 2023年5月31日
    00
  • C#中的GDI+图像编程详解

    “C#中的GDI+图像编程详解”是一篇介绍了GDI+在C#中的应用的技术文章,在文章中,作者详细讲述了如何使用GDI+来进行图像编程,包括图像的读取、处理、绘制等。 文章的主要内容包括: GDI+的概念及其在C#中的应用 GDI+是Windows操作系统中的图形设备接口,它可以被用于图像的读取、处理、绘制。在C#中,可以通过使用.NET框架来调用GDI+库的…

    C# 2023年5月31日
    00
  • asp.net读取excel文件的三种方法示例

    标题:ASP.NET读取Excel文件的三种方法示例 读取Excel文件是一个常见的需求,ASP.NET提供了多种方式读取Excel文件,本文将介绍三种方法示例。 1. 使用OleDbDataReader读取Excel文件 通过OleDbDataReader可以读取Excel文件的数据,需要注意的是,连接字符串中需要指定Excel文件的路径和名称,具体代码如…

    C# 2023年6月3日
    00
  • 基于C#开发中的那些编码问题(详谈)

    基于C#开发中的编码问题 在C#开发中,编码问题是一个非常重要的话题。因为不同的编码会导致程序在不同环境中的表现不同,甚至会导致程序出现异常或崩溃。下面是基于C#开发中的几个常见编码问题。 1. 字符编码问题 在C#中,字符串是以Unicode编码的方式存储的,也就是说每个字符都是由两个字节表示的。然而,在不同的环境中,字符串的编码会出现不同的情况,比如在数…

    C# 2023年6月7日
    00
  • c#获取两个特定字符之间的内容并输出的方法

    要获取两个特定字符之间的内容,可以利用C#中的字符串处理方法,具体步骤如下: 1.使用IndexOf方法找到第一个特定字符的位置,再使用LastIndexOf方法找到第二个特定字符的位置。 2.使用Substring方法获取两个字符之间的内容并输出。 下面是两个示例: 示例一:获取两个斜杠”/”之间的内容 string str = "C# 中获取两…

    C# 2023年6月7日
    00
  • unity实现弧形移动 可角度自定

    Unity实现弧形移动 可角度自定攻略 介绍 在Unity游戏开发中,经常需要实现物体从一个点移动到另一个点,而不是沿着直线运动。本文将介绍如何使用Unity实现弧形运动,并且可设定运动角度的完整攻略。 实现过程 以下是设定弧形移动的步骤: 步骤一:准备工作 在Unity中,需要先创建一个空物体Empty,将需要弧形运动的物体作为Empty的子物体,然后将所…

    C# 2023年6月3日
    00
  • C#使用BackgroundWorker控件

    下面是关于C#使用BackgroundWorker控件的完整攻略。 什么是BackgroundWorker控件? BackgroundWorker控件是C#中一种用于在后台执行操作的控件。它提供了一个简单的方法来执行长时间运行的任务而不会阻塞用户界面。它可以非常方便地执行异步操作,如下载或计算密集型任务等。 如何使用BackgroundWorker控件? 使…

    C# 2023年5月15日
    00
合作推广
合作推广
分享本页
返回顶部