简体   繁体   English

Web API 2 OWIN承载令牌认证 - AccessTokenFormat null?

[英]Web API 2 OWIN Bearer token authentication - AccessTokenFormat null?

I have an existing ASP.NET MVC 5 project and I'm adding a Web API 2 project to it. 我有一个现有的ASP.NET MVC 5项目,我正在添加一个Web API 2项目。 I want to use bearer token authentication and have followed Hongye Sun's tutorial "OWIN Bearer Token Authentication with Web API Sample" and this question as well. 我想使用不记名令牌认证,并遵循Hongye Sun的教程“使用Web API示例进行OWIN承载令牌认证” 这个问题

In my Login method, for the line Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket); 在我的Login方法中,对于Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket); , the AccessTokenFormat is null. AccessTokenFormat为null。 Any idea why? 知道为什么吗?

My AccountController : 我的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);
    }
}

Startup class: 启动课程:

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 just adds an extra property: MyIdentityUser只是添加了一个额外的属性:

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

MyUserManager calls my custom user authentication method to an internal server: 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 (I took this from the SPA template. Only GrantResourceOwnerCredentials has been changed): 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 : 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; }
}

How is AccessTokenFormat set? AccessTokenFormat是如何设置的? Is what I've set up correct? 我的设置是否正确? I'm not authenticating against any external services, just a legacy internal server. 我没有对任何外部服务进行身份验证,只是一个传统的内部服务器。 Thanks. 谢谢。

I had the same problem - it was to do with my initialisation in Startup(). 我遇到了同样的问题 - 这与我在Startup()中的初始化有关。

Like you, I was storing the OAuthBearerOptions in a static field: 和你一样,我将OAuthBearerOptions存储在一个静态字段中:

OAuthBearerOptions = new OAuthBearerAuthenticationOptions();

But then I was wrongly using a new instance of the same class later on: 但后来我错误地使用了同一个类的新实例

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

Obviously the fix was to use the static field instead: 显然,修复是使用静态字段:

app.UseOAuthBearerAuthentication(OAuthBearerOptions);

In fact, it doesn't look like you call UseOAuthBearerAuthentication() at all. 实际上,它看起来根本就不像你调用UseOAuthBearerAuthentication()。 I followed this excellent series of posts by Taiseer Joudeh. 我跟随了Taiseer Joudeh的这一系列优秀帖子

Full Startup.cs: 完全启动.s:

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...]
    }
}

I'm not sure if you're still looking for the answer to this - but here's a bit of code that I'm using in my AngularJS app to get the security token from my WebAPI2 endpoint. 我不确定你是否还在寻找答案 - 但是我在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);
    });

I can then pass the accessToken in the header of any other requests in order to get the authentication validation. 然后,我可以在任何其他请求的标头中传递accessToken,以获得身份验证验证。

I have removed the sample code as it can cause confusion when it's used with Web API and SPA template. 我删除了示例代码,因为它与Web API和SPA模板一起使用时会引起混淆。 You'd better stay with the template code to use OAuth authorization server to generate token. 您最好使用模板代码来使用OAuth授权服务器来生成令牌。 In your scenario, you should use resource owner password grant to authenticate the user. 在您的方案中,您应该使用资源所有者密码授予来验证用户。 Please check my blog on SPA template which has details about the password flow on http://blogs.msdn.com/b/webdev/archive/2013/09/20/understanding-security-features-in-spa-template.aspx 请在http://blogs.msdn.com/b/webdev/archive/2013/09/20/understanding-security-features-in-spa-template.aspx上查看我的SPA模板博客,其中包含有关密码流的详细信息。

Instead of writing your own Web API to handle login, you need to use OWIN OAuth Server's /token endpoint to handle password login. 您需要使用OWIN OAuth服务器的/令牌端点来处理密码登录,而不是编写自己的Web API来处理登录。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM