Asp.net-Web-Api

Web API 2 OWIN Bearer 令牌身份驗證 - AccessTokenFormat null?

  • November 12, 2013

我有一個現有的 ASP.NET MVC 5 項目,我正在向它添加一個 Web API 2 項目。我想使用不記名令牌認證,並遵循了 Hongye Sun 的教程“OWIN Bearer Token Authentication with Web API Sample”和這個問題

在我的Login方法中,對於 line Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);AccessTokenFormat為空。知道為什麼嗎?

我的帳戶控制器

[RoutePrefix("api")]
public class AccountController : ApiController
{        
   public AccountController() {}

   // POST api/login
   [HttpPost]
   [Route("login")]
   public HttpResponseMessage Login(int id, string pwd)
   {
       if (id > 0) // testing - not authenticating right now
       {
           var identity = new ClaimsIdentity(Startup.OAuthBearerOptions.AuthenticationType);
           identity.AddClaim(new Claim(ClaimTypes.Name, id.ToString()));
           AuthenticationTicket ticket = new AuthenticationTicket(identity, new AuthenticationProperties());
           var currentUtc = new SystemClock().UtcNow;
           ticket.Properties.IssuedUtc = currentUtc;
           ticket.Properties.ExpiresUtc = currentUtc.Add(TimeSpan.FromMinutes(30));
           var token = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);
           return new HttpResponseMessage(HttpStatusCode.OK)
           {
               Content = new ObjectContent<object>(new
               {
                   UserName = id.ToString(),
                   AccessToken = token
               }, Configuration.Formatters.JsonFormatter)
           };
       }

       return new HttpResponseMessage(HttpStatusCode.BadRequest);
   }

   // POST api/token
   [Route("token")]
   [HttpPost]
   public HttpResponseMessage Token(int id, string pwd)
   {
       // Never reaches here. Do I need this method?
       return new HttpResponseMessage(HttpStatusCode.OK);
   }
}

啟動類:

public class Startup
{
   private static readonly ILog _log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
   public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
   public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
   public static Func<MyUserManager> UserManagerFactory { get; set; }
   public static string PublicClientId { get; private set; }

   static Startup()
   {
       PublicClientId = "MyWeb";

       UserManagerFactory = () => new MyUserManager(new UserStore<MyIdentityUser>());

       OAuthBearerOptions = new OAuthBearerAuthenticationOptions();

       OAuthOptions = new OAuthAuthorizationServerOptions
       {
           TokenEndpointPath = new PathString("/api/token"),
           Provider = new MyWebOAuthProvider(PublicClientId, UserManagerFactory),
           AuthorizeEndpointPath = new PathString("/api/login"),
           AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
           AllowInsecureHttp = true
       };
   }

   public void Configuration(IAppBuilder app)
   {         
       // Enable the application to use bearer tokens to authenticate users
       app.UseOAuthBearerTokens(OAuthOptions);
       app.UseCookieAuthentication(new CookieAuthenticationOptions
       {
           AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
           LoginPath = new PathString("/api/login")
       });

       // Configure Web API to use only bearer token authentication.
       var config = GlobalConfiguration.Configuration;            
       config.SuppressDefaultHostAuthentication();
       config.Filters.Add(new HostAuthenticationFilter(OAuthBearerOptions.AuthenticationType));

       app.UseWebApi(config);                          
   }
}

MyIdentityUser只是添加了一個額外的屬性:

public class MyIdentityUser : IdentityUser
{
   public int SecurityLevel { get; set; }
}

MyUserManager將我的自定義使用者身份驗證方法呼叫到內部伺服器:

public class MyUserManager : UserManager<MyIdentityUser>
{
   public MyUserManager(IUserStore<MyIdentityUser> store) : base(store) { }

   public MyIdentityUser ValidateUser(int id, string pwd)
   {
       LoginIdentityUser user = null;

       if (MyApplication.ValidateUser(id, pwd))
       {
           // user = ??? - not yet implemented
       }

       return user;
   }
}   

MyWebOAuthProvider(我從 SPA 模板中獲取了這個。僅GrantResourceOwnerCredentials已更改):

public class MyWebOAuthProvider : OAuthAuthorizationServerProvider
{
   private readonly string _publicClientId;
   private readonly Func<MyUserManager> _userManagerFactory;

   public MyWebOAuthProvider(string publicClientId, Func<MyUserManager> userManagerFactory)
   {
       if (publicClientId == null)
       {
           throw new ArgumentNullException("publicClientId");
       }

       if (userManagerFactory == null)
       {
           throw new ArgumentNullException("userManagerFactory");
       }

       _publicClientId = publicClientId;
       _userManagerFactory = userManagerFactory;
   }

   public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
   {
       using (MyUserManager userManager = _userManagerFactory())
       {
           MyIdentityUser user = null;
           var ctx = context as MyWebOAuthGrantResourceOwnerCredentialsContext;

           if (ctx != null)
           {
               user = userManager.ValidateUser(ctx.Id, ctx.Pwd);
           }                

           if (user == null)
           {
               context.SetError("invalid_grant", "The user name or password is incorrect.");
               return;
           }

           ClaimsIdentity oAuthIdentity = await userManager.CreateIdentityAsync(user,
               context.Options.AuthenticationType);
           ClaimsIdentity cookiesIdentity = await userManager.CreateIdentityAsync(user,
               CookieAuthenticationDefaults.AuthenticationType);
           AuthenticationProperties properties = CreateProperties(user.UserName);
           AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
           context.Validated(ticket);
           context.Request.Context.Authentication.SignIn(cookiesIdentity);
       }
   }

   public override Task TokenEndpoint(OAuthTokenEndpointContext context)
   {
       ...  // unchanged from SPA template
   }

   public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
   {
       ...  // unchanged from SPA template
   }

   public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
   {
       ...  // unchanged from SPA template
   }

   public static AuthenticationProperties CreateProperties(string userName)
   {
       ...  // unchanged from SPA template
   }
}

MyWebOAuthGrantResourceOwnerCredentialsContext

public class MyWebOAuthGrantResourceOwnerCredentialsContext : OAuthGrantResourceOwnerCredentialsContext
{
   public MyWebOAuthGrantResourceOwnerCredentialsContext (IOwinContext context, OAuthAuthorizationServerOptions options, string clientId, string userName, string password, IList<string> scope)
       : base(context, options, clientId, userName, password, scope)
   { }

   public int Id { get; set; }        
   public string Pwd { get; set; }
}

AccessTokenFormat是如何設置的?我設置的是否正確?我沒有針對任何外部服務進行身份驗證,只是對舊的內部伺服器進行身份驗證。謝謝。

我遇到了同樣的問題——這與我在 Startup() 中的初始化有關。

像您一樣,我將 OAuthBearerOptions 儲存在靜態欄位中:

OAuthBearerOptions = new OAuthBearerAuthenticationOptions();

但是後來我錯誤地使用了同一類的新實例:

app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());  // wrong!

顯然,解決方法是改用靜態欄位:

app.UseOAuthBearerAuthentication(OAuthBearerOptions);

事實上,看起來你根本沒有呼叫 UseOAuthBearerAuthentication() 。我關注了 Taiseer Joudeh的精彩系列文章

完整的 Startup.cs:

public class Startup
{
   public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }

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

       ConfigureOAuth(app);

       WebApiConfig.Register(config);
       app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
       app.UseWebApi(config);
   }

   public void ConfigureOAuth(IAppBuilder app)
   {
       //use a cookie to temporarily store information about a user logging in with a third party login provider
       app.UseExternalSignInCookie(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ExternalCookie);
       OAuthBearerOptions = new OAuthBearerAuthenticationOptions();

       OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions() {

           AllowInsecureHttp = true,
           TokenEndpointPath = new PathString("/token"),
           AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
           Provider = new SimpleAuthorizationServerProvider()  // see post
       };

       // Token Generation
       app.UseOAuthAuthorizationServer(OAuthServerOptions);
       app.UseOAuthBearerAuthentication(OAuthBearerOptions);

       //[Configure External Logins...]
   }
}

引用自:https://stackoverflow.com/questions/19938947