【问题标题】:Web API giving 401 on JWTWeb API 在 JWT 上给出 401
【发布时间】:2016-03-28 02:33:27
【问题描述】:

我有以下问题:

web api 使用 JWT 来授权人们。我一直在关注这个教程:here

令牌提供者工作正常,如邮递员图片所示:

但是当我尝试将邮递员中的令牌传递给以下控制器时:

    [Authorize]
    [Route("ChangePassword")]
    public async Task<IHttpActionResult> ChangePassword(ChangePasswordBindingModel model) {
        if (!ModelState.IsValid) {
            return BadRequest(ModelState);
        }

        IdentityResult result = await this.AppUserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword);

        if (!result.Succeeded)
            return GetErrorResult(result);

        return Ok();
    }

那么这将是结果:

我看不出应该是什么问题。我在启动文件中做的也是最后启动API。

public class Startup {

    public void Configuration(IAppBuilder app) {
        HttpConfiguration httpConfig = new HttpConfiguration();

        ConfigureOAuthTokenGeneration(app);
        ConfigureOAuthTokenConsumption(app);


        ConfigureWebApi(httpConfig);
        app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

        app.UseWebApi(httpConfig);
    }

    private void ConfigureWebApi(HttpConfiguration httpConfig) {
        httpConfig.MapHttpAttributeRoutes();
        var jsonFormatter = httpConfig.Formatters.OfType<JsonMediaTypeFormatter>().First();

        jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
    }

    private void ConfigureOAuthTokenGeneration(IAppBuilder app) {
        app.CreatePerOwinContext(ApplicationDbContext.Create);
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

        OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions() {

            //Set to false in production
            AllowInsecureHttp = true,
            TokenEndpointPath = new PathString("/oauth/token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
            Provider = new CustomOAuthProvider(),
            AccessTokenFormat = new CustomJwtFormat("http://localhost:44300")
        };

        app.UseOAuthAuthorizationServer(OAuthServerOptions);
    }

    private void ConfigureOAuthTokenConsumption(IAppBuilder app) {
        var issuer = "http://localhost:44300";
        var audienceId = "414e1927a3884f68abc79f7283837fd1";
        var audienceSecret = TextEncodings.Base64Url.Decode("qMCdFDQuF23RV1Y-1Gq9L3cF3VmuFwVbam4fMTdAfpo");

        // Api controllers with an [Authorize] attribute will be validated with JWT
        app.UseJwtBearerAuthentication(
            new JwtBearerAuthenticationOptions
            {
                AuthenticationMode = AuthenticationMode.Active,
                AllowedAudiences = new[] { audienceId },
                IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
                {
                    new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
                }
            });
    }
}

【问题讨论】:

  • 请修复您的链接和图片
  • 谢谢提醒

标签: c# asp.net asp.net-web-api jwt


【解决方案1】:

我想我已经解决了两个教程(thisthis)的问题。看看CustomJwtFormatProtect()方法

public string Protect(AuthenticationTicket data) {
   ...
   var issued = data.Properties.IssuedUtc;
   ...
   var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims,
       issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);
   ...
}

看起来JwtSecurityTokenlocal 中而不是在UTC 时区中期待notBefore 参数。如果作者在约旦(UTC+2),这意味着 notBefore 会比现在早 2 小时,但它仍然可以工作。但是,由于我在加利福尼亚 (UTC-8),notBefore 设置为 8 小时 稍后 并且令牌验证失败!解决办法是有

DateTimeOffset issued = data.Properties.IssuedUtc?.ToLocalTime();

采用新的 C# 6 格式,或

DateTimeOffset issued = data.Properties.IssuedUtc.Value.ToLocalTime()

使用经典语法。感谢@Treetopvt 推动我通过中间件进行调试。使用此更改标准 JwtSecurityTokenHandler 可以正常工作

【讨论】:

    【解决方案2】:

    我最近浏览了相同的教程并遇到了类似的问题。所有具有 [Authorize] 属性的端点都返回 401。我完全分解了 JwtBearerAuthentication 中间件,发现 JWTSecurityTokenHandler 确定受众是否有效时存在问题。

    对于初学者,正如大多数指南会告诉您的那样,请验证您的受众、颁发者和机密是否与您生成 JWT 令牌的位置和您配置 OAuthConsumption 的位置相同。我发现在 JWT 创建方面很容易混淆这些。如果它们都正确,请查看下面的代码。

    我最终创建了自己的 JWT 处理程序,它派生自 JwtSecurityTokenHandler。它主要只是调用基本方法,但它确实让您深入了解验证过程的工作原理。请注意 ValidateToken 中的代码更改。

       class CustomJWTTokenHandler : JwtSecurityTokenHandler
    {
        public CustomJWTTokenHandler():base()
        {
    
        }
        public override bool CanReadToken(string tokenString)
        {
           var rtn =  base.CanReadToken(tokenString);
            return rtn;
        }
        public override bool CanValidateToken
        {
            get
            {
                return base.CanValidateToken;
            }
        }
    
        protected override ClaimsIdentity CreateClaimsIdentity(JwtSecurityToken jwt, string issuer, TokenValidationParameters validationParameters)
        {
            return base.CreateClaimsIdentity(jwt, issuer, validationParameters);
        }
        public override ReadOnlyCollection<ClaimsIdentity> ValidateToken(SecurityToken token)
        {
            try
            {
                var rtn = base.ValidateToken(token);
                return rtn;
            }
            catch (Exception)
            {
    
                throw;
            }
        }
    
        public override ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
        {
            var jwt = this.ValidateSignature(securityToken, validationParameters);
            if (validationParameters.ValidateAudience)
            {
                if (validationParameters.AudienceValidator != null)
                {
                    if (!validationParameters.AudienceValidator(jwt.Audiences, jwt, validationParameters))
                    {
                        throw new SecurityTokenInvalidAudienceException(string.Format(CultureInfo.InvariantCulture, ErrorMessages.IDX10231, jwt.ToString()));
                    }
                }
                else
                {
                    base.ValidateAudience(validationParameters.ValidAudiences, jwt, validationParameters);
                }
            }
    
            string issuer = jwt.Issuer;
            if (validationParameters.ValidateIssuer)
            {
                if (validationParameters.IssuerValidator != null)
                {
                    issuer = validationParameters.IssuerValidator(issuer, jwt, validationParameters);
                }
                else
                {
                    issuer = ValidateIssuer(issuer, jwt, validationParameters);
                }
            }
    
            if (validationParameters.ValidateActor && !string.IsNullOrWhiteSpace(jwt.Actor))
            {
                SecurityToken actor = null;
                ValidateToken(jwt.Actor, validationParameters, out actor);
            }
    
            ClaimsIdentity identity = this.CreateClaimsIdentity(jwt, issuer, validationParameters);
            if (validationParameters.SaveSigninToken)
            {
                identity.BootstrapContext = new BootstrapContext(securityToken);
            }
    
            validatedToken = jwt;
            return new ClaimsPrincipal(identity);
        }
    
        protected override JwtSecurityToken ValidateSignature(string token, TokenValidationParameters validationParameters)
        {
            var rtn =  base.ValidateSignature(token, validationParameters);
            var issuer = rtn.Issuer;
    
            return rtn;
        }
        protected override void ValidateAudience(IEnumerable<string> audiences, SecurityToken securityToken, TokenValidationParameters validationParameters)
        {
            if (audiences !=null && audiences.Any())
            {
                var jwt = securityToken as JwtSecurityToken;
                if (!jwt.Audiences.Any())
                {
                    throw new Exception("token has no audiences defined");
                }
                var inBothList= audiences.Where(X => jwt.Audiences.Contains(X)).ToList();
                if (!inBothList.Any()){
                    throw new Exception("token not in audience list");
                }
    
            }
            //base.ValidateAudience(audiences, securityToken, validationParameters);
        }
    
    
        public override SecurityToken ReadToken(string tokenString)
        {
            var rtnToken =  base.ReadToken(tokenString);
            //var validations = this.ValidateToken(rtnToken);
            return rtnToken;
        }
    }
    

    当您设置 UseJwtBearerAuthentication 中间件时,此处理程序已连接:

                app.UseJwtBearerAuthentication(
                new JwtBearerAuthenticationOptions
                {
                    AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
                    AllowedAudiences = new List<string>() { JWTConfigs.audience },
                    IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
                    {
                        new CustomSymmetricKeyIssuerSecurityTokenProvider(JWTConfigs.issuer, key)
                    },
                    TokenHandler = new CustomJWTTokenHandler()
                }
            );
    

    希望这对您有用,或者至少指出您的令牌失败的原因。

    【讨论】:

    • 感谢@treetopvt 分享这个,调试401问题真的很有用,你还记得为什么你一直收到401吗?
    • 我不确定观众检查失败的原因,但是一旦我明确地将我的 validationParameters.ValidAudiences 和明确构造的 JWT 传递到基本方法中,它确实可以工作。我的猜测是一个参数或另一个在本机实现中未正确传递。
    • 我正在解决同样的问题;但是在我实现 CustomJWTTokenHandler 类之后,问题就消失了。不知道我应该为此高兴还是难过:)
    猜你喜欢
    • 1970-01-01
    • 2019-09-07
    • 1970-01-01
    • 2020-11-05
    • 2020-05-05
    • 1970-01-01
    • 2020-01-10
    • 2020-12-22
    • 2018-01-09
    相关资源
    最近更新 更多