JWT+ASP.NET Core集成方案

Posted 、天上月

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JWT+ASP.NET Core集成方案相关的知识,希望对你有一定的参考价值。

json web token经过数字签名后,无法伪造,一个能够在各方之间安全的传输JSON对象的开放标准(RFC 7519)

JWT

JSON Web Token 经过数字签名后,无法伪造,一个能够在各方之间安全的传输JSON对象的开放标准(RFC 7519

创建项目和解决方案

dotnet new webapi -n SampleApi
cd SampleApi
dotnet new sln -n SampleApp
dotnet sln  add .\\SampleApi.csproj

引用包

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

该包已经依赖Microsoft.IdentityModel.TokensSystem.IdentityModel.Tokens.Jwt,该包由Azure AD 团队提供,所以不在aspnetcore6 运行时中。

  • 或直接修改jwtaspnetcore.csproj,引用包
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.1" />
  • appsettings.json

  "Authentication": 
    "JwtBearer": 
      "Issuer": "http://api.sampleapi.com",
      "Audience": "SampleApi",
      "SecurityKey": "SecurityKey23456"
    
  
  • Issuer:令牌的颁发者。一般就写成域名,实际可任意
  • Audience 颁发给谁。一般写成项目名,实际可任意
  • SecurityKey:签名验证的KEY;至少 128bit ,即16个英文字符以上,实际可任意英文字符

定义一个JwtSettings


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 
          
        );
    


AuthController登录GenerateToken

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();


AuthController调用服务

    /// <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)

.NET +JWT

JSON Web Token Libraries - jwt.io 可以看到,.NET有6个类库实现了JWT。

有二个常用的。

  1. 微软 Azure团队的实现:AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet: IdentityModel extensions for .Net (github.com)
  2. jwt-dotnet/jwt: Jwt.Net, a JWT (JSON Web Token) implementation for .NET (github.com)

作者: 、天上月OvO

出处:https://cnblogs.com/igeekfan

联系:luoyunchong@foxmail.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如有问题或建议,请多多赐教,非常感谢。

以上是关于JWT+ASP.NET Core集成方案的主要内容,如果未能解决你的问题,请参考以下文章

如何将 JWT 用于 asp.net Core

从 ASP.NET Core 中的 API 读取 JWT 令牌

ASP.Net Core 2.1 API JWT 无 cookie 会话?

无法在 ASP.NET Core 和 React 中创建包含 jwt 的 httponly cookie

ASP.NET Core JWT 和声明

asp.net core + angular2 JWT 承载