我有一个现有的ASP.NET MVC 5项目,我正在添加一个Web API 2项目。我想使用承载令牌认证,并遵循了Hongye Sun的教程“OWIN承载令牌认证与Web API示例”和
this question。
在我的登录方法中,对于行Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket); AccessTokenFormat为null。任何想法为什么?
我的帐户控制器:
[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); } }
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 } }
MyWebOAuthGrantResourceOwnerCredientialsContext:
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()。我跟着这个excellent series of posts由Taiseer Joudeh。
全面启动
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...] } }