ASP.NET Core系列:JWT身份认证
1. JWT概述
JSON Web Token(JWT)是目前流行的跨域身份验证解决方案。
JWT的官网地址:https://jwt.io
JWT的实现方式是将用户信息存储在客户端,服务端不进行保存。每次请求都把令牌带上以校验用户登录状态,这样服务就变成无状态的,利于服务器集群扩展。
1.1 JWT令牌结构
在紧凑的形式中,JSON Web Tokens由dot(.)分隔的三个部分组成:
◊ Header 头
◊ Payload 载荷
◊ Signature 签名
因此,JWT通常如右所示:xxxxx.yyyyy.zzzzz
(1)Header
标头通常由两部分组成:令牌的类型,即JWT,以及正在使用的签名算法,如:HMAC SHA256或RSA。
{ "alg": "HS256", "typ": "JWT" }
然后,这个JSON被编码为Base64Url,形成JWT的第一部分。
(2)Payload
Payload用来存放需要传递的数据,包括用户信息及认证信息等。
JWT 规定了7个官方字段,供选用。
◊ iss (issuer):签发人
◊ exp (expiration time):过期时间
◊ sub (subject):主题
◊ aud (audience):受众
◊ nbf (Not Before):生效时间
◊ iat (Issued At):签发时间
◊ jti (JWT ID):编号
除了官方字段,可以在这个部分自定义其它字段。
然后,这个JSON被编码为Base64Url,形成JWT的第二部分。
注意:JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。
(3)Signature
Signature 部分是对前两部分的签名,防止数据篡改。这部分存在的意义就是为了解决前面部分的不安全性。
首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), SECREATE_KEY )
签名用于验证消息在此过程中未被更改,并且,在使用私钥签名的令牌的情况下,它还可以验证JWT的发件人是否是它所声称的人。
2. ASP.NET Core中集成JWT身份认证
新建ASP.NET Core API应用程序,添加安装包:
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer Install-Package System.IdentityModel.Tokens.Jwt
appsettings.json
{ "Jwt": { "Issuer": "Issuer", "Audience": "Audience", "SigningKey": "EF1DA5B7-C4FA-4240-B997-7D1701BF9BE2" } }
JwtConfig.cs
public class JwtConfig { public string Issuer { get; set; } public string Audience { get; set; } public string SigningKey { get; set; } }
Startup.cs
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { var jwtconfig = Configuration.GetSection("Jwt").Get<JwtConfig>(); // JWT身份认证 services.AddAuthentication(option => { option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(option => { option.TokenValidationParameters = new TokenValidationParameters { ValidIssuer = jwtconfig.Issuer, ValidAudience = jwtconfig.Audience, ValidateIssuer = true, ValidateLifetime = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtconfig.SigningKey)), // 缓冲过期时间,总的有效时间等于这个时间加上jwt的过期时间,如果不配置,默认是5分钟 ClockSkew = TimeSpan.FromSeconds(3) }; }); services.AddOptions().Configure<JwtConfig>(Configuration.GetSection("Jwt")); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { // JWT身份认证 app.UseAuthentication(); app.UseMvc(); } }
AuthController.cs
using System.Security.Claims; using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Options; [Route("api/[controller]")] [ApiController] public class AuthController : ControllerBase { private JwtConfig jwtconfig; public AuthController(IOptions<JwtConfig> option) { jwtconfig = option.Value; } [HttpGet] public ActionResult<string> Get() { var claim = new Claim[]{ new Claim("UserName", "lb") }; var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtconfig.SigningKey)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: jwtconfig.Issuer, audience: jwtconfig.Audience, claims: claim, notBefore: DateTime.Now, expires: DateTime.Now.AddSeconds(30), signingCredentials: creds); return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) }); } /// <summary> /// 在需要身份认证的方法添加[Authorize] /// </summary> [Authorize] [HttpGet("{id}")] public ActionResult<string> Get(int id) { return "value"; } }
在访问需要JWT身份认证的接口时,接口添加header参数Authorization,值为“Bearer”+空格+token。若不能通过JWT身份认证,则调用接口返回状态401未认证。