8.2 JWT(代替Session)

简介: 经典的保持登陆状态的办法是Session,也就是用户登陆后,服务器产生唯一标识SessionId,并把SessionId和登陆的用户信息保存在服务器内存中,通时将SessionId发送给浏览器,当浏览器再次访问的时候,http请求中便携带了SessionId,服务器根据该Id在内存中取到用户信息,实现了登陆功能。

8.2 JWT(代替Session)

经典的保持登陆状态的办法是Session,也就是用户登陆后,服务器产生唯一标识SessionId,并把SessionId和登陆的用户信息保存在服务器内存中,通时将SessionId发送给浏览器,当浏览器再次访问的时候,http请求中便携带了SessionId,服务器根据该Id在内存中取到用户信息,实现了登陆功能。

Session的缺点:

  • 登陆用户多的时候,会占用服务器大量内存
  • 每次客户端请求都要向服务器获取一次Session,导致请求速度响应慢

现在多采用JWT代替Session,JWT使用JSON来保存令牌信息,并不把信息保存在服务端,而是保存在客户端。JWT的结构包括头部、负载和签名:

头部中保存的是加密算法说明,负载中保存用户信息,签名是根据头部和负责经过算法算出来的值

JWT的登陆流程:

  • 客户端向服务单发送登陆请求
  • 服务端检验用户名密码,如果成功将从数据库中提取出这个用户的Id、角色等信息
  • 服务端采用自定义的秘钥对用户信息(JSON)进行签名,形成签名数据
  • 将用户信息(JSON)和上一步形成的签名拼接形成JWT,发送给客户端
  • 客户端每次请求都带上这个JWT
  • 每次服务器收到带JWT的请求后,使用自定义的秘钥对JWT的签名进行校验,如果成功则从JWT中取出用户信息

JWT基本使用

  1. NuGet安装System.IdentityModel.Tokens.Jwt
  2. 生成JWT的程序

usingMicrosoft.IdentityModel.Tokens;

usingSystem.IdentityModel.Tokens.Jwt;

usingSystem.Security.Claims;

usingSystem.Text;

//每个claim代表一条信息,一个用户可能有多条信息如Id,name,所以这里用列表

varclaims=newList<Claim>();

//Claim具有两个属性Type和Value,他们都是string类型 Type:用户信息类型 Value:用户信息的值

claims.Add(newClaim(ClaimTypes.NameIdentifier, "6"));

claims.Add(newClaim(ClaimTypes.Name, "yzk"));

claims.Add(newClaim(ClaimTypes.Role, "User"));

claims.Add(newClaim(ClaimTypes.Role, "Admin"));//一个Type下允许有多个value

claims.Add(newClaim("PassPort", "E90000082"));

//签名秘钥,自定义,长一些安全

stringkey="fasdfad&9045dafz222#fadpio@0232";

DateTimeexpires=DateTime.Now.AddDays(1);//设置令牌过期时间

{//根据过期时间、多个claim、秘钥生成JWT

    byte[] secBytes=Encoding.UTF8.GetBytes(key);

    varsecKey=newSymmetricSecurityKey(secBytes);

    varcredentials=newSigningCredentials(secKey,SecurityAlgorithms.HmacSha256Signature);

    vartokenDescriptor=newJwtSecurityToken(claims: claims,

   expires: expires, signingCredentials: credentials);

    stringjwt=newJwtSecurityTokenHandler().WriteToken(tokenDescriptor);

}

Console.WriteLine(jwt);

JWT其实是明文,所以不要将重要的信息放到JWT中,为了防止篡改JWT,服务器需要对JWT签名进行验证

  1. JWT验证

stringjwt=Console.ReadLine()!;

stringsecKey="fasdfad&9045dafz222#fadpio@0232";//与之前的保持一致

JwtSecurityTokenHandlertokenHandler=new();

TokenValidationParametersvalParam=new();

varsecurityKey=newSymmetricSecurityKey(Encoding.UTF8.GetBytes(secKey));

valParam.IssuerSigningKey=securityKey;

valParam.ValidateIssuer=false;

valParam.ValidateAudience=false;

//解密

ClaimsPrincipalclaimsPrincipal=tokenHandler.ValidateToken(jwt,

        valParam, outSecurityTokensecToken);

foreach (varclaiminclaimsPrincipal.Claims)

{

    Console.WriteLine($"{claim.Type}={claim.Value}");

}

ASP.NET Core对JWT的封装

  1. 在配置系统中增加JWT节点,节点下设置秘钥和过期时间,再创建一个对应的配置类

"JWT": {

 "SigningKey": "fasdfad&9045dafz222#fadpio@0232",

 "ExpireSeconds": "86400"

}

publicclassJWTOptions

{

   publicstringSigningKey { get; set; }

   publicintExpireSeconds { get; set; }

}

  1. NuGet安装Microsoft.AspNetCore.Authentication.JwtBearer
  2. 对JWT进行配置在builder.Build之前添加

services.Configure<JWTOptions>(builder.Configuration.GetSection("JWT"));//实体配置类

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)//配置授权的各种属性

.AddJwtBearer(x=>//配置JWT的承载

{

   //配置JWT绑定到JWTOptions新的实例,返回一个JWTOptions实例

    JWTOptions?jwtOpt=builder.Configuration.GetSection("JWT").Get<JWTOptions>();

    byte[] keyBytes=Encoding.UTF8.GetBytes(jwtOpt.SigningKey);

    varsecKey=newSymmetricSecurityKey(keyBytes);

    x.TokenValidationParameters=new()//设置令牌验证参数

    {

        ValidateIssuer=false,

        ValidateAudience=false,

        ValidateLifetime=true,

        ValidateIssuerSigningKey=true,

        IssuerSigningKey=secKey

    };

});

  1. 在Program.cs中的app.UseAuthorization()前面加上app.UseAuthentication();
  2. 在控制类中增加登陆并且创建JWT的操作方法

publicrecordLoginRequest (stringUserName,stringPassword)

 

[HttpPost]

publicasyncTask<IActionResult>Login2(LoginRequestreq,

            [FromServices] IOptions<JWTOptions>jwtOptions)

{

    stringuserName=req.UserName;

    stringpassword=req.Password;

    varuser=awaituserManager.FindByNameAsync(userName);

    if (user==null)

    {

        returnNotFound($"用户名不存在{userName}");

    }

    varsuccess=awaituserManager.CheckPasswordAsync(user, password);

    if (!success)

    {

        returnBadRequest("Failed");

    }

   //登陆成功

    varclaims=newList<Claim>();

    claims.Add(newClaim(ClaimTypes.NameIdentifier, user.Id.ToString()));

    claims.Add(newClaim(ClaimTypes.Name, user.UserName));

    varroles=awaituserManager.GetRolesAsync(user);

    foreach (stringroleinroles)

    {

        claims.Add(newClaim(ClaimTypes.Role, role));

    }

    stringjwtToken=BuildToken(claims, jwtOptions.Value);//登陆成功后创建JWT

    returnOk(jwtToken);//将jwt返回给前端

}

 

//创建JWT

privatestaticstringBuildToken(IEnumerable<Claim>claims, JWTOptionsoptions)

{

    DateTimeexpires=DateTime.Now.AddSeconds(options.ExpireSeconds);

    byte[] keyBytes=Encoding.UTF8.GetBytes(options.SigningKey);

    varsecKey=newSymmetricSecurityKey(keyBytes);

    varcredentials=newSigningCredentials(secKey,

        SecurityAlgorithms.HmacSha256Signature);

    vartokenDescriptor=newJwtSecurityToken(expires: expires,

        signingCredentials: credentials, claims: claims);

    return"Bearer "+newJwtSecurityTokenHandler().WriteToken(tokenDescriptor);

}

  1. 在需要登陆才能访问的控制器类上添加[Authorize]

[Route("[controller]/[action]")]

[ApiController]

[Authorize]

publicclassTest2Controller : ControllerBase

{

    [HttpGet]

    publicIActionResultHello()

    {

       //ControllerBase类中定义的User属性中含有当前登陆用户的所有信息

        stringid=this.User.FindFirst(ClaimTypes.NameIdentifier)!.Value;//!不为空

        stringuserName=this.User.FindFirst(ClaimTypes.Name)!.Value;

        returnOk($"id={id},userName={userName}");

    }

}

在请求的时候,ASP.Net Core要求,JWT要放到请求报文头中,其key为Authorization ,值为Bearer JWT值。

对于客户端获得的JWT,在前端项目中可以将其保存在Cookie或者LocalStorage等位置,在退出的时候进行删除。

[Authorize]注意事项

[Authorize]可以加到控制器上也可以加到操作方法中,在控制器上标注表示该控制器下所有的操作方法都需要身份验证,如果某个操作方法不需要身份验证则在该操作方法上标注[AllowAnonymous]

在Swagger带有Authorize按钮

修改Program.cs

builder.Services.AddSwaggerGen(c=>

{

   varscheme=newOpenApiSecurityScheme()

   {

       Description="Authorization header. \r\nExample: 'Bearer 12345abcdef'", //描述

       Reference=newOpenApiReference{Type=ReferenceType.SecurityScheme,

           Id="Authorization"},

       Scheme="oauth2",Name="Authorization",

       In=ParameterLocation.Header,Type=SecuritySchemeType.ApiKey,

   };

   c.AddSecurityDefinition("Authorization", scheme);

   varrequirement=newOpenApiSecurityRequirement();

   requirement[scheme] =newList<string>();

   c.AddSecurityRequirement(requirement);

});

界面如图:

在对话框中输入Bearer JWT

相关文章
|
存储 算法 NoSQL
还分不清 Cookie、Session、Token、JWT?看这一篇就够了
Cookie、Session、Token 和 JWT(JSON Web Token)都是用于在网络应用中进行身份验证和状态管理的机制。虽然它们有一些相似之处,但在实际应用中有着不同的作用和特点,接下来就让我们一起看看吧,本文转载至http://juejin.im/post/5e055d9ef265da33997a42cc
37487 10
|
存储 JSON 安全
每日一博 - 闲聊 Session、cookie、 JWT、token、SSO、 OAuth 2.0
每日一博 - 闲聊 Session、cookie、 JWT、token、SSO、 OAuth 2.0
92 0
|
18天前
|
JavaScript
Node.js单点登录SSO详解:Session、JWT、CORS让登录更简单(二)
Node.js单点登录SSO详解:Session、JWT、CORS让登录更简单(一)
22 0
|
18天前
|
存储 JSON JavaScript
Node.js单点登录SSO详解:Session、JWT、CORS让登录更简单(一)
Node.js单点登录SSO详解:Session、JWT、CORS让登录更简单(一)
62 0
|
3月前
|
存储 JSON JavaScript
震撼!Cookie、Session、Token、JWT 终极对决:揭开 Web 认证的神秘面纱!
【8月更文挑战第13天】Web 开发中,Cookie、Session、Token 和 JWT 常混淆。Cookie 是服务器给客户端的小信息片,如登录状态,每次请求都会返回。Session 则是服务器存储的用户数据,通过 Session ID 追踪。Token 类似通行证,证明客户端身份且可加密。JWT 是结构化的 Token,含头部、载荷及签名,确保数据完整性和安全性。
64 4
|
6月前
|
存储 JSON JavaScript
session和JWT的应用及区别
在Node.js中实现登录认证,可以使用`express-session`进行Session管理。首先安装`express-session`,然后全局引入并配置。登录成功后,存储用户信息和登录状态至Session,之后可从Session中获取或销毁用户信息。另外,JWT(Json Web Token)也是一个选择。JWT包含header、payload和signauter三部分,通过`jsonwebtoken`包生成和解密Token,设置秘钥和过期时间。使用`express-jwt`进行解密,并配置全局错误处理中间件来处理无效Token的情况。
46 2
|
6月前
|
存储 JSON Java
面试官:Session和JWT有什么区别?
JSON Web Token (JWT) 是一种开放标准,用于安全地在网络上传输信息。JWT 包含头部、载荷和签名三部分,常用于身份验证和授权。与Session相比,JWT有以下优势:无服务器存储状态,支持跨域,适应微服务架构,自包含且可扩展。在Java开发中,可以使用HuTool框架操作JWT,包括生成、验证和解析Token。JWT通过在客户端存储令牌实现无状态认证,与Session的主要区别在于工作原理、存储方式和有效期管理。
116 6
|
存储 数据采集 前端开发
Node.js----前后端的身份认证(session与jwt)(一)
Node.js----前后端的身份认证(session与jwt)
|
6月前
|
存储 JSON 安全
Session 与 JWT 的对决:谁是身份验证的王者? (下)
Session 与 JWT 的对决:谁是身份验证的王者? (下)
Session 与 JWT 的对决:谁是身份验证的王者? (下)
|
6月前
|
存储 JSON API
Session 与 JWT 的对决:谁是身份验证的王者? (上)
Session 与 JWT 的对决:谁是身份验证的王者? (上)
Session 与 JWT 的对决:谁是身份验证的王者? (上)