问题
使用.netcore开发的Web应用部署在多台服务器上,使用nginx使用负载均衡,可以提高WEB应用的可用率,但是简单的部署登录的信息不能在每台服务器之间共享,即在A服务器上登录后,下次访问可能被nginx指引到B服务器上访问,这时B服务器无法验证在A服务器的登录信息,会被重定向到登录页面,网上有很多文章写了相关问题的解决方案,但是都没写得很清楚的,综合几位大神的方法,重新记录一下。
解决方法
使用共同的密钥文件进行cookie的加解密,只需要指明密钥文件的路径,其他的代码没有变化
1、在Program.cs中添加以下代码:
builder.Services.AddAuthentication("cookie11")
.AddCookie("cookie11", option =>
{
option.Cookie.SameSite = SameSiteMode.Lax;
option.Cookie.HttpOnly = true;
option.LoginPath = new PathString("/Login");
option.AccessDeniedPath = new PathString("/RightErr");
});
builder.Services.AddDataProtection()
.SetApplicationName("cookieshare")
.AddKeyManagementOptions(options =>
{
options.XmlRepository = new XmlRepository();
});
其中:SetApplicationName("cookieshare")是设置同一个应用的应用名称,XmlRepository是自己开发的一个类,这个类继承了IXmlRepository接口。
完整的Program.cs
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.Options;
using WebApplication12.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddAuthentication("cookie11")
.AddCookie("cookie11", option =>
{
option.Cookie.SameSite = SameSiteMode.Lax;
option.Cookie.HttpOnly = true;
option.LoginPath = new PathString("/Login");
option.AccessDeniedPath = new PathString("/RightErr");
});
builder.Services.AddDataProtection()
.SetApplicationName("cookieshare")
.AddKeyManagementOptions(options =>
{
options.XmlRepository = new XmlRepository();
});
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
2、XmlRepository类代码:
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.DataProtection.Repositories;
using System.Net;
using System.Security.Claims;
using System.Xml.Linq;
namespace WebApplication12.Models
{
public class XmlRepository : IXmlRepository
{
private readonly string _KeyContentPath = "";
public XmlRepository()
{
_KeyContentPath = Path.Combine(Directory.GetCurrentDirectory(), "key.xml");
}
public IReadOnlyCollection<XElement> GetAllElements()
{
var elements = new List<XElement>() { XElement.Load(_KeyContentPath) };
return elements;
}
public void StoreElement(XElement element, string friendlyName)
{
}
}
}
其中的key.xml文件需要放在Web应用下,指明文件路径。
3、登录方法
public IActionResult Login()
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, "mm")
};
var claimsIdentity = new ClaimsIdentity(claims, "cookie11");
AuthenticationProperties ap = new AuthenticationProperties
{
ExpiresUtc = DateTime.UtcNow.AddMinutes(20)
};
HttpContext.SignInAsync("cookie11", new ClaimsPrincipal(claimsIdentity), ap);
return RedirectToAction("Index", "Home");
}
4、生成key.xml文件
我创建了一个NUnit测试项目,需要引用Microsoft.AspNetCore.DataProtection和Microsoft.Extensions.DependencyInjection包
[Test]
public void Test1()
{
var directory = "d:\\temp\\a\\";
var services = new ServiceCollection();
services
.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(directory));
var keyManager = services
.BuildServiceProvider()
.GetRequiredService<IKeyManager>();
keyManager.CreateNewKey(
DateTimeOffset.Now, DateTimeOffset.Now.AddYears(100));
Assert.Pass();
}
运行后会在d:\temp\a下创建一个随机名称的xml文件,将这个xml文件重命名为key.xml,添加到Web应用中,并将复制到输出目录设置为“如果较新则复制”
<?xml version="1.0" encoding="utf-8"?>
<key id="32703ef5-aebc-4261-afba-9dfec69c135c" version="1">
<creationDate>2024-01-07T08:44:17.8516684Z</creationDate>
<activationDate>2024-01-07T16:44:17.8505563+08:00</activationDate>
<expirationDate>2124-01-07T16:44:17.8515013+08:00</expirationDate>
<descriptor deserializerType="Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
<descriptor>
<encryption algorithm="AES_256_CBC" />
<validation algorithm="HMACSHA256" />
<masterKey p4:requiresEncryption="true" xmlns:p4="http://schemas.asp.net/2015/03/dataProtection">
<!-- Warning: the key below is in an unencrypted form. -->
<value>OhW8u4UznUy//I2hPbkzVhJE5oloPSRnzZ+lfSNk+t6MsHEdDm6wGaNVVg81BwKqosETBP6KTKDKEmOOWElK0w==</value>
</masterKey>
</descriptor>
</descriptor>
</key>
小结
这样部署到多台服务器上,登录的Cookie就能统一验证了。