最近,团队的小伙伴们在作项目时,须要用到JWT认证。遂根据本身的经验,整理成了这篇文章,用来帮助理清JWT认证的原理和代码编写操做。web
JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于做为JSON对象在各方之间安全地传输信息。该信息能够被验证和信任,由于它是数字签名的。json
JWT是什么,看上面这段网上抄来的话。c#
关于JWT以及优缺点,网上有不少详细的说法,我这儿就不重复了。api
咱们只须要知道如下的事实:安全
在通常的系统中,咱们有时候会作个用户登陆。用户登陆完成进到系统后,须要根据用户的权限,来控制一些功能可用,而另外一些功能不可用。bash
在SOA/AOP架构中,作为最重要的API端,其实也须要有相似登陆或认证的内容,用来区分哪些用户可使用某个API,哪些用户不行。微信
同时,咱们但愿这个登陆或相似登陆的过程,只发生在一个固定位置。这样,在咱们写代码时,创建好这样一个过程后,在咱们后边写代码时,简单引用便可,而不须要每一个API程序都开发一次认证。这个需求,其实就是OAuth的由来。架构
最重要的是,这样的代码写出来,显得高大上。app
下面进入正题。post
认证这个操做,就像咱们最近的日子。
首先,咱们要有一个出入证,或者绿码。这个证,咱们称做令牌(Token)。咱们去领这个证,这个操做称为发行(Issue)。
咱们拿着这个证,去到一个地方。有专人会检查这个证,这称为用户身份验证(Authentication)。验证经过放行,称为受权(Authorization),验证不经过,叫做未受权错误(Unauthorized)。
若是这个证过时了,你就须要去从新办一个证。这个过程叫作刷新(RefreshToken)。
简言之,这就是认证的所有流程。
下面,我用一个Demo项目,来逐步完成这个过程。
这个Demo的开发环境是:Mac + VS Code + Dotnet Core 3.1.2。
$ dotnet --info
.NET Core SDK (reflecting any global.json):
Version: 3.1.201
Commit: b1768b4ae7
Runtime Environment:
OS Name: Mac OS X
OS Version: 10.15
OS Platform: Darwin
RID: osx.10.15-x64
Base Path: /usr/local/share/dotnet/sdk/3.1.201/
Host (useful for support):
Version: 3.1.3
Commit: 4a9f85e9f8
.NET Core SDKs installed:
3.1.201 [/usr/local/share/dotnet/sdk]
.NET Core runtimes installed:
Microsoft.AspNetCore.App 3.1.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 3.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]
首先,在这个环境下创建工程:
% dotnet new sln -o demo
The template "Solution File" was created successfully.
% cd demo
% dotnet new webapi -o demo
The template "ASP.NET Core Web API" was created successfully.
Processing post-creation actions...
Running 'dotnet restore' on demo/demo.csproj...
Restore completed in 179.13 ms for demo/demo.csproj.
Restore succeeded.
% dotnet sln add demo/demo.csproj
Project `demo/demo.csproj` added to the solution.
% dotnet add package Swashbuckle.AspNetCore
log : Restore completed in 2.75 sec for demo/demo.csproj.
% dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
log : Restore completed in 3.09 sec for demo/demo.csproj.
五步作完,基础项目就建完了。
看一下整个的目录结构:
% tree .
.
├── demo
│ ├── Controllers
│ │ └── WeatherForecastController.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── Startup.cs
│ ├── WeatherForecast.cs
│ ├── appsettings.Development.json
│ ├── appsettings.json
│ ├── demo.csproj
│ └── obj
│ ├── demo.csproj.nuget.dgspec.json
│ ├── demo.csproj.nuget.g.props
│ ├── demo.csproj.nuget.g.targets
│ ├── project.assets.json
│ └── project.nuget.cache
└── demo.sln
在ConfigureServices方法中加入如下代码:
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Demo", Version = "V1" });
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Description = "",
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] {}
}
});
});
在Configure方法中加入如下代码
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Demo V1");
});
关于Swagger的详细配置,这里不作说明,留着之后写。
签发Token是认证的第一步。
用户进到系统,在验证用户账号密码后,须要根据用户的数据,把Token返回给用户。
这个过程其实跟认证没什么关系,只是一个普通的API功能。
using System;
namespace demo.DTOModels
{
public class LoginRequestDTO
{
public string username { get; set; }
public string password { get; set; }
}
}
using Microsoft.AspNetCore.Mvc;
using demo.DTOModels;
namespace demo.Controllers
{
public class AuthenticationController : ControllerBase
{
[HttpPost, Route("requesttoken")]
public ActionResult RequestToken([FromBody] LoginRequestDTO request)
{
//这儿待完善
return Ok();
}
}
}
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"tokenParameter": {
"secret": "123456123456123456",
"issuer": "WangPlus",
"accessExpiration": 120,
"refreshExpiration": 1440
}
}
这里,tokenParameter节是咱们设置的参数。通常来讲,是这几个:
secret: JWT加密的密钥。如今主流用SHA256加密,须要256位以上的密钥,unicode是16个字符以上,尽可能复杂一些。密钥泄露,Token就会被破解,因此,你懂的。
issuer: 签发人的名称,若是没人注意,你能够把大名写在上面。
accessExpiration: Token的有效分钟数。过了这个时间,这个Token会过时。
refreshExpiration: refreshToken的有效分钟数。过了这个时间,用户须要从新登陆。
Token过时后,可让用户从新登陆认证拿Token。但这个方式会比较Low。高大上的方式是签发Token的时候,同时也签发一个refreshToken给用户。用户Token过时后,能够拿refreshToken去申请新的Token,同时刷新refreshToken。若是用户长时间未使用系统,refreshToken也过时了,才让用户从新登陆认证。
refreshToken能够用JWT生成,也能够本身生成,不影响认证。
using System;
namespace demo.Models
{
public class tokenParameter
{
public string Secret { get; set; }
public string Issuer { get; set; }
public int AccessExpiration { get; set; }
public int RefreshExpiration { get; set; }
}
}
using Microsoft.AspNetCore.Mvc;
using demo.DTOModels;
using Microsoft.Extensions.Configuration;
using System;
using System.Text;
using demo.Models;
using Microsoft.IdentityModel.Tokens;
using System.Security.Claims;
using System.IdentityModel.Tokens.Jwt;
namespace demo.Controllers
{
public class AuthenticationController : ControllerBase
{
private tokenParameter _tokenParameter = new tokenParameter();
public AuthenticationController()
{
var config = new ConfigurationBuilder()
.SetBasePath(AppContext.BaseDirectory)
.AddJsonFile("appsettings.json")
.Build();
_tokenParameter = config.GetSection("tokenParameter").Get<tokenParameter>();
}
[HttpPost, Route("requestToken")]
public ActionResult RequestToken([FromBody] LoginRequestDTO request)
{
//这儿在作用户的账号密码校验。我这儿略过了。
if (request.username == null && request.password == null)
return BadRequest("Invalid Request");
//生成Token和RefreshToken
var token = GenUserToken(request.username, "testUser");
var refreshToken = "123456";
return Ok(new[] { token, refreshToken });
}
//这儿是真正的生成Token代码
private string GenUserToken(string username, string role)
{
var claims = new[]
{
new Claim(ClaimTypes.Name, username),
new Claim(ClaimTypes.Role, role),
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_tokenParameter.Secret));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var jwtToken = new JwtSecurityToken(_tokenParameter.Issuer, null, claims, expires: DateTime.UtcNow.AddMinutes(_tokenParameter.AccessExpiration), signingCredentials: credentials);
var token = new JwtSecurityTokenHandler().WriteToken(jwtToken);
return token;
}
}
}
这个类里,验证账号密码的代码我略过了。还有,refreshToken给了一个固定串。真实项目这儿就按须要作就好。
(未完待续)
![]() |
微信公众号:老王Plus 扫描二维码,关注我的公众号,能够第一时间获得最新的我的文章和内容推送 本文版权归做者全部,转载请保留此声明和原文连接 |