我已经为Katana实现了一个基本身份验证中间件(下面的代码)。
(我的客户端托管在跨域,然后是实际的API)。
如果满足以下条件,浏览器可以跳过预检请求 是的:
请求方法是GET,HEAD或POST,而应用程序则不是 设置除Accept,Accept-Language之外的任何请求标头, 内容 - 语言,内容类型或最后事件ID,以及内容类型 标头(如果设置)是以下之一: 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 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");
我认为它必须在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,我也可以直接使用Basic Scheme进行不使用cookie的设置。
答案 0 :(得分:2)
如果web api和javascript文件来自不同的来源,并且您必须向请求添加授权标头或Cookie标头,则无法阻止浏览器发送预检请求。否则,它将导致CSRF攻击任何受保护的Web api。
您可以使用OWIN Cors package或Web API Cors package启用CORS方案,该方案可以为您处理预检请求。
OWIN cookie中间件负责设置auth cookie并验证它。这似乎是你想要的。
BTW,基本的auth挑战可能会导致浏览器弹出浏览器身份验证对话框,这在大多数Web应用程序中是不可取的。不确定这是不是你想要的。相反,使用表单发送用户名和密码并与cookie交换是常见的Web应用程序。如果您的计算机上安装了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
});