【问题标题】:Web Api with Owin with JWT always fails to authorize request带有 JWT 的 Owin 的 Web Api 总是无法授权请求
【发布时间】:2015-04-07 14:11:32
【问题描述】:

我一直按照教程直到系列中的this point。我在解决方案中使用了一个项目,它既充当令牌颁发机构又充当资源服务器。

JWT 是使用启动类中提到的端点生成的,我也在jwt.io 上对其进行了验证。但是,当我在 Chrome 上使用 Postman 将此 JWT 传递到使用 Authorize 属性保护的资源 API 端点时,我总是发现它返回

{ "message": "此请求的授权已被拒绝。" }

下面的 api 控制器类中的另一个 api 方法在通过 Chrome 上的 Postman 调用时有效。

我已使用 nuget 控制台所需的所有 dll 的最新版本

启动类中的代码

  public void Configuration(IAppBuilder app)
    {
        HttpConfiguration config = new HttpConfiguration();
        ConfigureOAuthTokenGeneration(app);
        ConfigureOAuthTokenConsumption(app);
        WebApiConfig.Register(config);
        app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
        app.UseWebApi(config);
    }


    private void ConfigureOAuthTokenGeneration(IAppBuilder app)
    {
        OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
        {
            //For Dev enviroment only (on production should be AllowInsecureHttp = false)
            AllowInsecureHttp = true,
            TokenEndpointPath = new PathString("/oauth/token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
            Provider = new CustomOAuthProvider(),
            AccessTokenFormat = new CustomJwtFormat(ConfigurationManager.AppSettings["Issuer"]),
        };

        // OAuth 2.0 Bearer Access Token Generation
        app.UseOAuthAuthorizationServer(OAuthServerOptions);
    }

    private void ConfigureOAuthTokenConsumption(IAppBuilder app)
    {
        string issuer = ConfigurationManager.AppSettings["Issuer"]; 
        string audienceId = ConfigurationManager.AppSettings["AudienceId"];
        byte[] audienceSecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["AudienceSecret"]);

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

自定义 OAuthProvider 中的代码

  public class CustomOAuthProvider : OAuthAuthorizationServerProvider
    {
        public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            context.Validated();
            return Task.FromResult<object>(null);
        }

        public override Task MatchEndpoint(OAuthMatchEndpointContext context)
        {
            //avoid pre-flight calls
            if (context.OwinContext.Request.Method == "OPTIONS" && context.IsTokenEndpoint)
            {
                context.OwinContext.Response.Headers.Add("Access-Control-Allow-Methods", new[] { "POST" });
                context.OwinContext.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "accept", "authorization", "content-type" });
                context.OwinContext.Response.StatusCode = 200;
                context.RequestCompleted();

                return Task.FromResult<object>(null);
            }

            return base.MatchEndpoint(context);       
        }

        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });

            //setting up claims in the constructor of class UserDetails 
            UserDetails user = new UserDetails();
            user.UserName = context.UserName;
            user.FirstName = "Dummy First";
            user.LastName = "Dummy Last";

            ClaimsIdentity identity = new ClaimsIdentity("JWT-BearerAuth-Test");
            identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
            foreach (string claim in user.Claims)
            {
                identity.AddClaim(new Claim(ClaimTypes.Role, claim));    
            }
            var ticket = new AuthenticationTicket(identity, null);

            context.Validated(ticket);

        }
    }

自定义 JWT 类

   public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
    {
        private readonly string _issuer = string.Empty;

        public CustomJwtFormat(string issuer)
        {
            _issuer = issuer;
        }

        public string Protect(AuthenticationTicket data)
        {
            if (data == null)
            {
                throw new ArgumentNullException("data");
            }

            string audienceId = ConfigurationManager.AppSettings["AudienceId"];

            string symmetricKeyAsBase64 = ConfigurationManager.AppSettings["AudienceSecret"];

            var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);

            var signingKey = new HmacSigningCredentials(keyByteArray);
            var issued = data.Properties.IssuedUtc;
            var expires = data.Properties.ExpiresUtc;
            var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);
            var handler = new JwtSecurityTokenHandler();
            var jwt = handler.WriteToken(token);
            return jwt;
        }

    }

资源服务器的 Api 控制器

   public class AdminController : ApiController
    {
        //This call works
        public IHttpActionResult ReadData(string id)
        {
            return Ok("ID sent in:" + id);
        }

        //[Authorize(Roles="EditRecord")]  //doesnt work
        [Authorize] //doesnt work either
        public IHttpActionResult EditData(string id)
        {
            return Ok("Edited ID:" + id);
        }
    }

我的环境是 VS2013 和 Framework 4.5,使用 OAuth2 和 Web Api 2。请原谅长篇文章。

【问题讨论】:

  • 嗨,你能做到这一点吗?我面临同样的问题。搜索但找不到任何解决方案

标签: oauth-2.0 asp.net-web-api2 owin jwt


【解决方案1】:

您需要确保方法“ConfigureOAuthTokenConsumption”中使用的 issuer、audienceId 和 AudienceSecret 的值与生成 JWT 令牌时使用的值相同,注意尾部斜杠“/”。

这是我现在唯一想到的。

【讨论】:

  • 它与 web.config 文件中的值相同,并从那里自行提取。代币创建和消费都只有一个项目,所以即使是错误也不会有不同的价值
猜你喜欢
  • 2021-04-24
  • 2014-05-24
  • 2019-04-18
  • 1970-01-01
  • 2017-10-10
  • 2018-01-09
  • 2018-06-06
  • 2018-09-27
  • 2021-06-08
相关资源
最近更新 更多