使用基本身份验证向Katana Hosted WebAPI添加cookie

Pou*_*sen 5 c# asp.net-web-api owin katana

我已经为Katana实现了一个基本认证中间件(下面的代码).

(我的客户端托管在跨域,然后是实际的API).

如果满足以下条件,浏览器可以跳过预检请求:

请求方法是GET,HEAD或POST,并且应用程序不会设置除Accept,Accept-Language,Content-Language,Content-Type或Last-Event-ID以及Content-Type标头之外的任何请求标头( if set)是以下之一:application/x-www-form-urlencoded multipart/form-data text/plain

在javascript中,我在服务器接受请求的所有请求上设置了认证头(使用jquery,beforeSend).这意味着上面将发送所有请求的选项请求.我不想那样.

function make_base_auth(user, password) {
    var tok = user + ':' + password;
    var hash = Base64.encode(tok);
    return "Basic " + hash;
}
Run Code Online (Sandbox Code Playgroud)

我该怎么做才能解决这个问题?我的想法是在用户身份验证后将用户信息存储在cookie中.

我还在katana项目中看到了Microsoft.Owin.Security.Cookies - 这可能是我想要的而不是我自己的基本身份验证吗?

BasicAuthenticationMiddleware.cs

using Microsoft.Owin;
using Microsoft.Owin.Logging;
using Microsoft.Owin.Security.Infrastructure;
using Owin;

namespace Composite.WindowsAzure.Management.Owin
{
    public class BasicAuthenticationMiddleware : AuthenticationMiddleware<BasicAuthenticationOptions>
    {
        private readonly ILogger _logger;

        public BasicAuthenticationMiddleware(
           OwinMiddleware next,
           IAppBuilder app,
           BasicAuthenticationOptions options)
            : base(next, options)
        {
            _logger = app.CreateLogger<BasicAuthenticationMiddleware>();
        }

        protected override AuthenticationHandler<BasicAuthenticationOptions> CreateHandler()
        {
            return new BasicAuthenticationHandler(_logger);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

BasicAuthenticationHandler.cs

using Microsoft.Owin.Logging;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Infrastructure;
using System;
using System.Text;
using System.Threading.Tasks;

namespace Composite.WindowsAzure.Management.Owin
{
    public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOptions>
    {
        private readonly ILogger _logger;

        public BasicAuthenticationHandler(ILogger logger)
        {
            _logger = logger;
        }
        protected override Task ApplyResponseChallengeAsync()
        {
            _logger.WriteVerbose("ApplyResponseChallenge");
            if (Response.StatusCode != 401)
            {
                return Task.FromResult<object>(null);
            }

            AuthenticationResponseChallenge challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode);

            if (challenge != null)
            {
                Response.Headers.Set("WWW-Authenticate", "Basic");
            }

            return Task.FromResult<object>(null);
        }
        protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
        {
            _logger.WriteVerbose("AuthenticateCore");

            AuthenticationProperties properties = null;

            var header = Request.Headers["Authorization"];

            if (!String.IsNullOrWhiteSpace(header))
            {
                var authHeader = System.Net.Http.Headers.AuthenticationHeaderValue.Parse(header);

                if ("Basic".Equals(authHeader.Scheme, StringComparison.OrdinalIgnoreCase))
                {
                    string parameter = Encoding.UTF8.GetString(Convert.FromBase64String(authHeader.Parameter));
                    var parts = parameter.Split(':');
                    if (parts.Length != 2)
                        return null;

                    var identity = await Options.Provider.AuthenticateAsync(userName: parts[0], password: parts[1], cancellationToken: Request.CallCancelled);
                    return new AuthenticationTicket(identity, properties);
                }
            }

            return null;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Options.Provider.AuthenticateAsync验证了用户名/密码,并在验证后返回标识.

产品规格

我想解决的是:我有一个部署了N Azure云服务的Owin托管WebAPI.它们中的每一个都链接到一个存储帐户,该帐户包含用户名/哈希值密码列表.

从我的客户端我将这N个服务中的任何一个添加到客户端,然后可以通过他们的webapis与他们进行通信.他们被认证锁定.第一步是使用上面提供的列表通过基本身份验证方案验证用户.之后,我希望很容易添加其他身份验证方案,如Owin,UseWindowsAzureAuthentication ect或UseFacebookAuthentication.(我确实有一个挑战,因为webapi没有网络前端,除了添加服务的跨域网站).

如果你擅长Katana并想和我一起工作,请随时给我发邮件至pks@s-innovations.net.我也会在最后提供答案.

更新

根据答案,我做了以下几点:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = "Application",
    AuthenticationMode = AuthenticationMode.Active,
    LoginPath = "/Login",
    LogoutPath = "/Logout",
    Provider = new CookieAuthenticationProvider
    {
        OnValidateIdentity = context =>
        {
            //    context.RejectIdentity();
            return Task.FromResult<object>(null);
        },
        OnResponseSignIn = context =>
        {

        }
    }
});

app.SetDefaultSignInAsAuthenticationType("Application");
Run Code Online (Sandbox Code Playgroud)

我假设它必须在AuthenticationMode = Active,否则Authorize属性不起作用?

我的webapi控制器到底需要做什么来交换cookie?

public async Task<HttpResponseMessage> Get()
{
    var context = Request.GetOwinContext();
    //Validate Username and password
    context.Authentication.SignIn(new AuthenticationProperties()
    {
        IsPersistent = true
    },
    new ClaimsIdentity(new[] { new Claim(ClaimsIdentity.DefaultNameClaimType, "MyUserName") }, "Application"));

    return Request.CreateResponse(HttpStatusCode.OK);
}
Run Code Online (Sandbox Code Playgroud)

以上还好吗?

现行解决方案

我已将BasicAuthenticationMiddleware添加为活动的,将上述CookieMiddleware添加为被动.

然后在AuthenticateCoreAsync中我检查我是否可以使用Cookie登录,

 var authContext = await Context.Authentication.AuthenticateAsync("Application");
            if (authContext != null) 
                return new AuthenticationTicket(authContext.Identity, authContext.Properties);
Run Code Online (Sandbox Code Playgroud)

所以我现在可以从webapi控制器交换用户名/传递给cookie,我也可以直接使用Basic Sc​​heme进行不使用cookie的设置.

Hon*_*Sun 2

如果 Web api 和 javascript 文件来自不同来源,并且您必须在请求中添加授权标头或 cookie 标头,则无法阻止浏览器发送预检请求。否则将会对任何受保护的 Web api 造成 CSRF 攻击。

您可以使用OWIN Cors 包Web API Cors 包来启用 CORS 场景,它可以为您处理预检请求。

OWIN cookie 中间件负责设置 auth cookie 并验证它。这似乎就是你想要的。

顺便说一句,基本身份验证挑战可能会导致浏览器弹出浏览器身份验证对话框,这在大多数 Web 应用程序中是不期望的。不确定这是否是您想要的。相反,常见的 Web 应用程序会使用表单发布来发送用户名和密码并与 cookie 进行交换。

如果您的计算机上安装了 VS 2013 RC 或 VWD 2013 RC,则可以创建启用了个人身份验证的 MVC 项目。该模板使用 cookie 中间件和表单登录后。虽然它是MVC控制器,但您可以简单地将代码转换为Web API。

[更新] 关于预检请求,根据规范,即使带有 cookie 标头也会发送。您可以考虑添加Max Age标头以使其缓存在浏览器上。 JSONP是另一个不需要预检的选项。

[Update2] 为了通过owin中间件设置cookie,请使用以下示例代码。

var identity = new ClaimsIdentity(CookieAuthenticationDefaults.ApplicationAuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, "Test"));
AuthenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant(identity, new AuthenticationProperties()
{
    IsPersistent = true
});
Run Code Online (Sandbox Code Playgroud)