首页 > 编程语言 >Asp.Net Core鉴权授权:JWT基本使用

Asp.Net Core鉴权授权:JWT基本使用

时间:2022-09-21 23:35:17浏览次数:59  
标签:Core Asp 服务器端 JWT new claims string 客户端

实现用户登录功能的经典做法是用Session,但是在前后端分离、分布式环境下已经不适应了,而现在我们倾向于采用JWT代替Session实现登录。

JWT全称是JSON web token,它是使用JSON格式来保存令牌信息的。JWT机制不是把用户的登录信息保存在服务器端,而是把登录信息(也叫作令牌)保存在客户端。为了防止客户端的数据造假,保存在客户端的令牌经过了签名处理,而签名的密钥只有服务器端才知道,每次服务器端收到客户端提交的令牌的时候都要检查一下签名,如果发现数据被篡改,则拒绝接收客户端提交的令牌。
image

  • 头部(header)中保存的是加密算法的说明。
  • 负载(payload)中保存的是用户的ID、用户名、角色等信息。
  • 签名(signature)是根据头部和负载一起算出来的值。

JWT实现登录的流程如下。

  1. 客户端向服务器端发送用户名、密码等请求登录。
  2. 服务器端校验用户名、密码,如果校验成功,则从数据库中取出这个用户的ID、角色等用户相关信息。
  3. 服务器端采用只有服务器端才知道的密钥来对用户信息的JSON字符串进行签名,形成签名数据。
  4. 服务器端把用户信息的JSON字符串和签名拼接到一起形成JWT,然后发送给客户端。
  5. 客户端保存服务器端返回的JWT,并且在客户端每次向服务器端发送请求的时候都带上这个JWT。
  6. 每次服务器端收到浏览器请求中携带的JWT后,服务器端用密钥对JWT的签名进行校验,如果校验成功,服务器端则从JWT中的JSON字符串中读取出用户的信息。

这样服务器端就知道这个请求对应的用户了,也就实现了登录的功能。

由此可以看出,在JWT机制下,登录用户的信息保存在客户端,服务器端不需要保存数据,这样我们的程序就天然地适合分布式的集群环境,而且服务器端从客户端请求中就可以获取当前登录用户的信息,不需要再去状态服务器中获取,因此程序的运行效率更高。虽然用户信息保存在客户端,但是由于有签名的存在,客户端无法篡改这些用户信息,因此可以保证客户端提交的JWT的可信度。

JWT的基本使用

生成JWT令牌

我们先创建一个控制台程序生成JWT,需要安装JWT读写的NuGet包System.IdentityModel.Tokens.Jwt

var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.NameIdentifier, "6"));
claims.Add(new Claim(ClaimTypes.Name, "路飞"));
claims.Add(new Claim(ClaimTypes.Role, "User"));
claims.Add(new Claim(ClaimTypes.Role, "Admin"));
claims.Add(new Claim("jz", "112233"));


string key = "kjdfsjffd^kjfkfkds#dsffdsdsfd@fdsufdsfo33300";// 秘钥
DateTime expires = DateTime.Now.AddDays(1); // 过期时间

// 下面进行加密生成JWT
byte[] secBytes = Encoding.UTF8.GetBytes(key);
var secKey = new SymmetricSecurityKey(secBytes);
var credentials = new SigningCredentials(secKey, SecurityAlgorithms.HmacSha256Signature);
var tokenDescriptor = new JwtSecurityToken
(claims: claims,expires: expires, signingCredentials: credentials);
string jwt = new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);

Console.WriteLine(jwt);

一个用户的信息可能会包含多项内容,比如身份证号、生日、邮箱、地址等,在.NET中Claim就代表一条用户信息。Claim有两个主要的属性:Type和Value,它们都是string类型的,Type代表用户信息的类型,Value代表用户信息的值。由于Type是string类型的,因此可以取任何值,也可以自定义。
我们根据多个Claim对象、过期时间、密钥来生成JWT。

运行程序生成如下:
eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjYiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoi6Lev6aOeIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjpbIlVzZXIiLCJBZG1pbiJdLCJqeiI6IjExMjIzMyIsImV4cCI6MTY2Mzg1ODgwMX0._hH5ohaJatky2Nk_SH0XenuaWpYhYslwcy94NPjWz_g

JWT被句点分隔成了3部分,分别是头部、负载和签名。
JWT看起来很乱,好像是加密过的,其实它们都是明文存储的,只不过进行了简单的编码而已。JWT中使用Base64URL算法对字符串进行编码,这个算法跟Base64算法基本相同,考虑到JWT可能会被放到URL中,而Base64有3个特殊字符+、/和=,它们在URL里面有特殊含义,因此我们需要从Base64中删除=,并且把+替换成-、把/替换成_。

解码JWT令牌

再创建一个控制台项目,进行解码操作。

string jwt = Console.ReadLine()!;
string[] segments = jwt.Split('.');
string head = JwtDecode(segments[0]); // 头部
string payload = JwtDecode(segments[1]); // 负载
Console.WriteLine("--------head--------");
Console.WriteLine(head);
Console.WriteLine("--------payload--------");
Console.WriteLine(payload);

string JwtDecode(string s)
{
    s = s.Replace('-', '+').Replace('_', '/');
    switch (s.Length % 4)
    {
        case 2:
            s += "==";
            break;
        case 3:
            s += "=";
            break;
    }
    var bytes = Convert.FromBase64String(s); // 解码
    return Encoding.UTF8.GetString(bytes);
}

调用自定义的JwtDecode方法对头部和负载部分进行解码。JwtDecode方法除了做简单的字符串替换之外,还对替换后的字符串进行Base64解码。
这段对JWT进行解码的代码中没有用到任何密钥,只是简单地解码,因为JWT的头部和负载部分都没有加密,实质上都是以明文的形式保存的。

运行结果如下:
--------head--------
{"alg":"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256","typ":"JWT"}
--------payload--------
{"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier":"6","http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name":"路飞","http://schemas.microsoft.com/ws/2008/06/identity/claims/role":["User","Admin"],"jz":"112233","exp":1663858801}

从程序运行结果可以看出,JWT的头部中本质上以明文的形式记录了JWT的签名使用的哈希算法,负载中本质上也以明文的形式记录了我们设置的多条Claim信息。由于JWT会被发送到客户端,而负载中的内容是以明文形式保存的,因此一定不要把不能被客户端知道的信息放到负载中。

JWT的编码和解码规则都是公开的,而且负载部分的Claim信息也是明文的,因此恶意攻击者可以对负载部分中的用户ID等信息进行修改,从而冒充其他用户的身份来访问服务器上的资源。因此,服务器端需要对签名部分进行校验,从而检查JWT是否被篡改了。

我们可以调用JwtSecurityTokenHandler类对JWT进行解码,因为它会在对JWT解码前对签名进行校验。

string jwt = Console.ReadLine()!;
string secKey = "kjdfsjffd^kjfkfkds#dsffdsdsfd@fdsufdsfo33300";
JwtSecurityTokenHandler tokenHandler = new();
TokenValidationParameters valParam = new();
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secKey));
valParam.IssuerSigningKey = securityKey;
valParam.ValidateIssuer = false;
valParam.ValidateAudience = false;
// 调用ValidateToken方法对JWT进行解密
ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(jwt,
        valParam, out SecurityToken secToken);
foreach (var claim in claimsPrincipal.Claims)
{
    Console.WriteLine($"{claim.Type}={claim.Value}");
}

如果我们输入的是服务器端返回的JWT,上面的代码能够正常运行。
运行结果如下:
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier=6
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name=路飞
http://schemas.microsoft.com/ws/2008/06/identity/claims/role=User
http://schemas.microsoft.com/ws/2008/06/identity/claims/role=Admin
jz=112233
exp=1663858801
但是如果我们篡改JWT,程序运行时就会抛出内容为“Signature validation failed”的异常。exp值是过期时间,如果收到过期的JWT,即使签名校验成功,ValidateToken方法也会抛出异常。

总之,JWT机制让我们可以把用户的信息保存到客户端,每次客户端向服务器端发送请求的时候,客户端只要把JWT发送到服务器端,服务器端就可以得知当前请求用户的信息,而通过签名的机制则可以避免JWT内容被篡改。

标签:Core,Asp,服务器端,JWT,new,claims,string,客户端
From: https://www.cnblogs.com/nullcodeworld/p/16717615.html

相关文章

  • Asp.Net Core鉴权授权:标识框架identity
    在一个系统中,不是所有功能都能被自由地访问的,比如有的功能需要注册用户才能访问,有的功能需要VIP用户才能访问。针对资源的访问限制有两个概念:Authentication与Authorizatio......
  • Linux(Debian) 配置netcore环境
    一、准备工作    ①、开启粘贴板#进入defaults.vim【未安装vim,按需安装】linaro@linaro-alip:~$sudovi/usr/share/vim/vim81/defaults.vim#将setmouse=......
  • 微服务系列之授权认证(三) JWT
    1.JWT简介官方定义:JWT是JSONWebToken的缩写,JSONWebToken是一个开放标准(RFC7519),它定义了一种紧凑的、自包含的方式,可以将各方之间的信息作为JSON对象安全地传......
  • EF Core Database Migration
    1.准备工作1.1创建项目(以下使用.NETCore3.1)1.1创建项目ASP.NETCoreWebAPI,命名为WebApi1.2新建项目.NETCore的类库,命名为Core1.3新建项目.NETCore的类库,......
  • .Net Aspose.Words 生成Word文档
    .NetAspose.Words生成Word文档在开发WinForm项目中,有一需求要生成Word文档,百度学习,记录一下实现方法NuGet包,找到 Aspose.Words安装21.8.0或以下 版本,否则高版本即......
  • .NET 6 EFCore WebApi 使用 JMeter 进行吞吐量测试
    .NET6EFCoreWebApi使用JMeter进行吞吐量测试开发环境VS2022.NET6测试环境测试工具接口压力测试工具:JMeter数据库MySQL5.7数据库和WebApi服务在同一台服务......
  • 【HMS Core】使用视频编辑AI能力SDK报错2012
    ​问题描述在使用视频编辑AI能力SDK报错20128详细报错信息2022-09-0516:16:48.3065571-6445/cn.c7cloud.ShortVideoAlbumE/FileUtil:IOException:java.io.FileNotF......
  • JasperReport-PDF报表打印概述
    3PDF报表打印概述3.1概述在企业级应用开发中,报表生成、报表打印下载是其重要的一个环节。在之前的课程中我们已经学习了报表中比较重要的一种:Excel报表。其实除了Excel......
  • JasperReport-父子报表
    4父子报表4.1概述复杂报表或数据内容较多的时候,可以使用子报表解决。4.2制作父报表首先制作父报表,就是调用子报表的一个基础报表。主报表的作用有如下两种:父报表中......
  • JasperReport-用户档案下载
    5用户档案下载5.1搭建环境(1)配置坐标 <dependency><groupId>net.sf.jasperreports</groupId><artifactId>jasperreports</artifactId......