none
将用户登录验证等单独做个项目,发布到某个虚拟目录下,其他项目的页面需要登录时,都去调用该登录界面,并返回登录前界面;登录一次后,其他项目就不需要调用了,怎样实现呢? RRS feed

  • 问题

  • 将用户登录验证等单独做个项目,发布到某个虚拟目录下,其他项目的页面需要登录时,都去调用该登录界面,并返回登录前界面;登录一次后,其他项目就不需要调用了,怎样实现呢?
    2011年3月1日 3:22

答案

  • 您好,下面提供的是同一个域名内的方法。

    首先需要保证cookie能在不同的项目中访问,需要配置machinekey。效果类似:

    <machineKey 
    validationKey="21F090935F6E49C2C797F69BBAAD8402ABD2EE0B667A8B44EA7DD4374267A75D7
    AD972A119482D15A4127461DB1DC347C1A63AE5F1CCFAACFF1B72A7F0A281B"
    decryptionKey="ABAA84D7EC4BB56D75D217CECFFB9628809BDB8BF91CFCD64568A145BE59719F"
    validation="SHA1"
    decryption="AES"
    />

    具体参考:http://msdn.microsoft.com/zh-cn/library/ms998288.aspx

                 http://msdn.microsoft.com/zh-cn/library/w8h3skw9.aspx

    然后,使用一致的身份验证:

    在Global中类似:

    void Application_AuthenticateRequest(Object sender, EventArgs e)
            {

                string cookieName = FormsAuthentication.FormsCookieName;
                HttpCookie authCookie = Context.Request.Cookies[cookieName];

                if (null == authCookie)
                {
                    // There is no authentication cookie.
                    return;
                }

                FormsAuthenticationTicket authTicket = null;
                try
                {
                    authTicket = FormsAuthentication.Decrypt(authCookie.Value);
                }
                catch (Exception ex)
                {
                    // Log exception details (omitted for simplicity)
                    throw ex;
                    // return;
                }

                if (null == authTicket)
                {
                    // Cookie failed to decrypt.
                    return;
                }

                // When the ticket was created, the UserData property was assigned a
                // pipe delimited string of role names.
                string[] roles = authTicket.UserData.Split(new char[] { '|' });

                // Create an Identity object
                FormsIdentity id = new FormsIdentity(authTicket);

                // This principal will flow throughout the request.
                System.Security.Principal.GenericPrincipal principal = new System.Security.Principal.GenericPrincipal(id,roles);
                // Attach the new principal object to the current HttpContext object
                Context.User = principal;
            }

    • 已标记为答案 ahking 2011年3月4日 0:00
    2011年3月1日 3:44

全部回复

  • 您好,下面提供的是同一个域名内的方法。

    首先需要保证cookie能在不同的项目中访问,需要配置machinekey。效果类似:

    <machineKey 
    validationKey="21F090935F6E49C2C797F69BBAAD8402ABD2EE0B667A8B44EA7DD4374267A75D7
    AD972A119482D15A4127461DB1DC347C1A63AE5F1CCFAACFF1B72A7F0A281B"
    decryptionKey="ABAA84D7EC4BB56D75D217CECFFB9628809BDB8BF91CFCD64568A145BE59719F"
    validation="SHA1"
    decryption="AES"
    />

    具体参考:http://msdn.microsoft.com/zh-cn/library/ms998288.aspx

                 http://msdn.microsoft.com/zh-cn/library/w8h3skw9.aspx

    然后,使用一致的身份验证:

    在Global中类似:

    void Application_AuthenticateRequest(Object sender, EventArgs e)
            {

                string cookieName = FormsAuthentication.FormsCookieName;
                HttpCookie authCookie = Context.Request.Cookies[cookieName];

                if (null == authCookie)
                {
                    // There is no authentication cookie.
                    return;
                }

                FormsAuthenticationTicket authTicket = null;
                try
                {
                    authTicket = FormsAuthentication.Decrypt(authCookie.Value);
                }
                catch (Exception ex)
                {
                    // Log exception details (omitted for simplicity)
                    throw ex;
                    // return;
                }

                if (null == authTicket)
                {
                    // Cookie failed to decrypt.
                    return;
                }

                // When the ticket was created, the UserData property was assigned a
                // pipe delimited string of role names.
                string[] roles = authTicket.UserData.Split(new char[] { '|' });

                // Create an Identity object
                FormsIdentity id = new FormsIdentity(authTicket);

                // This principal will flow throughout the request.
                System.Security.Principal.GenericPrincipal principal = new System.Security.Principal.GenericPrincipal(id,roles);
                // Attach the new principal object to the current HttpContext object
                Context.User = principal;
            }

    • 已标记为答案 ahking 2011年3月4日 0:00
    2011年3月1日 3:44
  • 现在做的项目都是发布到内部网, 没有域名,地址都是类似http://10.0.0.12/account的,这样的用上面的方法可以跨地址么?

    比如http://10.0.0.17/myapp调用http://10.0.0.12/account下的登录进行验证

    2011年3月1日 5:46
  • 有些项目不要求先登录,只有操作特定的页面才要求登录,设置Global恐怕不适用吧
    2011年3月1日 6:46
  • 现在做的项目都是发布到内部网, 没有域名,地址都是类似http://10.0.0.12/account 的,这样的用上面的方法可以跨地址么?

    比如http://10.0.0.17/myapp 调用http://10.0.0.12/account 下的登录进行验证


    这种情况我没试过,我看到跨域的读取都是将cookie改为字符串,然后再次验证一下。或者您可以尝试统一设置cookie的domain。

     

    2011年3月1日 9:46
  • 有些项目不要求先登录,只有操作特定的页面才要求登录,设置Global恐怕不适用吧


    Global的Application_AuthenticateRequest是统一验证的入口,如果不在这,那么将要在每个页面上去设置,那将很烦琐。对于无需验证的,我上面的代码中也判断了,不用担心。

    像这一段:if (null == authCookie)
                {
                    // There is no authentication cookie.
                    return;
                }

    2011年3月1日 9:48
  • 有些项目不要求先登录,只有操作特定的页面才要求登录,设置Global恐怕不适用吧


    Global的Application_AuthenticateRequest是统一验证的入口,如果不在这,那么将要在每个页面上去设置,那将很烦琐。对于无需验证的,我上面的代码中也判断了,不用担心。

    像这一段:if (null == authCookie)
                {
                    // There is no authentication cookie.
                    return;
                }


    我在web.config中的<location path="" >中设定需要登录的界面也可以吧?

    我参考http://msdn.microsoft.com/zh-cn/library/eb0zx8fc.aspx对machineKey进行了配置,有一次出现了错误:

     验证视图状态 MAC 失败。如果此应用程序由网络场或群集承载,请确保 <machineKey> 配置指定了相同的 validationKey 和验证算法。不能在群集中使用 AutoGenerate。 
    说明: 执行当前 Web 请求期间,出现未处理的异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息。 

    虽然只出现了一次,但是我仍然很担心将来部署到生产环境会出现不稳定的情况

    2011年3月2日 0:48
  • machineKey里的validationKey和decryptionKey要再计算。这里有详细的解释,我上面已贴出,http://msdn.microsoft.com/zh-cn/library/ms998288.aspx 或参考<asp.net3.5揭秘>中关于membership的章节。
    2011年3月2日 3:47
  • machineKey里的validationKey和decryptionKey要再计算。这里有详细的解释,我上面已贴出,http://msdn.microsoft.com/zh-cn/library/ms998288.aspx 或参考<asp.net3.5揭秘>中关于membership的章节。

    我按照这篇文章http://msdn.microsoft.com/zh-cn/library/eb0zx8fc.aspx,直接把machineKey里的validationKey和decryptionKey值复制下来了,这样不行么?
    2011年3月2日 8:44
  • machineKey里的validationKey和decryptionKey要再计算。这里有详细的解释,我上面已贴出,http://msdn.microsoft.com/zh-cn/library/ms998288.aspx 或参考<asp.net3.5揭秘>中关于membership的章节。

    我按照这篇文章http://msdn.microsoft.com/zh-cn/library/eb0zx8fc.aspx ,直接把machineKey里的validationKey和decryptionKey值复制下来了,这样不行么?

    按理是可以。可您用公开的key,别人就能解密,留下一个安全隐患。最好生成machineKey。
    2011年3月2日 10:25
  • 有什么工具可以生成一个么?
    2011年3月2日 12:23
  • 另外,我搜到别人也遇到过类似的问题,不知以下解决方法是否可行

     

    如果你使用了一些比较复杂的控件(asp.net控件方式的HTML编辑器并且包含了一篇复杂文章就经常如此),看看你的页面上的ViewState是不是很大。如果很大,可以使用我下面的代码放到你的页面中:

    C# code
    using System; using System.IO; using System.Web.UI; public class XVPage : Page { static private DirectoryInfo _Dir; private DirectoryInfo Dir { get { if (_Dir == null) { _Dir = new DirectoryInfo(Server.MapPath("~/App_Data/")); if (!_Dir.Exists) _Dir.Create(); _Dir = new DirectoryInfo(Path.Combine(_Dir.FullName, "ViewState")); if (!_Dir.Exists) _Dir.Create(); } return _Dir; } } protected override object LoadPageStateFromPersistenceMedium() { PageStatePersister ps = this.PageStatePersister; ps.Load(); if (ps.ControlState != null) ps.ControlState = 反序列化对象((string)ps.ControlState); if (ps.ViewState != null) ps.ViewState = 反序列化对象((string)ps.ViewState); return new Pair(ps.ControlState, ps.ViewState); } protected override void SavePageStateToPersistenceMedium(object state) { PageStatePersister ps = this.PageStatePersister; if (state is Pair) { Pair pair = (Pair)state; ps.ControlState = pair.First; ps.ViewState = pair.Second; } else { ps.ViewState = state; } if (ps.ControlState != null) ps.ControlState = 序列化对象(ps.ControlState); if (ps.ViewState != null) ps.ViewState = 序列化对象(ps.ViewState); ps.Save(); } private object 反序列化对象(string stateID) { string stateStr = (string)Cache[stateID]; string file = Path.Combine(Dir.FullName, stateID); if (stateStr == null) stateStr = File.ReadAllText(file); else Cache.Remove(stateID); return new ObjectStateFormatter().Deserialize(stateStr); } private string 序列化对象(object obj) { string value = new ObjectStateFormatter().Serialize(obj); string stateID = (DateTime.Now.Ticks + (long)value.GetHashCode()).ToString(); //产生离散的id号码 File.WriteAllText(Path.Combine(Dir.FullName, stateID), value); Cache.Insert(stateID, value); return stateID; } protected override void OnUnload(EventArgs e) { base.OnUnload(e); DateTime dt = DateTime.Now.AddMinutes(-20); foreach (FileInfo fl in Dir.GetFiles()) if (fl.LastAccessTime < dt) try { fl.Delete(); } catch { } } }


    你可以把它作为你的页面的父页面。
    2011年3月2日 12:24
  • 哈,您在一个问题里,索要的也太多了吧。

    关于工具,上面我贴的连接里有c#的例子,您是否有看呢?

    您最后贴的这段代码是关于降低web form的viewstate的,跟我们讨论的身份验证完全两码事!八杆子打不着的。

    2011年3月2日 12:36
  • 哈,您在一个问题里,索要的也太多了吧。

    关于工具,上面我贴的连接里有c#的例子,您是否有看呢?

    您最后贴的这段代码是关于降低web form的viewstate的,跟我们讨论的身份验证完全两码事!八杆子打不着的。


    呵呵,问题都是关联的,上面那段代码讲的就是由于viewstate太大而导致验证视图状态 MAC 失败的
    2011年3月2日 23:57
  • 哈,您在一个问题里,索要的也太多了吧。

    关于工具,上面我贴的连接里有c#的例子,您是否有看呢?

    您最后贴的这段代码是关于降低web form的viewstate的,跟我们讨论的身份验证完全两码事!八杆子打不着的。


    呵呵,问题都是关联的,上面那段代码讲的就是由于viewstate太大而导致验证视图状态 MAC 失败的


    好,不错!

    实战中如果安全要求不高,也可以尝试将EnableViewStateMac设为false。

    另我们明确的设置了machinekey。我不明白这加密解密跟viewstate的大小有什么关系?

    您现在刚好有环境,能否往viewstate中放一个很多行记录的datatable,然后测试一下,期待您的分享!

    2011年3月4日 1:29