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未认证。

posted @ 2019-08-20 17:10 libingql 阅读(...) 评论(...) 编辑 收藏