栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

使用JWT 身份验证在 ASP.NET Core 中构建安全的 REST API

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

使用JWT 身份验证在 ASP.NET Core 中构建安全的 REST API

介绍

在本文中,我们使用JWT 身份验证在 ASP.NET Core 中构建安全的 REST API 。我们从 JWT 的本质及其结构开始。

文章的第 1 - 4 节 解释了什么是 JWT 令牌、如何使用 .Net Core 设置它、安装所需的包、创建应用程序模型、迁移和更新数据库

第 5 - 9 节 重点介绍生成安全 JWT 令牌、使用和不使用生成的令牌进行安全调用以及注册用户。已使用 Postman 测试和触发 Web 请求以保护 API。

  1. JWT 结构

请参阅下面的 JWT 令牌。它在下面被分解和解释为标题、有效载荷、签名,

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1Njc3OCIsIm5hbWUiOiJSYWp1IEt1bWFyIiwiaWF0IjozNDU2Njd9.eJBP0IBy20JT9iwP6pHiKkFfHcbMPg_gVYKH-e5j0qk

标题

提供有关令牌 (JWT) 类型和用于签署令牌的算法(例如 RSA、SHA256)的详细信息。在上面的例子中,它是,

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

有效载荷

包含用户详细信息,也称为声明,这是要保护的数据。在上面的例子中,它是,

eyJzdWIiOiI1Njc3OCIsIm5hbWUiOiJSYWp1IEt1bWFyIiwiaWF0IjozNDU2Njd9

签名

标头、有效载荷和密钥之间的加密。在上面的例子中,它是,

eJBP0IBy20JT9iwP6pHiKkFfHcbMPg_gVYKH-e5j0qk
  1. 让我们从安装必需的包开始

使用 API 模板创建一个新的 ASP.NET Core 应用程序并安装以下包。目标框架是3.1及以上。

Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
Install-Package Microsoft.AspNetCore.Identity.EntityframeworkCore
Install-Package Microsoft.EntityframeworkCore
Install-Package Microsoft.EntityframeworkCore.Design
Install-Package Microsoft.EntityframeworkCore.SqlServer
Install-Package Microsoft.EntityframeworkCore.Tools
Install-Package Microsoft.VisualStudio.Web.CodeGeneration.Design
Install-Package System.IdentityModel.Tokens.Jwt

3(一)。如图所示在代码中配置JWT

将以下内容添加到 appsettings.json,

"JWT": {
    "key": "C1CF4B7DC4C4175B6618DE4F55CA4",
    "Issuer": "MySecureRestApi",
    "Audience": "AsecureRestApiUser",
    "DurationInMinutes": 20
}

为上述设置创建一个相应的类,即 Settings/JWT.cs ,它将用于使用 ASP.NET Core 的 IOptions 功能从我们之前创建的 appsettings.json 的 JWT 部分读取数据。

public class JWTSettings {
    public string Key {
        get;
        set;
    }
    public string Issuer {
        get;
        set;
    }
    public string Audience {
        get;
        set;
    }
    public double DurationInMinutes {
        get;
        set;
    }
}

然后,将以下类添加到您的项目中:

DbContext - 在 appsettings.json 中添加连接字符串
源自 IdentityUser 的 ApplicationUser
3(b)。如图所示在代码中配置JWT

要配置身份验证,请将代码添加到 ConfigureServices 方法,如下所示 -

public void ConfigureServices(IServiceCollection services) {
    Line 1 //The JWT Configuration from AppSettings
    services.Configure < JWTSettings > (_configuration.GetSection("JWT"));
    //The User Manager Service
    services.AddIdentity < ApplicationUser, IdentityRole > ().AddEntityframeworkStores < ApplicationDbContext > ();
    services.AddScoped < IUserService, UserService > ();
    //Adding DB Context with MSSQL
    services.AddDbContext < ApplicationDbContext > (options => options.UseSqlServer(_configuration.GetConnectionString("DefaultConnection"), b => b.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)));
    //The JWT Athentication
    services.AddAuthentication(options => {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    }).AddJwtBearer(oa => {
        oa.RequireHttpsmetadata = false;
        oa.SaveToken = false;
        oa.TokenValidationParameters = new TokenValidationParameters {
            ValidateIssuerSigningKey = true,
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ClockSkew = TimeSpan.Zero,
                ValidIssuer = _configuration["JWT:Issuer"],
                ValidAudience = _configuration["JWT:Audience"],
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Key"]))
        };
    });
    services.AddControllers();
}

4.对于数据库迁移

在 APP Settings.json 中添加连接字符串。对于创建数据库,使用 Entity framework Core 的代码优先方法。

在执行应用程序之前,请在包管理器控制台上运行以下命令以应用迁移。

add-migration “migrationDB”
update-database

在成功迁移后创建一组 ASPNET 用户和角色表,见下文,

  1. 注册用户

创建 具有以下属性的 Models/RegisterModel.cs。用户必须使用此对象发布数据才能注册。

public class RegisterModel {
    [Required]
    public string FirstName {
        get;
        set;
    }
    [Required]
    public string LastName {
        get;
        set;
    }
    [Required]
    public string Username {
        get;
        set;
    }
    [Required]
    public string Email {
        get;
        set;
    }
    [Required]
    public string Password {
        get;
        set;
    }
}

在 IUserService.cs 中,将以下 2 个函数定义添加到接受注册模型的注册用户。

Task RegisterAsync(RegisterModel model);
Task GetTokenAsync(TokenRequestModel model);

转到具体类 UserService 来实现注册功能。

public async Task < string > RegisterAsync(RegisterModel model) {
    var user = new ApplicationUser {
        UserName = model.Username,
            Email = model.Email,
            FirstName = model.FirstName,
            LastName = model.LastName
    };
    var userWithSameEmail = await _userManager.FindByEmailAsync(model.Email);
    if (userWithSameEmail == null) {
        var result = await _userManager.CreateAsync(user, model.Password);
        if (result.Succeeded) {
            return $ "Success: User Registered with username {user.UserName}";
        } else {
            string descr = "";
            if (result.Errors.Any()) {
                foreach(var item in result.Errors) {
                    descr += item.Description;
                }
            }
            return $ "Error(s), registering user : {descr}";
        }
    } else {
        return $ "Error(s), Email {user.Email } is already registered.";
    }
}

在上面的代码片段中,我们接受 RegisterModel 对象,执行验证并在 DB 中创建用户,否则返回相应的错误消息。

从控制器,呼叫将如下所示,

[HttpPost("CreateUser")]
public async Task < ActionResult > RegisterAsync(RegisterModel model) {
    var result = await _userService.RegisterAsync(model);
    return Ok(result);
}
  1. 用 Postman 测试

打开 Postman 并定义一个要发布到/api/user/CreateUser的原始 JSON 对象 。检查以下屏幕截图,成功后,我们会收到一条确认的用户创建消息。

7.生成JWT令牌

让我们尝试获取 JWT 令牌。我们将构建一个令牌生成函数,它接受一个 TokenRequestModel(电子邮件、密码),验证它们,并为我们构建一个令牌。

以下是令牌Models/TokenRequestModel.cs和Models/AuthenticationModel.cs 的模型类 ,

public class TokenRequestModel {
    [Required]
    public string Email {
        get;
        set;
    }
    [Required]
    public string Password {
        get;
        set;
    }
}

另一个类 AuthenticationModel.cs 基本上是来自 API 端点的响应。此端点将返回状态消息、用户详细信息,最后返回我们的令牌。

public class AuthenticationModel {
    public string Message {
        get;
        set;
    }
    public bool IsAuthenticated {
        get;
        set;
    }
    public string UserName {
        get;
        set;
    }
    public string Email {
        get;
        set;
    }
    public string Token {
        get;
        set;
    }
}

在 UserService 的具体类中检查以下实现以生成令牌,

public async Task < AuthenticationModel > GetTokenAsync(TokenRequestModel model) {
    var authenticationModel = new AuthenticationModel();
    var user = await _userManager.FindByEmailAsync(model.Email);
    if (user == null) {
        authenticationModel.IsAuthenticated = false;
        authenticationModel.Message = $ "No Accounts Registered with {model.Email}.";
        return authenticationModel;
    }
    if (await _userManager.CheckPasswordAsync(user, model.Password)) {
        authenticationModel.IsAuthenticated = true;
        JwtSecurityToken jwtSecurityToken = await CreateJwtToken(user);
        authenticationModel.Token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
        authenticationModel.Email = user.Email;
        authenticationModel.UserName = user.UserName;
        return authenticationModel;
    }
    authenticationModel.IsAuthenticated = false;
    authenticationModel.Message = $ "Incorrect Credentials for user {user.Email}.";
    return authenticationModel;
}
private async Task < JwtSecurityToken > CreateJwtToken(ApplicationUser user) {
    var userClaims = await _userManager.GetClaimsAsync(user);
    var roles = await _userManager.GetRolesAsync(user);
    var roleClaims = new List < Claim > ();
    for (int i = 0; i < roles.Count; i++) {
        roleClaims.Add(new Claim("roles", roles[i]));
    }
    var claims = new [] {
        new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            new Claim(JwtRegisteredClaimNames.Email, user.Email),
            new Claim("uid", user.Id)
    }.Union(userClaims).Union(roleClaims);
    var symmetricSecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwt.Key));
    var signingCredentials = new SigningCredentials(symmetricSecurityKey, SecurityAlgorithms.HmacSha256);
    var jwtSecurityToken = new JwtSecurityToken(issuer: _jwt.Issuer, audience: _jwt.Audience, claims: claims, expires: DateTime.UtcNow.AddMinutes(_jwt.DurationInMinutes), signingCredentials: signingCredentials);
    return jwtSecurityToken;
}

以下是上述代码功能中的活动,

第 4 行 到第 11 行 检查提供的电子邮件和密码的有效性,如果不是,则返回一条消息。

第 14 行 调用 CreateJWTToken 函数。此函数构建 JWT。它获取用户的所有声明(用户详细信息)以及角色(如果有)。

最后, 第 51 行到第 57 行 创建一个新的 JWT 安全令牌并返回它们。

现在,将此函数连接到Controllers/UserController.cs

[HttpPost("GenerateToken")]
public async Task < IActionResult > GetTokenAsync(TokenRequestModel model) {
    var result = await _userService.GetTokenAsync(model);
    if (string.IsNullOrEmpty(result.Token)) return NotFound(" " + result.Message);
    return Ok(result.Token);
}

接下来,将有效的电子邮件和密码请求发送到 …/api/user/GenerateToken。有关 JWT 令牌和包含用户详细信息的 AuthenticationModel 对象的成功响应,请参见下面的 Postman 屏幕截图。此令牌的有效期为 20 分钟(因为 app.json 中的过期时间设置为 20 分钟)。

在为同一用户发布无效密码时,PostMan 返回以下消息:“用户凭据不正确:johnk@gmail.com”。

  1. 安全地访问数据库信息,即使用生成的令牌。

我之前在我的数据库中创建了几个城市,我将使用此不记名令牌访问这些城市。为此,我需要在 Controller 类中使用 [Authorize] 关键字装饰 Get City 端点。参考下面的代码库,

[Authorize]
[HttpGet]
[Route("AllCities")]
public IActionResult GetAllCities(string Email) {
    if (appdbcontext.CountryInfos == null || !appdbcontext.CountryInfos.Any()) return Ok("No Country-Cities created as yet.");
    var res = appdbcontext.CountryInfos.Where(aa => aa.Email == Email).
    Select(xx => new {
        City = xx.City,
            Email = xx.Email,
            Isfavourite = xx.Isfavourite
    });
    if (res.Count() < 1) {
        return NotFound("No records against this UserEmail" + Email);
    } else return Ok(res);
}

现在复制上一个片段中的承载令牌并在 POSTMAN 中触发此 GET 请求,…api/user/allcities?email 以返回城市。如需成功响应,请参阅以下屏幕,

结论

我试图涵盖 JWT 的基础知识、生成令牌、使用此令牌调用保护 API 端点、注册用户、数据库迁移、实体框架核心 - 代码优先。在接下来的文章中,将重点关注刷新令牌和向用户添加角色。

REST API 安全最佳实践

以下是 REST API 的一些通用安全设计指南和最佳实践:

始终以最低权限启动 REST API 访问。默认的 API 访问应该是拒绝访问。如果用户已通过身份验证,则默认访问权限应为对尽可能少的数据进行只读访问。可以为更高的角色添加更多的数据访问权限。
使用 HTTPS 确保 REST API 始终安全,并遵循最新的更新建议,例如 HTTPS 2.0。
不要在用户 ID、密码或电子邮件等登录字段中传递纯文本。始终传递安全的加密请求。
确保密码不是纯文本。使用哈希保存密码并使用重置密码选项。
切勿在 URL 中公开变量和其他数据。
如果可能的话,用时间戳、客户端信息和用户记录每个请求。
在开始将输入参数传递给后端之前,请始终对其进行验证。
使用标准和现代授权最佳实践,例如 OAuth。
让您自己和您的 API 了解最新的更新和建议。
确保 Web 服务器具有最新的补丁和安装。
感谢您的阅读,请在评论部分告诉我您的问题、想法或反馈。我感谢您的反馈和鼓励。
推荐一个有趣的网站,这里有丰富的代码教程及开发项目供参考学习,点击查看。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/276616.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号