json web token经过数字签名后,无法伪造,一个能够在各方之间安全的传输JSON对象的开放标准(RFC 7519) JSON Web Token 经过数字签名后,无法伪造,一个能够在各方之间安全的传输JSON对象的开放标准(RFC 7519)
dotnet new webapi -n SampleApicd SampleApidotnet new sln -n SampleAppdotnet sln add .\SampleApi.csprojdotnet add package Microsoft.AspNetCore.Authentication.JwtBearer该包已经依赖Microsoft.IdentityModel.Tokens、System.IdentityModel.Tokens.Jwt,该包由Azure AD 团队提供,所以不在aspnetcore6 运行时中。
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.1" /> "Authentication": { "JwtBearer": { "Issuer": "http://api.sampleapi.com", "Audience": "SampleApi", "SecurityKey": "SecurityKey23456" } }public class JwtSettings{ public JwtSettings(byte[] key, string issuer, string audience) { Key = key; Issuer = issuer; Audience = audience; } /// <summary> ///令牌的颁发者 /// </summary> public string Issuer { get; } /// <summary> /// 颁发给谁 /// </summary> public string Audience { get; } public byte[] Key { get; } public TokenValidationParameters TokenValidationParameters => new TokenValidationParameters { //验证Issuer和Audience ValidateIssuer = true, ValidateAudience = true, ValidateIssuerSigningKey = true, //是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比 ValidateLifetime = true, ValidIssuer = Issuer, ValidAudience = Audience, IssuerSigningKey = new SymmetricSecurityKey(Key) }; public static JwtSettings FromConfiguration(IConfiguration configuration) { var issuser = configuration["Authentication:JwtBearer:Issuer"] ?? "default_issuer"; var auidence = configuration["Authentication:JwtBearer:Audience"] ?? "default_auidence"; var securityKey = configuration["Authentication:JwtBearer:SecurityKey"] ?? "default_securitykey"; byte[] key = Encoding.ASCII.GetBytes(securityKey); return new JwtSettings(key, issuser, auidence); }}中间件Middleware引用
app.UseAuthentication();//认证 app.UseAuthorization();//授权定义JWT扩展方法服务注入
public static IServiceCollection AddJwt(this IServiceCollection services, IConfiguration configuration) { services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); services.AddScoped<IStorageUserService, StorageUserService>(); var jwtSettings = JwtSettings.FromConfiguration(configuration); services.AddSingleton(jwtSettings); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => options.TokenValidationParameters = jwtSettings.TokenValidationParameters); return services; }services.AddJwt(Configuration);定义一个数据库的实体类,数据库访问 为模拟数据
public class SysUser{ public int Id { get; set; } public string UserName { get; set; }}public interface IStorageUserService{ /// <summary> /// 根据登录验证用户 /// </summary> /// <param name="loginInfo"></param> /// <returns></returns> Task<SysUser> CheckPasswordAsync(LoginInfo loginInfo);}public class StorageUserService : IStorageUserService{ public async Task<SysUser> CheckPasswordAsync(LoginInfo loginInfo) { return await Task.FromResult( new SysUser { Id = new Random().Next(10000), UserName = loginInfo.UserName } ); }}using Microsoft.AspNetCore.Authorization;using Microsoft.AspNetCore.Mvc;using Microsoft.IdentityModel.Tokens;using SampleApi.Models;using System.IdentityModel.Tokens.Jwt;using System.Security.Claims;namespace SampleApi.Auth;/// <summary>/// 登录认证个人信息/// </summary>[ApiController][Route("/api/[controller]/[action]")][AllowAnonymous]public class AuthController : ControllerBase{ private readonly IStorageUserService _userService; private readonly JwtSettings _jwtSettings; public AuthController(JwtSettings jwtSettings, IStorageUserService userService) { _jwtSettings = jwtSettings; _userService = userService; } /// <summary> /// 登录,生成访问Toekn /// </summary> /// <param name="loginInfo"></param> /// <returns></returns> [HttpPost] public async Task<IActionResult> GenerateToken(LoginInfo loginInfo) { SysUser user = await _userService.CheckPasswordAsync(loginInfo); if (user == null) { return Ok(new { Status = false, Message = "账号或密码错误" }); } var claims = new List<Claim>(); claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString())); claims.Add(new Claim(ClaimTypes.Name, user.UserName)); var key = new SymmetricSecurityKey(_jwtSettings.Key); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: _jwtSettings.Issuer, audience: _jwtSettings.Audience, claims: claims, expires: DateTime.Now.AddMinutes(120), signingCredentials: creds ); return Ok(new { Status = true, Token = new JwtSecurityTokenHandler().WriteToken(token) }); }}aspnetcore6默认集成了swagger,直接运行项目,实际上为模拟数据库请求,所以点击登录接口即可。
{ "status": true,"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6Ijc4NjciLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoic3RyaW5nIiwiZXhwIjoxNjQzMDMyNzA1LCJpc3MiOiJodHRwOi8vYXBpLnNhbXBsZWFwaS5jb20iLCJhdWQiOiJTYW1wbGVBcGkifQ.Rl8XAt2u0aZRxEJw2mVUnV6S9WzQ65qUYjqXDTneCxE"}当使用Swagger测试时,增加,可配置全局请求头。增加一个扩展方法。
services.AddSwagger(Configuration); public static IServiceCollection AddSwagger(this IServiceCollection services, IConfiguration configuration) { services.AddSwaggerGen(options => { try { options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"{typeof(Startup).Assembly.GetName().Name}.xml"), true); } catch (Exception ex) { Log.Warning(ex.Message); } options.SwaggerDoc("v1", new OpenApiInfo { Title = "SampleApp - HTTP API", Version = "v1", Description = "The SampleApp Microservice HTTP API. This is a Data-Driven/CRUD microservice sample" }); options.AddSecurityRequirement(new OpenApiSecurityRequirement() { { new OpenApiSecurityScheme { Reference = new OpenApiReference() { Id = "Bearer", Type = ReferenceType.SecurityScheme } }, Array.Empty<string>() } }); options.AddSecurityDefinition(JwtBearerDefaults.AuthenticationScheme, new OpenApiSecurityScheme { Description = "JWT授权(数据将在请求头中进行传输) 参数结构: \"Authorization: Bearer {token}\"", Name = "Authorization", //jwt默认的参数名称 In = ParameterLocation.Header, //jwt默认存放Authorization信息的位置(请求头中) Type = SecuritySchemeType.ApiKey }); }); services.AddEndpointsApiExplorer(); return services; } /// <summary> /// 编码Token /// </summary> /// <param name="token"></param> /// <returns></returns> [HttpGet] [AllowAnonymous] public CurrentUser DecodeToken(string token) { var jwtTokenHandler = new JwtSecurityTokenHandler(); if (jwtTokenHandler.CanReadToken(token)) { JwtPayload jwtPayload = new JwtSecurityTokenHandler().ReadJwtToken(token).Payload; string? userIdOrNull = jwtPayload.Claims.FirstOrDefault(r => r.Type == ClaimTypes.NameIdentifier)?.Value; string? UserName = jwtPayload.Claims.FirstOrDefault(r => r.Type == ClaimTypes.Name)?.Value; CurrentUser currentUser = new CurrentUser { UserId = userIdOrNull == null ? null : Convert.ToInt32(userIdOrNull), UserName = UserName }; return currentUser; } return null; }IStorageUserService增加接口,StorageUserService的实现,创建一个CurrentUser类
public class StorageUserService : IStorageUserService{ private readonly IHttpContextAccessor _contextAccessor; public StorageUserService(IHttpContextAccessor contextAccessor) { _contextAccessor = contextAccessor; } public async Task<CurrentUser> GetUserByRequestContext() { var user = _contextAccessor.HttpContext.User; string? userIdOrNull = user.Claims?.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value; string? UserName = user.Claims?.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value; CurrentUser currentUser = new CurrentUser { IsAuthenticated = user.Identity.IsAuthenticated, UserId = userIdOrNull == null ? null : Convert.ToInt32(userIdOrNull), UserName = UserName }; return await Task.FromResult(currentUser); }}public class CurrentUser{ /// <summary> /// 是否登录 /// </summary> public bool IsAuthenticated { get; set; } /// <summary> /// 用户Id /// </summary> public int? UserId { get; set; } /// <summary> /// 用户名 /// </summary> public string? UserName { get; set; }}public interface IStorageUserService{ /// <summary> /// 根据Request Header携带Authorization:Bearer+空格+AccessToken获取当前登录人信息 /// </summary> /// <returns></returns> Task<CurrentUser> GetUserByRequestContext();} /// <summary> /// 根据Request Header携带Authorization:Bearer+空格+AccessToken获取当前登录人信息 /// </summary> /// <returns></returns> [HttpGet] [Authorize] public async Task<CurrentUser> GetUserByRequestContext() { return await _userService.GetUserByRequestContext(); }在swagger右上角,点击Authorize,header的参数结构: "Authorization: Bearer+空格+ {token}"
SampleApp/SampleApi at master · luoyunchong/SampleApp (github.com)
JSON Web Token Libraries - jwt.io 可以看到,.NET有6个类库实现了JWT。
有二个常用的。
作者:、天上月OvO
出处:https://cnblogs.com/igeekfan
联系:luoyunchong@foxmail.com