C# 使用基本身份验证向Katana托管的WebAPI添加cookie

C# 使用基本身份验证向Katana托管的WebAPI添加cookie,c#,asp.net-web-api,owin,katana,C#,Asp.net Web Api,Owin,Katana,我已经为Katana实现了一个基本的身份验证中间件(代码如下) (我的客户端托管在跨域上,而实际上是API) 如果出现以下情况,浏览器可以跳过飞行前请求 是真的: 请求方法是GET、HEAD或POST,而应用程序没有 设置除Accept、Accept Language、, 内容语言、内容类型或上次事件ID以及内容类型 标题(如果已设置)是以下内容之一: 应用程序/x-www-form-urlencoded多部分/form data text/plain 在javascript中,我在所有请求上设

我已经为Katana实现了一个基本的身份验证中间件(代码如下)

(我的客户端托管在跨域上,而实际上是API)

如果出现以下情况,浏览器可以跳过飞行前请求 是真的:

请求方法是GET、HEAD或POST,而应用程序没有 设置除Accept、Accept Language、, 内容语言、内容类型或上次事件ID以及内容类型 标题(如果已设置)是以下内容之一: 应用程序/x-www-form-urlencoded多部分/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;
}
我该怎么做才能避开这件事?我的想法是,当用户通过身份验证时,将用户信息存储在cookie中

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


因此,我现在可以从webapi控制器将用户名/密码交换到cookie,我还可以直接使用基本方案进行不使用cookie的设置。

如果web api和javascript文件来自不同的来源,您必须向请求添加授权标头或cookie标头,您不能阻止浏览器发送飞行前请求。否则将对任何受保护的web api造成CSRF攻击

您可以使用或启用CORS场景,它可以为您处理飞行前请求

OWIN cookie中间件负责设置auth cookie并对其进行验证。这似乎是你想要的

顺便说一句,基本身份验证挑战可能会导致浏览器弹出浏览器身份验证对话框,这在大多数web应用程序中是不可能的。不知道这是不是你想要的。相反,使用form post发送用户名和密码并与cookie交换是常见web应用程序的功能

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

[更新] 对于预览请求,它将根据Cookie报头根据规范发送。您可以考虑添加页眉以使其在浏览器上缓存。 是另一个不需要飞行前准备的选项

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

谢谢你的评论。我正在调查cookies auth的事情。我也在看MVC项目,但我一直在把它转换成webapi。MVC使用带有CheckPasswordAndSignInAsync的Owin.Identity东西——我没有这个。我会在几秒钟内用更多的信息更新我的问题。我已经有了cors。我只是不想发送两个请求,因为基本的身份验证,这就是为什么我开始研究cookies的原因。我想要两个选项,cookies和基本身份验证。我还有一个命令行工具,需要访问webapi。不确定两者是否都应该有mode=active,或者只是cookies。我更新了我的答案以回应您的评论。使用cookie无助于避免飞行前请求。您可以考虑飞行前缓存或JSONP作为选择。谢谢。您发布的最后一个代码段。我应该使用它,而不是Context.Authentication.sign,并且我仍然能够执行authenticateSync来再次读取它吗?
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;
        }
    }
}
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");
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);
}
 var authContext = await Context.Authentication.AuthenticateAsync("Application");
            if (authContext != null) 
                return new AuthenticationTicket(authContext.Identity, authContext.Properties);
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.ApplicationAuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, "Test"));
AuthenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant(identity, new AuthenticationProperties()
{
    IsPersistent = true
});