2016-11-26 49 views
0

我正在爲我的SPA創建一個簡單的OAuth服務器,以及this教程。我可以向OAuth提供商請求JWT罰款。但是,當我在授權標題中發送JWT時(例如:Authorization Bearer <token>),我總是收到401「此請求的授權已被拒絕」。ASP.NET JWT承載認證不會改變請求上下文中的用戶

單步執行代碼,我可以看到我的用戶在請求上下文中被設置爲匿名Windows身份驗證用戶,而不是JWT的所有者。

我看到很多人說在OAuth配置後把app.UseWebAPI()調用,但我沒有在我的startup.cs中註冊WebAPI。我可以做?我目前在我的代碼中沒有app.UseWebAPI()任何地方(並且API沒有授權就可以正常工作)。

這裏是我的startup.cs:

public partial class Startup 
    { 
     public void Configuration(IAppBuilder app) 
     { 
      ConfigureOAuth(app); 
     } 
    } 

public partial class Startup 
    { 
     /// <summary> 
     /// OAuth configuration 
     /// </summary> 
     /// <param name="app"></param> 
     public void ConfigureOAuth(IAppBuilder app) 
     { 
      // Unique identifier for the entity issuing the token 
      var issuer = ConfigurationManager.AppSettings["issuer"]; 
      // Private key used to secure the token 
      var secret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["secret"]); 

      // Enable JWT authentication 
      app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions 
      { 
       AuthenticationMode = AuthenticationMode.Active, 
       AllowedAudiences = new[] { "Any" }, 
       IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[] 
       { 
        new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret) 
       } 
      }); 

      // Create authentication endponit 
      app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions 
      { 
       AllowInsecureHttp = true, 
       TokenEndpointPath = new PathString("/api/login/"), 
       AccessTokenExpireTimeSpan = TimeSpan.FromHours(1), 
       Provider = new CustomOAuthProvider(), 
       AccessTokenFormat = new CustomJwtFormat(issuer) 
      }); 
     } 
    } 

這裏是我的CustomOAuthProvider類:

public class CustomOAuthProvider : OAuthAuthorizationServerProvider 
    { 
     IMessengerRepository repo; 

     public CustomOAuthProvider() : base() 
     { 
      repo = new MessengerRepository(); 
     } 

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

      // Check against the repository to see if the login details are correct 
      var user = repo.Authenticate(context.UserName, context.Password); 

      // If the login details are incorrect, return an error 
      if (user == null) 
      { 
       context.SetError("invalid_grant", "The username or password is incorrect"); 
       context.Rejected(); 
       return Task.FromResult<object>(null); 
      } 

      // Otherwise, return a JWT 
      var ticket = new AuthenticationTicket(SetClaimsIdentity(context, user), new AuthenticationProperties()); 
      context.Validated(ticket); 

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

     public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) 
     { 
      // Because the audience is not validated, the request is validated straight away without any checks 
      context.Validated(); 
      return Task.FromResult<object>(null); 
     } 

     /// <summary> 
     /// Create a ClaimsIdentity based on their username and role 
     /// </summary> 
     /// <param name="context"></param> 
     /// <param name="user"></param> 
     /// <returns></returns> 
     public static ClaimsIdentity SetClaimsIdentity(OAuthGrantResourceOwnerCredentialsContext context, UserDto user) 
     { 
      var identity = new ClaimsIdentity("JWT"); 

      // Add claims for the user themselves 
      identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName)); 
      identity.AddClaim(new Claim("sub", context.UserName)); 
      // Add claim for the user's role 
      identity.AddClaim(new Claim(ClaimTypes.Role, user.UserRole.RoleName.TrimEnd(' '))); 

      return identity; 
     } 
    } 

和我CustomJwtFormat類:

public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket> 
    { 
     // Private key for encoding token 
     private static readonly byte[] secret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["secret"]); 
     // Entity issuing the token 
     private readonly string issuer; 

     public CustomJwtFormat(string issuer) 
     { 
      this.issuer = issuer; 
     } 

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

      var signingKey = new HmacSigningCredentials(secret); 
      var issued = data.Properties.IssuedUtc; 
      var expires = data.Properties.ExpiresUtc; 

      return new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(issuer, null, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey)); 
     } 

     public AuthenticationTicket Unprotect(string protectedText) 
     { 
      throw new NotImplementedException(); 
     } 
    } 

回答

1

以撒,

你實現The ñ看起來不錯,但audience id有一個小問題。

CustomJwtFormatauthorization中,您指定null作爲受衆標識。而對於authentication,則指定Any。 這就是你得到401 Unauthorized的原因。

如果您更改您的JwtSecurityToken與以下它將工作。

return new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(issuer, "Any", data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey));