将 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的主要内容,如果未能解决你的问题,请参考以下文章