将 JWT 身份验证实现从 .net core 2 转移到 asp.net web api 2

Posted

技术标签:

【中文标题】将 JWT 身份验证实现从 .net core 2 转移到 asp.net web api 2【英文标题】:transfer JWT Authentication implementation from .net core 2 to asp.net web api 2 【发布时间】:2018-06-23 06:48:11 【问题描述】:

我在 .net core 2 应用程序中实现了 JWT 身份验证,它工作正常。

我想在 asp.net web api 2 应用程序中使用此实现和结构,但出现错误

我的结构:

JwtTokenBuilder 类:

using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Linq;

namespace solution.Authentication

  public sealed class JwtTokenBuilder
  
    private SecurityKey securityKey = null;
    private string subject = "";
    private string issuer = "";
    private string audience = "";
    private Dictionary<string, string> claims = new Dictionary<string, string>();
    private DateTime expireTime = DateTime.UtcNow.AddMinutes(30);

    public JwtTokenBuilder AddSecurityKey(SecurityKey securityKey)
    
      this.securityKey = securityKey;
      return this;
    

    public JwtTokenBuilder AddSubject(string subject)
    
      this.subject = subject;
      return this;
    

    public JwtTokenBuilder AddIssuer(string issuer)
    
      this.issuer = issuer;
      return this;
    

    public JwtTokenBuilder AddAudience(string audience)
    
      this.audience = audience;
      return this;
    

    public JwtTokenBuilder AddClaim(string type, string value)
    
      this.claims.Add(type, value);
      return this;
    

    public JwtTokenBuilder AddClaims(Dictionary<string, string> claims)
    
      this.claims.Union(claims);
      return this;
    

    public JwtTokenBuilder AddExpiry(DateTime expireTime)
    
      this.expireTime = expireTime;
      return this;
    

    public JwtToken Build()
    
      EnsureArguments();

      var claims = new List<Claim>
            
              new Claim(JwtRegisteredClaimNames.Sub, this.subject),
              new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
            
      .Union(this.claims.Select(item => new Claim(item.Key, item.Value)));

      var token = new JwtSecurityToken(
                        issuer: this.issuer,
                        audience: this.audience,
                        claims: claims,
                        expires: this.expireTime,
                        signingCredentials: new SigningCredentials(
                                                  this.securityKey,
                                                  SecurityAlgorithms.HmacSha256));

      return new JwtToken(token);
    

    #region " private "

    private void EnsureArguments()
    
      if (this.securityKey == null)
        throw new ArgumentNullException("Security Key");

      if (string.IsNullOrEmpty(this.subject))
        throw new ArgumentNullException("Subject");

      if (string.IsNullOrEmpty(this.issuer))
        throw new ArgumentNullException("Issuer");

      if (string.IsNullOrEmpty(this.audience))
        throw new ArgumentNullException("Audience");
    

    #endregion
  

令牌对象:

using System;
using System.IdentityModel.Tokens.Jwt;

namespace solution.Authentication

  public sealed class JwtToken
  
    private JwtSecurityToken token;

    internal JwtToken(JwtSecurityToken token)
    
      this.token = token;
    

    public DateTime ValidTo => token.ValidTo;
    public string access_token => new JwtSecurityTokenHandler().WriteToken(this.token);
  

安全密钥类:

using Microsoft.IdentityModel.Tokens;
using System.Text;

namespace solution.Authentication

  public static class JwtSecurityKey
  
    public static SymmetricSecurityKey Create(string secret)
    
      return new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secret));
    
  

我的令牌控制器方法 generate 并返回 token

private JwtToken getToken(User user)

  DateTime startTime = DateTime.Now;
  DateTime expireTime = DateTime.Now.AddMinutes(60);

  var token = new JwtTokenBuilder()
                 .AddSecurityKey(JwtSecurityKey.Create("SecurityKey"))
                 .AddSubject("Subject")
                 .AddIssuer("Issuer")
                 .AddAudience("Audience")
                 .AddClaim("Username", user.UserName)
                 .AddExpiry(expireTime)
                 .Build();

  return token;

在 .net core 2 应用程序中,我使用 OWIN 启动类来验证我的 token 用于所有具有 Authorize 的控制器strong> 属性。

控制器示例:

namespace solution.Controllers

  public class ExampleController : ApiController
  
    [HttpPost]
    [Route("api/Example")]
    [Authorize(Policy = "Session")]
    public void Run()
    
       // do something;
    
    

用于验证 JWT 令牌的我的 owin 启动类:

using System;
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Threading.Tasks;

namespace solution

  public class Startup
  
    public void ConfigureServices(IServiceCollection services)
    
      services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
               .AddJwtBearer(options =>
               
                 options.TokenValidationParameters = new TokenValidationParameters
                 
                   ValidateIssuer = true,
                   ValidateAudience = true,
                   ValidateLifetime = true,
                   ValidateIssuerSigningKey = true,

                   ValidIssuer = "Issuer",
                   ValidAudience = "Audience",
                   IssuerSigningKey = JwtSecurityKey.Create("SecurityKey")



                 ;

                 options.Events = new JwtBearerEvents
                 
                   OnAuthenticationFailed = context =>
                   
                     return Task.CompletedTask;
                   ,
                   OnTokenValidated = context =>
                   
                     return Task.CompletedTask;
                   
                 ;
               );

      services.AddAuthorization(options =>
      
        options.AddPolicy("Session", policy => policy.RequireClaim("SessionId"));
      );

      services.AddSignalR();
      services.AddCors(options =>
      
        options.AddPolicy("CorsPolicy",
            builder => builder
                .AllowAnyOrigin()
                .AllowAnyMethod()
                .AllowAnyHeader()
                .AllowCredentials());
      );
      services.AddMvc();
    

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    
      loggerFactory.AddConsole();

      if (env.IsDevelopment())
      
        app.UseDeveloperExceptionPage();
      

      app.Use(async (context, next) =>
      
        await next();
        if (context.Response.StatusCode == 404 &&
            !Path.HasExtension(context.Request.Path.Value) &&
            !context.Request.Path.Value.StartsWith("/api/", StringComparison.OrdinalIgnoreCase))
        
          context.Request.Path = "/index.html";
          await next();
        
      );

      app.UseDeveloperExceptionPage();
      app.UseAuthentication();
      app.UseMvcWithDefaultRoute();
      app.UseDefaultFiles();
      app.UseStaticFiles();
      app.UseCors(policyName: "CorsPolicy");
      app.UseSignalR(routes =>
      
      );
    
  

我想在 asp.net web api 中使用这种结构,只改变 owin 类,可以吗? 请帮助我进行任何更改

【问题讨论】:

【参考方案1】:

将我的实现从 .net core 2 转移到 asp.net web api 2

的结构更改

我使用 System.IdentityModel.Tokens.Jwt 命名空间来生成和验证 JWT 令牌。

.net core 2 与 System.IdentityModel.Tokens.Jwt version="5.1.4" 兼容,但 asp.net web api 2 与 System.IdentityModel.Tokens.Jwt version="4.0.2" 兼容

包版本中的相同更改对代码进行了更改,由于更改包版本,我使用System.IdentityModel.Tokens命名空间而不是Microsoft.IdentityModel.Tokens的部分代码。

代码更改:

JwtTokenBuilder 类:

在这个类中更改SigningCredentials参数设置

  var token = new JwtSecurityToken(
                    issuer: this.issuer,
                    audience: this.audience,
                    claims: claims,
                    expires: this.expireTime,
                    signingCredentials: new System.IdentityModel.Tokens.SigningCredentials(
                                              this.securityKey,
                                              Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256Signature
                                            , Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256Signature));

安全密钥类:

更改安全密钥生成方式

using System.IdentityModel.Tokens;
using System.Text;

namespace solution.Authentication

  public static class JwtSecurityKey
  
    public static SymmetricSecurityKey Create(string secret)
    
      return new InMemorySymmetricSecurityKey(Encoding.UTF8.GetBytes(secret));
    
  

控制器属性:

namespace solution.Controllers

  public class ExampleController : ApiController
  
    [HttpPost]
    [Route("api/Example")]
    [System.Web.Http.Authorize]
    public void Run()
    
       // do something;
    
    

我的主要更改是在 Startup OWIN 类中并将 Microsoft.Owin.Security.Jwt 包版本从“3.1.0”更改为“3.0.0”以验证传入请求的 JWT 令牌。

实现:

using Microsoft.Owin;
using Owin;
using System.Web.Http;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Jwt;

[assembly: OwinStartup(typeof(solution.Startup))]

namespace solution

  public class Startup
  
    public void Configuration(IAppBuilder app)
    
      app.MapSignalR();
      HttpConfiguration config = new HttpConfiguration();
      config.MapHttpAttributeRoutes();
      ConfigureOAuth(app);
      app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
      app.UseWebApi(config);
    
    public void ConfigureOAuth(IAppBuilder app)
    
      var issuer = "issuer";
      var audience = "audience";
      var secret = JwtSecurityKey.Create("SecurityKey").GetSymmetricKey();

      // Api controllers with an [Authorize] attribute will be validated with JWT
      var option =
          new JwtBearerAuthenticationOptions
          
            AuthenticationMode = AuthenticationMode.Active,
            AllowedAudiences = new[]  audience ,
            IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
              
                        new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
              
          ;
      app.UseJwtBearerAuthentication(
            option
        );
    
  

【讨论】:

您如何获得IssuerSecurityTokenProviders?我只有IssuerSecurityKeyProviders

以上是关于将 JWT 身份验证实现从 .net core 2 转移到 asp.net web api 2的主要内容,如果未能解决你的问题,请参考以下文章

使用 Net Core 2 进行 JWT 远程身份验证时出错

JWT 身份验证 ASP.NET Core MVC 应用程序

如何从.Net Core API 中的身份验证 JWT 令牌中获取身份用户?

.net core 2.2 应用程序的 JWT 身份验证不使用身份

使用 JWT 令牌的 ASP.NET Core 网站到 WebApi 身份验证

NET Core 3.1 MVC 授权/身份验证,带有在单独的 Net Core 3.1 Web Api 中从外部获取的令牌 (JWT)