我们有一个非常大的 Web 应用程序,有很多页面。这些页面需要我们了解用户的角色才能正确显示内容。所以在 Application_AuthenticationRequest 中,我们有这样的代码:
var id = new GenericIdentity(Request.Headers["ceid"]);
var rp = new MyRoleProvider();
var principal = new GenericPrincipal(id, rp.GetRolesForUser(id.Name));
Context.User = principal;
问题是我们需要使用 Web 服务来获取角色,而且由于每次用户访问页面时都会调用一次,因此 Web 服务被访问的次数过多。
理想的解决方案是,如果我们可以将角色存储在 session 变量中,但是, session 在 Application_AuthenticateRequest 中不可用。我们考虑在应用程序变量中存储一个包含所有用户条目的字典,但我希望我们能找到更好的解决方案。
有什么方法可以存储当前用户的角色,使它们在 Application_AuthenticationRequest 中可用?我们很有安全意识; cookie 会是一个有效的选择吗?
最佳答案
我将创建一个静态类来保存我们所有当前用户的角色列表(基本上扩展了您的 Dictionary 想法):如果列表中不存在给定 ID 的角色,我们会从 Web 服务中检索它们。否则我们返回存储的角色。
我们需要在一定数量的用户不事件后使数据过期的方法(我们不想一直将所有用户的所有角色都保存在内存中),因此我们将实现我们自己的过期机制(在我的示例中为 20 分钟)。
下面是我将如何编写类,以及如何使用它:
/// <summary>
/// This is the static class that will hold the roles for all active users (active users
/// are those that have been using the website within the last 20 minutes).
/// </summary>
public static class MyRoles
{
/// <summary>
/// This class holds your roles
/// </summary>
private class Principal
{
private DateTime lastAccess; // Our expiration timer
private System.Security.Principal.GenericPrincipal principal; // Our roles
public Principal(System.Security.Principal.GenericPrincipal principal)
{
this.principal = principal;
this.lastAccess = DateTime.Now;
}
public System.Security.Principal.GenericPrincipal GenericPrincipal
{
get
{
// We reset our timer. It will expire 20 minutes from now.
this.lastAccess = DateTime.Now;
return principal;
}
}
/// <summary>
/// This tells us if a user has been active in the last 20 minutes
/// </summary>
public bool IsValid
{
get
{
// Valid within 20 minutes from last call
return DateTime.Now <= this.lastAccess.AddMinutes(20);
}
}
}
/// <summary>
/// This will hold IDs and related roles
/// </summary>
private static Dictionary<string, Principal> ids = new Dictionary<string, Principal>();
/// <summary>
/// Method to retrieve roles for a given ID
/// </summary>
/// <param name="header">Our ID</param>
/// <returns></returns>
public static System.Security.Principal.GenericPrincipal GetRoles(string header)
{
if (ids.ContainsKey(header) && ids[header].IsValid)
{
// We have roles for this ID
return ids[header].GenericPrincipal;
}
else
{
// We don't have this ID (or it's expired) so get it from the web service
var id = new System.Security.Principal.GenericIdentity(header);
var principal = new System.Security.Principal.GenericPrincipal(id, new MyRoleProvider().GetRolesForUser(id.Name));
// Store roles
ids.Add(header, new Principal(principal));
return principal;
}
}
}
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
// This is how we use our class
var principal = MyRoles.GetRoles(Request.Headers["ceid"]);
}
请注意,我的示例代码没有考虑存储 GenericPrincipal 实例列表所需的内存占用。我的目标只是让您了解如何在不使用 Session 对象的情况下保留(和过期)用户数据。
20 分钟过期机制可以轻松链接到 Session_End 事件,以便在 session 结束后立即使数据过期(如果您使用的是 InProc session )。
另请注意,就像 session 一样,此解决方案仅适用于单服务器环境。如果您有一个负载平衡的环境(2 个或更多 Web 服务器),则必须将数据保存到数据库中。
我希望这能够帮到你。如果没有,请告诉我原因,我会尽力帮助您:)
关于c# - 如何保留 Application_AuthenticateRequest 中所需的 session 特定值?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30519039/