简体   繁体   English

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

[英]Add cookies to Katana Hosted WebAPI with Basic Authentication

I have implemented a Basic Authentication Middleware for Katana (Code below). 我已经为Katana实现了一个基本认证中间件(下面的代码)。

(My client is hosted on a cross domain then the actually API). (我的客户端托管在跨域,然后是实际的API)。

The browser can skip the preflight request if the following conditions are true: 如果满足以下条件,浏览器可以跳过预检请求:

The request method is GET, HEAD, or POST, and The application does not set any request headers other than Accept, Accept-Language, Content-Language, Content-Type, or Last-Event-ID, and The Content-Type header (if set) is one of the following: application/x-www-form-urlencoded multipart/form-data text/plain 请求方法是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

In javascript I set the authentication header( with jquery, beforeSend) on all requests for the server to accept the requests. 在javascript中,我在服务器接受请求的所有请求上设置了认证头(使用jquery,beforeSend)。 This means that above will send the Options request on all requests. 这意味着上面将发送所有请求的选项请求。 I dont want that. 我不想那样。

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

What would I do to get around this? 我该怎么做才能解决这个问题? My idea would be to have the user information stored in a cookie when he has been authenticated. 我的想法是在用户身份验证后将用户信息存储在cookie中。

I also saw in the katana project that are a Microsoft.Owin.Security.Cookies - is this maybe what i want instead of my own basic authentication? 我还在katana项目中看到了Microsoft.Owin.Security.Cookies - 这可能是我想要的而不是我自己的基本身份验证吗?

BasicAuthenticationMiddleware.cs 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);
        }
    }
}

BasicAuthenticationHandler.cs 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;
        }
    }
}

Options.Provider.AuthenticateAsync validated the username/password and return the identity if authenticated. Options.Provider.AuthenticateAsync验证了用户名/密码,并在验证后返回标识。

Specifications 产品规格

What I am trying to solve is: I have a Owin Hosted WebAPI deployed with N Azure Cloud Services. 我想解决的是:我有一个部署了N Azure云服务的Owin托管WebAPI。 Each of them are linked to a storage account that holds a list of username/hashed passwords. 它们中的每一个都链接到一个存储帐户,该帐户包含用户名/哈希值密码列表。

From my client I am adding any of these N services to the client and can then communicate with them by their webapis. 从我的客户端我将这N个服务中的任何一个添加到客户端,然后可以通过他们的webapis与他们进行通信。 They are locked down with authentication. 他们被认证锁定。 The first step is to validate the users over basic authentication scheme with the list provided above. 第一步是使用上面提供的列表通过基本身份验证方案验证用户。 After that, I hope its easy to add other authentication schemes very easy as of the Owin, UseWindowsAzureAuthentication ect, or UseFacebookAuthentication. 之后,我希望很容易添加其他身份验证方案,如Owin,UseWindowsAzureAuthentication ect或UseFacebookAuthentication。 (I do have a challenge here, as the webapi do not have web frontend other then the cross domain site that adds the services). (我确实有一个挑战,因为webapi没有网络前端,除了添加服务的跨域网站)。

If your good at Katana and want to work alittle with me on this, feel free to drop me a mail at pks@s-innovations.net. 如果你擅长Katana并想和我一起工作,请随时给我发邮件至pks@s-innovations.net。 I will provide the answer here at the end also. 我也会在最后提供答案。

Update 更新

Based on answer I have done the following: 根据答案,我做了以下几点:

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");

I assume that it has to be in AuthenticationMode = Active, else the Authorize attributes wont work? 我假设它必须在AuthenticationMode = Active,否则Authorize属性不起作用?

What exactly needs to be in my webapi controller to do the exchange for a cookie? 我的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);
}

Is above okay? 以上还好吗?

Current Solution 现行解决方案

I have added my BasicAuthenticationMiddleware as the active one, added the above CookieMiddleware as passive. 我已将BasicAuthenticationMiddleware添加为活动的,将上述CookieMiddleware添加为被动。

Then in the AuthenticateCoreAsync i do a check if I can login with the Cookie, 然后在AuthenticateCoreAsync中我检查我是否可以使用Cookie登录,

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

So I can now exchange from webapi controller a username/pass to a cookie and i can also use the Basic Scheme directly for a setup that dont use cookies. 所以我现在可以从webapi控制器交换用户名/传递给cookie,我也可以直接使用Basic Sc​​heme进行不使用cookie的设置。

If web api and javascript file are from different origins and you have to add authorization header or cookie header to the request, you cannot prevent browser from sending preflight request. 如果web api和javascript文件来自不同的来源,并且您必须向请求添加授权标头或cookie标头,则无法阻止浏览器发送预检请求。 Otherwise it will cause CSRF attack to any protected web api. 否则,它将导致CSRF攻击任何受保护的Web api。

You can use OWIN Cors package or Web API Cors package to enable CORS scenario, which can handle the preflight request for you. 您可以使用OWIN Cors包Web API Cors包来启用CORS方案,该方案可以为您处理预检请求。

OWIN cookie middleware is responsible for setting auth cookie and verify it. OWIN cookie中间件负责设置auth cookie并验证它。 It seems to be what you want. 这似乎是你想要的。

BTW, Basic auth challenge can cause browser to pop up browser auth dialog, which is not expected in most of the web application. BTW,Basic auth challenge可能会导致浏览器弹出浏览器身份验证对话框,这在大多数Web应用程序中是不可取的。 Not sure if it's what you want. 不确定这是不是你想要的。 Instead, using form post to send user name and password and exchange them with cookie is what common web app does. 相反,使用表单发送用户名和密码并与cookie交换是常见的Web应用程序。

If you have VS 2013 RC or VWD 2013 RC installed on your machine, you can create an MVC project with Individual auth enabled. 如果您的计算机上安装了VS 2013 RC或VWD 2013 RC,则可以创建启用了单独身份验证的MVC项目。 The template uses cookie middleware and form post login. 该模板使用cookie中间件并形成后登录。 Although it's MVC controller, you can simply convert the code to Web API. 虽然它是MVC控制器,但您只需将代码转换为Web API即可。

[Update] Regarding preflight request, it will be sent even with cookie header according to the spec. [更新]关于预检请求,根据规范,即使使用cookie头也会发送。 You may consider to add Max Age header to make it be cached on the browser. 您可以考虑添加Max Age标头以使其在浏览器上缓存。 JSONP is another option which doesn't require preflight. JSONP是另一种不需要预检的选项。

[Update2] In order to set cookie by owin middleware, please use the following sample code. [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
});

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

相关问题 使用Windows身份验证的OWIN / Katana WebAPI不断要求登录/密码 - OWIN/Katana WebAPI using Windows Authentication keeps asking for login/password WebAPI基本身份验证授权过滤器和机器密钥 - WebAPI Basic Authentication Authorization Filter and machine keys WebAPI:没有“基本”一词的基本身份验证如何工作 - WebAPI: How basic authentication can work without the word basic Unity Dependency Resolver无法与Katana和WebAPI一起使用? - Unity Dependency Resolver not working with Katana and WebAPI? 在WebAPI和asp.net核心中使用基于Cookie的身份验证 - Using Cookies based authentication in WebAPI and asp.net core 具有基本身份验证的WebApi会给IIS提供500个内部错误 - WebApi with Basic Authentication gives 500-internal error with IIS ASP.NET WebAPI 基本身份验证始终因 401/未授权而失败 - ASP.NET WebAPI Basic Authentication always fails as 401/Unauthorized 将身份验证添加到ASP.NET WebApi 2.2 - Add Authentication to ASP.NET WebApi 2.2 如何在Katana中编写多个身份验证处理程序? - How to compose multiple authentication handlers in Katana? 在OWIN / Katana身份验证管理器中使用HttpPost进行注销 - Use HttpPost for Logout in OWIN/Katana authentication manager
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM