使用 JWT 库
JWT,a JWT(JSON Web Token) implementation for .NET
该库支持生成和解析JSON Web Token
你可以直接通过Nuget获取,也可以自己下载和编译源码.
// 不要忘了 using
using JWT;
using JWT.Algorithms;
using JWT.Builder;
// 自定义秘钥
// jwt 的生成和解析都需要使用
const string secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";
创建 JWT
// 使用 JwtBuilder 来生成 token
string token = new JwtBuilder()
.WithAlgorithm(new HMACSHA256Algorithm()) // 使用算法
.WithSecret(secret) // 使用秘钥
.AddClaim("exp", DateTimeOffset.UtcNow.AddHours(1).ToUnixTimeSeconds())
.AddClaim("claim2", "claim2-value")
.Build();
Console.WriteLine(token);
生成的 token 如下:
// 注意:是通过.符号分隔成3段,分别对应的是header.payload.signature
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1Mjg3MjA2NTEsImNsYWltMiI6ImNsYWltMi12YWx1ZSJ9.56xcZALlJuwROe3qssCbe_DDjpQShk-Ik7kWAzONWFU
生成后可以分发出去, 别人拿着 token 来请求接口的时候, 我们需要解析验证
// 使用 JwtBuilder 来解析 token
try
{
string json = new JwtBuilder()
.WithSecret(secret)
.MustVerifySignature()
.Decode(token);
Console.WriteLine(json);
}
catch (TokenExpiredException)
{
Console.WriteLine("token 已过期");
}
catch (SignatureVerificationException)
{
Console.WriteLine("token 签名无效");
}
解析后得到的 json 字符串如下:
{
"exp": 1528721303,
"claim2": "claim2-value"
}
使用 Microsoft.IdentityModel.Tokens 库
- 新建一个 ASP.NET Core API 项目
- 新增一个控制器
AuthController
生成 JWT
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace WebApplication1.Controllers
{
[ApiController]
[Route("[controller]/[action]")]
public class AuthController : Controller
{
private readonly IConfiguration _configuration;
public AuthController(IConfiguration configuration)
{
_configuration = configuration;
}
[HttpGet]
public IActionResult Token()
{
var claims = new[]
{
new Claim(type: ClaimTypes.Name, value: "username")
};
var issuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["IssuerSigningKey"]));
var creds = new SigningCredentials(issuerSigningKey, SecurityAlgorithms.HmacSha256);
//.NET Core’s JwtSecurityToken class takes on the heavy lifting and actually creates the token.
/**
* Claims (Payload)
Claims 部分包含了一些跟这个 token 有关的重要信息。 JWT 标准规定了一些字段,下面节选一些字段:
iss: The issuer of the token,token 是给谁的
sub: The subject of the token,token 主题
exp: Expiration Time。 token 过期时间,Unix 时间戳格式
iat: Issued At。 token 创建时间, Unix 时间戳格式
jti: JWT ID。针对当前 token 的唯一标识
除了规定的字段外,可以包含其他任何 JSON 兼容的字段。
* */
var token = new JwtSecurityToken(
issuer: "test.com", //
audience: "test.com",
claims: claims,
expires: DateTime.Now.AddMinutes(1),
signingCredentials: creds);
return Ok(new
{
access_token = new JwtSecurityTokenHandler().WriteToken(token: token)
});
}
}
}
生成的 JWT 可以放到 jwt.io 里验证一下.
使用 IdentityServer4 库
IdentityServerTools
是 IdentityServer4 中的一个工具类, 封装了 JWT 生成方法, 以便使用.
按照套路, 注入 IdentityServer4 服务, 这里我们仅使用工具类来生成 JWT, 所以不需要配置其他东西.
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityServer()
.AddDeveloperSigningCredential();
// 省略其他
}
在控制器中构造函数注入使用
using IdentityServer4;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Security.Claims;
using System.Threading.Tasks;
// For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
namespace WebApplication1.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
private readonly IdentityServerTools _identityServerTools;
public AuthController(IdentityServerTools identityServerTools)
{
_identityServerTools = identityServerTools;
}
[HttpGet]
public async Task<IActionResult> TokenAsync()
{
// 完整的场景, 应该验证用户密码
Claim[] claims = new Claim[]
{
new Claim(type: ClaimTypes.NameIdentifier, value: Guid.NewGuid().ToString("N")),
new Claim(type: ClaimTypes.Name, value: "admin"),
new Claim(type: ClaimTypes.Gender, value: "man"),
new Claim(type: ClaimTypes.Email, value: "xxx@xxx.com"),
new Claim(type: "custom", value: "value"),
};
// 可以按自己需求, 返回指定结构的数据
return Ok(value: await _identityServerTools.IssueJwtAsync(lifetime: 3600, claims: claims));
}
}
}
这个时候访问 /api/auth
应该就能看到一段老长老长的 JWT 了, 可以扔到 jwt.io 里验证下.
身份认证
上面介绍了几种不同库生成 JWT 的方法, 当别人拿着我们分发出去的 JWT 来访问我们的接口时, 需要对其进行身份认证
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
namespace WebApplication1
{
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)
{
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false, // 是否验证 Issuer
ValidIssuer = "test.com",
ValidateAudience = false, // 是否验证 Audience
ValidAudience = "",
ValidateIssuerSigningKey = true, // 是否验证签名秘钥
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["IssuerSigningKey"])),
ValidateLifetime = true, // 是否验证失效时间
};
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAuthentication(); // 使用身份验证
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
}
}
}