IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> .NET 5.0 JWT 身份验证 -> 正文阅读

[Java知识库].NET 5.0 JWT 身份验证

介绍

本文将给出 JSON Web Tokens 的定义,并展示它们如何在 .Net 应用程序中使用。本文将参考文章 使用 .NET 5.0 Web API 和 Microsoft SQL Server 构建待办事项列表应用程序 ,其中我展示了如何使用 .NET 5.0 Web API 构建待办事项列表的后端。本文将描述 JWT 的结构,并解释如何在 .NET 应用程序中生成 JWT。

目录

  • 工具
  • JWT 定义
  • JWT结构
  • 在 .NET 应用程序中生成 JWT
  • 结论

工具

  • Visual Studio 2019

JWT 定义

JSON Web Token (JWT) 是一种开放标准,可在空间受限的环境中安全传输声明。这允许 JWT 通过 URL、POST 方法或 HTTP 标头传输。在本文中,我们将通过 POST 方法发送 JWT。

JWT 是一个标准,因为 2011 年成立的 JSON 对象签名和加密组 (JOSE) “标准化了完整性保护(签名和 MAC)和加密的机制以及密钥和算法标识符的格式,以支持使用 JSON 的协议的安全服务”。JOSE 小组建立了以 JSON 格式作为数字签名令牌安全传输声明的规范。

声明是关于对象的断言语句。在本文中,我将展示名为“name”的声明断言登录用户是“Sakhile-admin”。在这种情况下,我们的对象是用户。共有三种类型的索赔:注册索赔、公共索赔和私人索赔。在待办事项应用程序中,我们使用了注册声明和我定义的两个私有声明。

JWT结构

JWT 具有三个不同的部分,即标头、有效负载和签名。标头通常包含两个声明,用于对令牌进行签名的算法和令牌的类型。但是,只有算法声明是强制性的。有不同类型的算法用于对令牌进行签名,例如RS256、RS256等。如果没有使用算法,则声明的断言为无,并且此 JWT 是不安全的。我将展示如何创建一个不安全的 JWT。不安全的 JWT 没有签名,一些 JWT 验证库可以将这些令牌解释为有效的令牌,这可能允许某人修改令牌负载。TokenValidationParameters 是 JwtBearerOptions 的一个选项,不认为未签名的令牌有效。我将展示 .NET 应用程序将未签名的令牌视为无效并返回 401 未经授权的状态代码。

负载包含关于对象的声明。这是添加用户数据的地方。待办事项应用程序中使用的注册声明之一是过期时间,它显示令牌被视为无效的确切时刻。JWT 设置为 3 小时后到期。短期令牌有助于缓解跨站点请求伪造 (CSRF) 攻击。但是由于to-do应用的前端使用localStorage来存储token而不是Cookies,所以CSRF是不可能的。

签名是使用 Base64Url 编码的标头和有效载荷、用户提供的秘密字符串和标头中描述的算法形成的。签名验证令牌中包含的数据在令牌从发行者发送到受众以及从受众发送到发行者时没有被更改。

在 .NET 应用程序中生成 JWT

要生成 JWT,必须在我们简单的待办事项列表应用程序中编辑三个文件。startup.cs 文件、appsettings.json 文件和 AthenticationController.cs 文件。

appsettings.cs 文件包含 JWT 凭据。出于安全原因,凭据存储在此处。秘密字符串用于对 JWT 进行签名。

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "SQLConnection": "Server=.;Database=ToDoDB;Trusted_Connection=True;Integrated Security=true"
  },
  "JWT": {
    "ValidAudience": "http: //localhost:4200",
    "ValidIssuer": "http://localhost:24288",
    "Secret": "MySecretStringMuuustBeVeeeeeeeeeeryLooooooooOng"
  }
}

startup.cs 文件。在 ConfigureServices 方法中,添加了 AddAuthentication 服务来验证令牌。在 Configure 方法中,添加了 app.UseAuthentication() 以在此应用程序中使用身份验证。

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ToDoAPI.Authentication;
using ToDoAPI.Models;

namespace ToDoAPI
{
    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.AddControllers();
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "ToDoAPI", Version = "v1" });
            });

            services.AddDbContext<ApplicationDbContext>(options =>
           options.UseSqlServer(Configuration.GetConnectionString("SQLConnection")));

            services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();

            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(options =>
            {
                options.SaveToken = true;
                options.RequireHttpsMetadata = false;
                options.TokenValidationParameters = new TokenValidationParameters()
                {
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    ValidAudience = Configuration["JWT:ValidAudience"],
                    ValidIssuer = Configuration["JWT:ValidIssuer"],
                    // set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
                    ClockSkew = TimeSpan.Zero,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWT:Secret"]))
                };
            });
            services.AddCors();
        }
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseCors(options =>
            options.WithOrigins("http://localhost:4200")
            .AllowAnyMethod()
            .AllowAnyHeader());
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "ToDoAPI v1"));
            }
            app.UseAuthentication();
            app.UseRouting();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

要生成安全的 JWT,必须在 athenticationController.cs 文件中的第 33 行和第 70 行之间编写 Login 方法,如下所示。第 44 行和第 50 行中的代码显示在 JWT 中定义和使用了名称和角色私有声明。第 60 行显示了 authSigningKey 秘密和 SecurityAlgorithms.HmacSha256 算法用于对 JWT 进行签名。这产生了一个签名的 JWT。

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using ToDoAPI.Authentication;
using ToDoAPI.Models;

namespace ToDoAPI.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class AuthenticationController : ControllerBase
    {
        private readonly UserManager<ApplicationUser> userManager;
        private readonly RoleManager<IdentityRole> roleManager;
        private readonly IConfiguration _configuration;

        public AuthenticationController(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager, IConfiguration configuration)
        {
            this.userManager = userManager;
            this.roleManager = roleManager;
            _configuration = configuration;
        }

        [HttpPost]
        [Route("login")]
        public async Task<IActionResult> Login([FromBody] LoginModel model)
        {
            var user = await userManager.FindByNameAsync(model.Username);

            if (user != null && await userManager.CheckPasswordAsync(user, model.Password))
            {
                var userRoles = await userManager.GetRolesAsync(user);
                var authClaims = new List<Claim>
                {
                    new Claim("name", user.UserName),
                    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                };

                foreach (var userRole in userRoles)
                {
                    authClaims.Add(new Claim("role", userRole));
                }

                var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Secret"]));

                var token = new JwtSecurityToken(
                    issuer: _configuration["JWT:ValidIssuer"],
                    audience: _configuration["JWT:ValidAudience"],
                    expires: DateTime.Now.AddMinutes(120),
                    claims: authClaims,
                    signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)
                );

                return Ok(new
                {
                    token = new JwtSecurityTokenHandler().WriteToken(token),
                    expiration = token.ValidTo
                });
            }
            return Unauthorized();
        }

        [HttpPost]
        [Route("register")]
        public async Task<IActionResult> Register([FromBody] RegisterModel model)
        {
            var userExists = await userManager.FindByNameAsync(model.Username);

            if (userExists != null)
            {
                return StatusCode(StatusCodes.Status500InternalServerError, new Response { Status = "Error", Message = "User already exists!" });
            };

            ApplicationUser user = new ApplicationUser()
            {
                Email = model.Email,
                SecurityStamp = Guid.NewGuid().ToString(),
                UserName = model.Username
            };

            var result = await userManager.CreateAsync(user, model.Password);

            if (!result.Succeeded)
            {
                return StatusCode(StatusCodes.Status500InternalServerError, new Response { Status = "Error", Message = "User creation failed! Please check user details and try again." });
            }

            return Ok(new Response { Status = "Success", Message = "User created successfully" });
        }

        [HttpPost]
        [Route("register-admin")]
        public async Task<IActionResult> RegisterAdmin([FromBody] RegisterModel model)
        {
            var userExists = await userManager.FindByNameAsync(model.Username);

            if (userExists != null)
            {
                return StatusCode(StatusCodes.Status500InternalServerError, new Response { Status = "Error", Message = "User already exists!" });
            };

            ApplicationUser user = new ApplicationUser()
            {
                Email = model.Email,
                SecurityStamp = Guid.NewGuid().ToString(),
                UserName = model.Username
            };
            var result = await userManager.CreateAsync(user, model.Password);
            if (!result.Succeeded)
            {
                return StatusCode(StatusCodes.Status500InternalServerError, new Response { Status = "Error", Message = "User creation failed! Please check user details and try again." });
            }
            if (!await roleManager.RoleExistsAsync(UserRoles.Admin))
            {
                await roleManager.CreateAsync(new IdentityRole(UserRoles.Admin));
            }
            if (!await roleManager.RoleExistsAsync(UserRoles.User))
            {
                await roleManager.CreateAsync(new IdentityRole(UserRoles.User));
            }
            if (await roleManager.RoleExistsAsync(UserRoles.Admin))
            {
                await userManager.AddToRoleAsync(user, UserRoles.Admin);
            }
            return Ok(new Response { Status = "Success", Message = "User created successfully" });

        }
    }
}

生成的签名 JWT 如下所示。请注意,令牌具有三个编码部分。“alg”声明是 HS256,它显示了用于签署此 JWT 的算法。

.NET 5.0 JWT 身份验证
要生成不安全的 JWT,必须更改 Login 方法,如下所示。

[HttpPost]
[Route("login")]
public async Task < IActionResult > Login([FromBody] LoginModel model) {
    var user = await userManager.FindByNameAsync(model.Username);
    if (user != null && await userManager.CheckPasswordAsync(user, model.Password)) {
        var userRoles = await userManager.GetRolesAsync(user);
        var authClaims = new List < Claim > {
            new Claim("name", user.UserName),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
        };
        foreach(var userRole in userRoles) {
            authClaims.Add(new Claim("role", userRole));
        }
        var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Secret"]));
        var token = new JwtSecurityToken(issuer: _configuration["JWT:ValidIssuer"], audience: _configuration["JWT:ValidAudience"], expires: DateTime.Now.AddMinutes(120), claims: authClaims);
        return Ok(new {
            token = new JwtSecurityTokenHandler().WriteToken(token),
                expiration = token.ValidTo
        });
    }
    return Unauthorized();
}

生成的未签名 JWT 如下所示。请注意,令牌只有两个编码部分,编码后的令牌末尾有一个点。此外,“alg”声明是没有的。

.NET 5.0 JWT 身份验证
.NET 5.0 JWT 身份验证

总结

在本文中,我描述了令牌及其结构。我展示了如何在 .NET 应用程序中生成安全和不安全的 JWT。我描述了一些具有不安全令牌的应用程序可能会导致跨站点请求伪造 (CSRF) 攻击。JWT 是一种标准,它定义了一种紧凑且自包含的方式,用于在各方之间作为 JSON 对象安全地传输信息。签名的 JWT 可用于在应用程序中进行身份验证和授权。我已经展示了可以在 .NET 应用程序中轻松实现 JWT 身份验证,并且只有经过签名的 JWT 才能用于访问应用程序的端点。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-10-25 12:24:25  更:2021-10-25 12:25:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 0:14:13-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码