如何使用JWT进行身份验证与授权
Posted dotNET跨平台
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用JWT进行身份验证与授权相关的知识,希望对你有一定的参考价值。
简介
JWT定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。
工作流程
1、用户使用账号、密码登录应用,登录的请求发送到认证服务器。
2、认证服务器进行用户验证,然后创建JWT字符串返回给客户端。
3、客户端请求接口时,在请求头带上JWT。应用服务器验证JWT合法性,如果合法则继续调用应用接口返回结果。
数据结构
JWT由3部分组成:标头(Header)、有效载荷(Payload)和签名(Signature)。在传输的时候,会将JWT的3部分分别进行Base64编码后用.进行连接形成最终传输的字符串JWT由3部分组成:标头(Header)、有效载荷(Payload)和签名(Signature)。在传输的时候,会将JWT的3部分分别进行Base64编码后用.进行连接形成最终传输的字符串JWT一般是这样一个字符串,分为三个部分,以"."隔开:
xxxxx.yyyyy.zzzzz
JWTString=Base64(Header).Base64(Payload).HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
token认证的优势
相⽐于 Session 认证的⽅式来说,使⽤ jwt 进⾏⾝份认证主要有下⾯优势
1.⽆状态
2.有效避免了CSRF 攻击
3.适合移动端应⽤
4.单点登录友好
NETCore使用JWT
添加数据访问模拟api,ValuesController
其中api/value1是可以直接访问的,api/value2添加了权限校验特性标签 [Authorize]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Demo.Jwt.Controllers
[ApiController]
public class ValuesController : ControllerBase
[HttpGet]
[Route("api/value1")]
public ActionResult<IEnumerable<string>> Get()
return new string[] "value1", "value1" ;
[HttpGet]
[Route("api/value2")]
[Authorize]
public ActionResult<IEnumerable<string>> Get2()
return new string[] "value2", "value2" ;
添加模拟登陆,生成Token的api,AuthController
这里模拟一下登陆校验,只验证了用户密码不为空即通过校验,真实环境完善校验用户和密码的逻辑。
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 Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
namespace Demo.Jwt.Controllers
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
[AllowAnonymous]
[HttpGet]
public IActionResult Get(string userName, string pwd)
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(pwd))
var claims = new[]
new Claim(JwtRegisteredClaimNames.Nbf,$"new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()") ,
new Claim (JwtRegisteredClaimNames.Exp,$"new DateTimeOffset(DateTime.Now.AddMinutes(30)).ToUnixTimeSeconds()"),
new Claim(ClaimTypes.Name, userName)
;
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Const.SecurityKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: Const.Domain,
audience: Const.Domain,
claims: claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
return Ok(new
token = new JwtSecurityTokenHandler().WriteToken(token)
);
else
return BadRequest(new message = "username or password is incorrect." );
Startup添加JWT验证的相关配置
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Text;
namespace Demo.Jwt
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)
//添加jwt验证:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
options.TokenValidationParameters = new TokenValidationParameters
ValidateIssuer = true,//是否验证Issuer
ValidateAudience = true,//是否验证Audience
ValidateLifetime = true,//是否验证失效时间
ClockSkew = TimeSpan.FromSeconds(30),
ValidateIssuerSigningKey = true,//是否验证SecurityKey
ValidAudience = Const.Domain,//Audience
ValidIssuer = Const.Domain,//Issuer,这两项和前面签发jwt的设置一致
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Const.SecurityKey))//拿到SecurityKey
;
);
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
///添加jwt验证
app.UseAuthentication();
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
app.UseMvc(routes =>
routes.MapRoute(
name: "default",
template: "controller=Home/action=Index/id?");
);
namespace Demo.Jwt
public class Const
/// <summary>
/// 这里为了演示,写死一个密钥。实际生产环境可以从配置文件读取,这个是用网上工具随便生成的一个密钥
/// </summary>
public const string SecurityKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDI2a2EJ7m872v0afyoSDJT2o1+SitIeJSWtLJU8/Wz2m7gStexajkeD+Lka6DSTy8gt9UwfgVQo6uKjVLG5Ex7PiGOODVqAEghBuS7JzIYU5RvI543nNDAPfnJsas96mSA7L/mD7RTE2drj6hf3oZjJpMPZUQI/B1Qjb5H3K3PNwIDAQAB";
public const string Domain = "http://localhost:5000";
在实际开发中需要用下列手段来增加JWT的安全性:
1、因为JWT是在请求头中传递的,所以为了避免网络劫持,推荐使用HTTPS来传输,更加安全
2、JWT的哈希签名的密钥是存放在服务端的,所以只要服务器不被攻破,理论上JWT是安全的。因此要保证服务器的安全
3、JWT可以使用暴力穷举来破解,所以为了应对这种破解方式,可以定期更换服务端的哈希签名密钥(相当于盐值)。这样可以保证等破解结果出来了,你的密钥也已经换了
token 过期后,如何自动续期
1、方式1
将 token 过期时间设置为15分钟;
前端发起请求,后端验证 token 是否过期;如果过期,前端发起刷新token请求,后端为前端返回一个新的token;
前端用新的token发起请求,请求成功;
如果要实现每隔72小时,必须重新登录,后端需要记录每次用户的登录时间;用户每次请求时,检查用户最后一次登录日期,如超过72小时,则拒绝刷新token的请求,请求失败,跳转到登录页面。
另外后端还可以记录刷新token的次数,比如最多刷新50次,如果达到50次,则不再允许刷新,需要用户重新授权。
2、方式2
登录成功以后,后端返回 access_token 和 refresh_token,客户端缓存此两种token;
使用 access_token 请求接口资源,成功则调用成功;如果token超时,客户端携带 refresh_token 调用token刷新接口获取新的 access_token;
后端接受刷新token的请求后,检查 refresh_token 是否过期。如果过期,拒绝刷新,客户端收到该状态后,跳转到登录页;如果未过期,生成新的 access_token 返回给客户端。
客户端携带新的 access_token 重新调用上面的资源接口。
客户端退出登录或修改密码后,注销旧的token,使 access_token 和 refresh_token 失效,同时清空客户端的 access_token 和 refresh_toke。
后端实现token过期还可以利用Redis来存储token,设置redis的键值对的过期时间。如果发现redis中不存在token的记录,说明token已经过期了。
以上是关于如何使用JWT进行身份验证与授权的主要内容,如果未能解决你的问题,请参考以下文章
如何使用自定义策略模式实现 jwt 令牌基础身份验证以在 .net 核心中进行授权?
使用 JWT 进行护照身份验证:如何将护照的默认未经授权响应更改为我的自定义响应?
使用 Django GraphQL JWT 和 Graphene Relay 进行身份验证和授权