我已经为 Katana 实现了一个基本身份验证中间件(代码如下)。
(我的客户端托管在跨域上,然后是实际的 API)。
如果满足以下条件,浏览器可以跳过预检请求:
请求方法为 GET、HEAD 或 POST,并且应用程序没有设置除 Accept、Accept-Language、Content-Language、Content-Type 或 Last-Event-ID 之外的任何请求标头,以及 Content-Type 标头(如果设置)是以下之一: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;
}
我该怎么做才能解决这个问题?我的想法是在他通过身份验证后将用户信息存储在 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);
}
}
}
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 验证了用户名/密码,如果通过了身份验证,则返回身份。
规格
我要解决的是:我有一个使用 N Azure 云服务部署的 Owin 托管 WebAPI。它们中的每一个都链接到一个包含用户名/哈希密码列表的存储帐户。
从我的客户端,我将这些 N 服务中的任何一个添加到客户端,然后可以通过他们的 webapis 与它们通信。它们被身份验证锁定。第一步是使用上面提供的列表通过基本身份验证方案验证用户。在那之后,我希望它很容易添加其他身份验证方案,如 Owin、UseWindowsAzureAuthentication 等或 UseFacebookAuthentication。(我在这里确实有一个挑战,因为除了添加服务的跨域站点之外,webapi 没有 Web 前端)。
如果你擅长 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");
我假设它必须处于 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);
}
以上可以吗?
当前解决方案
我已将我的 BasicAuthenticationMiddleware 添加为主动,将上述 CookieMiddleware 添加为被动。
然后在 AuthenticateCoreAsync 我检查我是否可以使用 Cookie 登录,
var authContext = await Context.Authentication.AuthenticateAsync("Application");
if (authContext != null)
return new AuthenticationTicket(authContext.Identity, authContext.Properties);
所以我现在可以从 webapi 控制器交换用户名/传递给 cookie,我也可以直接使用基本方案进行不使用 cookie 的设置。