Web分布式部署,跨应用程序Forms身份验证的集成方案
最近一个项目要求进行分布式部署、保证在双十一期间系统的正常运行,虽然该系统平时访问量不是很大,但是基于业务需要,必须在至少两台服务器上部署。
该系统需要登录后才可以使用,首先需要解决分布式部署的用户状态共享问题,在项目中使用的是Forms身份验证,
如果是用Session,可以考虑使用微软的Azure Redis Cache(https://msdn.microsoft.com/library/azure/dn690522.aspx)将session存储到Redis中。
1、针对Forms配置项的改造,主要是在web.cofig中配置了machineKey,参考https://msdn.microsoft.com/zh-cn/library/eb0zx8fc.aspx
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<authentication mode="Forms">
<forms name="Quote" loginUrl="~/Account/Login" protection="All" timeout="" defaultUrl="~/Home/index" path="/" />
</authentication>
<!--跨应用程序进行 Forms 身份验证 https://msdn.microsoft.com/zh-cn/library/eb0zx8fc.aspx-->
<!--<machineKey validationKey="[your key here]" decryptionKey="[your key here]" validation="SHA1" />-->
<machineKey validationKey="77A439696CB986680CEE71CB179BBFFA75AA0FE3AB875B278EE8C54536F2B364E1BDAB809BA26D4263C33863D29B4040CD55D9665E8002D26F04A80C701A4067" decryptionKey="79378FA6BD4BE839D0B8C1E94367A820C77F38FA9CD8C7F0" validation="SHA1"/>
<authorization>
<allow users="*" />
</authorization>
<anonymousIdentification enabled="true" cookieName=".DotNet" />
用户登录后 创建一个票据,放在cookie中,保存方法
/// <summary>
/// 创建一个票据,放在cookie中
/// 票据中的数据经过加密,解决cookie的安全问题。
/// </summary>
/// <param name="userInfo">登录用户</param>
/// <param name="issueDateTime">发布时间</param>
/// <param name="experation">过期时间</param>
/// <param name="isPersistent">持久性</param>
public static void SetCookie(BaseUserInfo userInfo, DateTime? issueDateTime = null, DateTime? experation = null, bool isPersistent = true)
{
if (issueDateTime == null)
{
issueDateTime = DateTime.Now;
}
if (experation == null)
{
//设置COOKIE过期时间
double userLoginExperation;
if (double.TryParse(ConfigurationManager.AppSettings["UserLoginExperation"], out userLoginExperation))
{
experation = DateTime.Now.AddHours(userLoginExperation);
}
else
{
experation = DateTime.Now.AddHours();
}
}
BaseSystemInfo.UserInfo = userInfo;
BaseSystemInfo.UserInfo.ServicePassword = BaseSystemInfo.ServicePassword;
BaseSystemInfo.UserInfo.ServiceUserName = BaseSystemInfo.ServiceUserName;
BaseSystemInfo.UserInfo.SystemCode = BaseSystemInfo.SystemCode;
JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
string userData = javaScriptSerializer.Serialize(BaseSystemInfo.UserInfo);
//生成验证票据,其中包括用户名、生效时间、过期时间、是否永久保存和用户数据等。
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(, userInfo.NickName, (DateTime)issueDateTime, (DateTime)experation, isPersistent, userData, FormsAuthentication.FormsCookiePath);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket));
cookie.Expires = (DateTime)experation;
HttpResponse response = HttpContext.Current.Response;
//指定客户端脚本是否可以访问[默认为false]
cookie.HttpOnly = true;
//指定统一的Path,比便能通存通取
cookie.Path = "/";
//设置跨域,这样在其它二级域名下就都可以访问到了 同一个网站下
//cookie.Domain = "jinhuoyan.net";
response.AppendCookie(cookie);
}
此处提供一下MachineKey的生成方法:
#region public ActionResult CreateMachineKey(int m,int n) 生成machineKey密钥
/// <summary>
/// 生成machineKey密钥
/// </summary>
/// <param name="m"></param>
/// <param name="n"></param>
/// <remarks>
/// 参数:
/// 第一个参数是用于创建 decryptionKey 属性的字节数。
/// 第二个参数是用于创建 validationKey 属性的字节数。
/// 注意:所创建的十六进制字符串的大小是从命令行传入值的大小的两倍。例如,如果您为密钥指定 24 字节,则转换后相应的字符串长度为 48 字节。
/// decryptionKey 的有效值为 8 或 24。此属性将为数据加密标准 (DES) 创建一个 16 字节密钥,或者为三重 DES 创建一个 48 字节密钥。
/// validationKey 的有效值为 20 到 64。此属性将创建长度从 40 到 128 字节的密钥。
/// 代码的输出是一个完整的<machineKey>元素,您可以将其复制并粘贴到Machine.config文件中。
/// </remarks>
[CheckLogin]
public ActionResult CreateMachineKey(int m, int n)
{
//String[] commandLineArgs = System.Environment.GetCommandLineArgs();
//string decryptionKey = CreateKey(System.Convert.ToInt32(commandLineArgs[1]));
//string validationKey = CreateKey(System.Convert.ToInt32(commandLineArgs[2]));
string decryptionKey = CreateKey(m);
string validationKey = CreateKey(n);
string result = string.Format("<machineKey validationKey=\"{0}\" decryptionKey=\"{1}\" validation=\"SHA1\"/>", validationKey, decryptionKey);
return Content(result);
} [NonAction]
static String CreateKey(int numBytes)
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] buff = new byte[numBytes];
rng.GetBytes(buff);
return BytesToHexString(buff);
}
[NonAction]
static String BytesToHexString(byte[] bytes)
{
StringBuilder hexString = new StringBuilder(); for (int counter = ; counter < bytes.Length; counter++)
{
hexString.Append(String.Format("{0:X2}", bytes[counter]));
}
return hexString.ToString();
}
#endregion
2、创建一个属性,用于在Action上标注对应的菜单的Code
/// <summary>
/// CustomerResourceAttribute
/// 自定义的对方法应用的属性,在Action上标注权限菜单对应的Code
///
/// 修改纪录
///
/// 2015-10-11 版本:1.0 SongBiao 创建文件。
///
/// <author>
/// <name>SongBiao</name>
/// <date>2015-10-11</date>
/// </author>
/// </summary> [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public sealed class CustomerResourceAttribute : Attribute
{
private readonly string _resourceName; public CustomerResourceAttribute(string resourceName)
{
_resourceName = resourceName;
}
/// <summary>
/// 资源名称
/// </summary>
public string ResourceName
{
get { return _resourceName; }
} /// <summary>
/// 资源描述
/// </summary>
public string Descript { get; set; }
}
3、创建一个用户检测用户是否登录的标签
/// <summary>
/// CheckLoginAttribute
/// 用于检测用户是否处于登录状态的标签
/// 某些功能只需要用户登录就可以使用
///
/// 修改纪录
///
/// 2015-10-11 版本:1.0 SongBiao 创建文件。
///
/// <author>
/// <name>SongBiao</name>
/// <date>2015-10-11</date>
/// </author>
/// </summary> public class CheckLoginAttribute :System.Web.Mvc.AuthorizeAttribute //AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool pass = false;
if (!httpContext.Request.IsAuthenticated)
{
httpContext.Response.StatusCode = ;//无权限状态码
pass = false;
}
else
{
pass = true;
}
return pass;
} protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
base.HandleUnauthorizedRequest(filterContext);
if (filterContext.HttpContext.Response.StatusCode == )
{
filterContext.Result = new RedirectResult("/");
}
}
}
4、创建一个身份验证过滤器,实现对匿名访问、登录后可访问、验证是否具有对应菜单(action)的处理
/// <summary>
/// 身份验证过滤器
/// 1、匿名访问
/// 2、登录就可以访问
/// 3、需要验证是否有菜单或按钮或资源的权限
///
///
/// 修改纪录
///
/// 2015-10-11 版本:1.0 SongBiao 创建文件。
///
/// <author>
/// <name>SongBiao</name>
/// <date>2015-10-11</date>
/// </author>
/// </summary> public class PermissionCheckAttribute : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (filterContext.HttpContext.Request.Url == null)
{
throw new ArgumentNullException("filterContext");
}
string pageUrl = filterContext.HttpContext.Request.Url.AbsolutePath; //OperateContext.GetThisPageUrl(false);
//是否是Ajax请求
var bAjax = filterContext.HttpContext.Request.IsAjaxRequest(); //1、允许匿名访问 用于标记在授权期间要跳过 AuthorizeAttribute 的控制器和操作的特性
var actionAnonymous = filterContext.ActionDescriptor.GetCustomAttributes(typeof(AllowAnonymousAttribute), true) as IEnumerable<AllowAnonymousAttribute>;
var controllerAnonymous = filterContext.Controller.GetType().GetCustomAttributes(typeof(AllowAnonymousAttribute), true) as IEnumerable<AllowAnonymousAttribute>;
if ((actionAnonymous != null && actionAnonymous.Any()) || (controllerAnonymous != null && controllerAnonymous.Any()))
{
return;
}
//2、判断登录状态 Controller Action 标签 某些功能只需判断是否登录 用户没登录 调到登录页面
var checkLoginControllerAttr =filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof (CheckLoginAttribute), true) as IEnumerable<CheckLoginAttribute>;
if (checkLoginControllerAttr != null && checkLoginControllerAttr.Any())
{
return;
}
var checkLoginActionAttr = filterContext.ActionDescriptor.GetCustomAttributes(typeof(CheckLoginAttribute), true) as IEnumerable<CheckLoginAttribute>;
if (checkLoginActionAttr != null && checkLoginActionAttr.Any())
{
return;
}
//3、有些要判断是否有某个菜单 action的权限 具体判断某个用户是否有某个权限
//用于标记在授权期间需要CustomerResourceAttribute 的操作的特性
var attNames = filterContext.ActionDescriptor.GetCustomAttributes(typeof(CustomerResourceAttribute), true) as IEnumerable<CustomerResourceAttribute>;
//用户具有的菜单
var moduleList = OperateContext.Current.ModuleList;
if (moduleList == null || !moduleList.Any())
{
//没有获取到任何菜单 拒绝访问
filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { Controller = "Message", action = "General", bAjaxReq = bAjax, message = "没有获取您拥有的权限菜单" }));
}
else
{
//判断用户的权限菜单中的code是否与控制器上标示的资源的code一致
var joinResult = (from aclEntity in moduleList
join attName in attNames on aclEntity.Code equals attName.ResourceName
select attName).Any();
if (joinResult)
{
return;
}
else
{
//没有对应的权限 拒绝访问
filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { Controller = "Message", action = "DenyAccess", bAjaxReq = bAjax, message = "您没有访问权限:" + pageUrl }));
}
}
}
}
这里注意下OperateContext.Current.ModuleList,这个是取得用户具有的菜单,通用权限系统底层提供有对应的接口。
5、在FilterConfig中加入权限验证
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new PermissionCheckAttribute());
}
}
6、应用方法
对某个Action加入自定义验证权限的标签
不需要验证登录状态的
只要登录就可以访问的
至此,通用权限管理系统底层的权限控制、跨应用程序Forms身份验证集成完成了。如有疑问,欢迎提出。
Web分布式部署,跨应用程序Forms身份验证的集成方案的更多相关文章
- .net Forms身份验证不能用在应用的分布式部署中吗?
参照网上的一些方法,使用Forms身份验证对应用进行分布式部署,发现没有成功. 应用部署的两台内网服务器:192.168.1.19,192.168.1.87,使用Nginx做负载分配,配置完全相同:每 ...
- ASP.NET Web API Basic Identity 中的基本身份验证
缺点 用户凭证在请求中发送. 凭据作为明文发送. 每个请求都会发送凭据. 无法注销,除非结束浏览器会话. 易于跨站点请求伪造(CSRF); 需要反CSRF措施. 优点 互联网标准. 受所有主要浏览器支 ...
- ASP.NET Forms 身份验证
ASP.NET Forms 身份验证 在开发过程中,我们需要做的事情包括: 1. 在 web.config 中设置 Forms 身份验证相关参数.2. 创建登录页. 登录页中的操作包括: 1. 验证用 ...
- asp.net Forms身份验证
Web.config中的配置<system.web><authentication mode="Forms"> <forms name="K ...
- 采用Asp.Net的Forms身份验证时,持久Cookie的过期时间会自动扩展
原文:http://www.cnblogs.com/sanshi/archive/2012/06/22/2558476.html 若是持久Cookie,Cookie的有效期Expiration属性有当 ...
- 通过Forms身份验证设置不同页面的访问权限
使用Forms身份验证的时候,如果允许注册页面可以匿名用户访问,其他所有页面只允许注册用户访问,我们可以如下设置web.config文件来达到上述的效果: 1.在“system.web”节点下,添加登 ...
- asp.net Forms身份验证详解
在做网站的时候,都会用到用户登录的功能.对于一些敏感的资源,我们只希望被授权的用户才能够访问,这让然需要用户的身份验证.对于初学者,通常将用户登录信息存放在Session中,笔者在刚接触到asp.ne ...
- Forms身份验证 知识总结
最简单的Forms验证实现方法:FormsAuthentication.SetAuthCookie()方法,传递一个登录名即可FormsAuthentication.SignOut()方法退出Form ...
- 关于ASP.NET的“Forms身份验证”
目录结构如图如示: 如果用户没有通过身份验证则跳转到登录页面让用户登录,在配置文件的<system.web></system.web>结点下添加如下代码: <!--身份验 ...
随机推荐
- python 加密解密(base64, AES)
1. 使用base64 s1 = base64.encodestring('hello world') s2 = base64.decodestring(s1) print s1, s2 结果 1 2 ...
- Session和Cookie的分析与区别
首先说一下Web.config文件中的cookieless="false"的理解 cookieless="false"表示: 如果用户浏览器支持cookie时启 ...
- Uvalive 4865 Data Recovery 最大流
题意就是 给一个50 * 50的矩阵 然后给出每行每列元素的和 和一个初始矩阵 矩阵中有些是未知,有些是已知 然后我们求目标矩阵就是把能确定的元素的值求出来,实在不能确定的就置为-1 所有矩阵元素的值 ...
- 转载php在IIS中运行
在IIS中配置PHP运行环境简单步骤 安装 IIS 7.0 打开 Control Panel\Programs\Programs and Features\Turn Windows features ...
- IE浏览器在虚拟机中无法正常显示字符
今天在win7虚拟机中安装IE10之后,打开任何页面都无法显示文字,自己找了一些资料也没有什么发现,虚拟机其他的程序都能很好的工作,只有IE10有问题,于是初步怀疑是IE10设置的问题.于是打开IE的 ...
- if condition volist
<table class="table table-hover table-striped"> <if condition="$order_list e ...
- C# 数据类型详解
在asp.net中C#数据类型包括有值类型.简单类型.整型.布尔型.字符型.浮点型.结构类型等等,有需要学习的朋友可进入参考参考. 4.1 值类型 各种值类型总是含有相应该类型的一个值.C#迫使你初始 ...
- Dubbo远程调用服务框架原理与示例
Dubbo 是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成. 主要核心部件: Remoting: 网络通信框架 ...
- inpuy type=date
http://www.w3schools.com/html/html_form_input_types.asp http://caniuse.com/#feat=input-datetime 浏览器兼 ...
- PHP发送邮件。
第三方类库: ①.email.class.php. ②.phpmailer:https://github.com/PHPMailer/PHPMailer. PHPMailer发送邮件”SMTP 错误: ...