【问题标题】:Web API 2 OWIN Bearer token authentication - AccessTokenFormat null?Web API 2 OWIN Bearer 令牌身份验证 - AccessTokenFormat null?
【发布时间】:2013-11-25 04:17:18
【问题描述】:

我有一个现有的 ASP.NET MVC 5 项目,我正在向它添加一个 Web API 2 项目。我想使用不记名令牌认证,并遵循了孙宏业的教程“OWIN Bearer Token Authentication with Web API Sample”和this question

在我的Login 方法中,对于Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket); 行,AccessTokenFormat 为空。知道为什么吗?

我的AccountController

[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
    }
}

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是如何设置的?我设置的是否正确?我没有针对任何外部服务进行身份验证,只是对旧的内部服务器进行身份验证。 谢谢。

【问题讨论】:

  • 链接“OWIN Bearer Token Authentication with Web API Sample”似乎损坏了。它显示“此项目尚未发布。”
  • @kr85 我删除了链接。如果您查看下面孙宏业的回答,您会发现他删除了该代码示例,因为它引起了混淆。

标签: asp.net-web-api oauth-2.0 asp.net-mvc-5 owin asp.net-identity


【解决方案1】:

我删除了示例代码,因为它在与 Web API 和 SPA 模板一起使用时会引起混淆。您最好保留模板代码以使用 OAuth 授权服务器生成令牌。在您的场景中,您应该使用资源所有者密码授权来对用户进行身份验证。请查看我关于 SPA 模板的博客,其中包含有关 http://blogs.msdn.com/b/webdev/archive/2013/09/20/understanding-security-features-in-spa-template.aspx 上的密码流程的详细信息

您需要使用 OWIN OAuth Server 的 /token 端点来处理密码登录,而不是编写自己的 Web API 来处理登录。

【讨论】:

  • 感谢您的回复。如果我只需要使用内部遗留服务器应用程序而不是任何外部 OAuth 提供程序来验证用户身份,我还需要设置 AuthorizeEndpointPath 吗?如果是这样,我应该将其设置为什么?我的身份验证调用是否应该覆盖userManager.FindAsync()?在某处有这样的例子吗?谢谢!
  • 你有没有把这个排序?
  • @Ionian316 我必须承认我正在尝试做同样的事情,但几乎没有文档?你做对了吗?
  • @StevenYates 不,我最终返回并使用我的旧自定义 MembershipProvider 类来处理身份验证,而不是使用 OAuth 和令牌。不知道 Web API 2.1 是否改进了很多。当 Web API 3 出现时,我可能会再看看。希望到那时他们会有所改善。
  • @PeterStulinski 不,请参阅上面的 Steven 我的 cmets。
【解决方案2】:

我不确定您是否还在寻找这个问题的答案 - 但这是我在 AngularJS 应用程序中使用的一些代码,用于从我的 WebAPI2 端点获取安全令牌。

    $http({
        method: 'POST', url: '/token', data: { username: uName, password: uPassword, grant_type: 'password' },
        transformRequest: function (obj) {
            var str = [];
            for (var p in obj)
                str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
            return str.join("&");
        }
    }).success(function (data, status, headers, config) {
        console.log("http success", data);
        accessToken.value = data.access_token;
        console.log("access token = ", accessToken.value);
    }).error(function (data, status, headers, config) {
        console.log("http error", data);
    });

然后我可以在任何其他请求的标头中传递 accessToken 以获得身份验证验证。

【讨论】:

    【解决方案3】:

    我遇到了同样的问题 - 这与我在 Startup() 中的初始化有关。

    和你一样,我将 OAuthBearerOptions 存储在静态字段中:

    OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
    

    但后来我错误地使用了同一类的新实例

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

    显然,解决方法是改用静态字段:

    app.UseOAuthBearerAuthentication(OAuthBearerOptions);
    

    事实上,看起来你根本没有调用 UseOAuthBearerAuthentication()。我关注了 Taiseer Joudeh 的 excellent series of posts

    完整的 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...]
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2014-07-06
      • 1970-01-01
      • 2014-10-05
      • 2020-01-14
      • 2014-11-18
      • 2018-06-03
      • 1970-01-01
      • 2014-08-24
      • 1970-01-01
      相关资源
      最近更新 更多