我无法让页面从 cookie 加载用户角色

I can't get the pages to load user roles from cookies

我已经将以前在 iis7 上经过广告验证的网站迁移到了 iis 8.5。在此过程中,我尝试切换到表单身份验证。并非网站上的每个页面都是 aspx 页面 - 那里有静态内容和文件,因为它是一个内部网站 - 但基本表单身份验证有效,因为我将应用程序池设置为集成模式并设置 web.config 使用表单身份验证。我一直很高兴,如果你尝试做很多事情,比如加载 pdf,它会确保你先登录到 cas 服务器。

我的问题是角色 - 我已经在登录页面中设置了 cookie 以在其中包含角色(我从 CAS 属性中提取)并且当我在该页面上执行 User.IsInRole() 时它说是的,这个人有这个角色。但是如果我移动到一个新页面,aspx 与否,这个人仍然通过身份验证但没有角色。我试图避免使用角色管理器,因为我不想与提供者打交道(cas 服务器正在处理身份验证部分),但如果必须的话,我会使用角色管理器。到目前为止,我运气不好。我相信角色不是从 cookie 加载的,因为我放了相同的 (User.IsInRole("staff"))?"True":"False";主页中的行如下所示,但在登录页面上它 returns true 而在主页上它 returns false。

我将附加我的网络配置和登录页面代码的相关部分。

Web配置(仅announce文件夹使用角色atm):

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <modules>
      <!-- Re-add auth modules (in their original order) to run for all static and dynamic requests -->
      <remove name="FormsAuthentication" />
      <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
      <remove name="DefaultAuthentication" />
      <add name="DefaultAuthentication" type="System.Web.Security.DefaultAuthenticationModule" />
      <remove name="RoleManager" />
      <add name="RoleManager" type="System.Web.Security.RoleManagerModule" />
      <remove name="UrlAuthorization" />
      <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
    </modules>
  </system.webServer>
  <system.web>
    <authentication mode="Forms" />
    <authorization>
      <deny users="?" />
      <allow users="*" />
    </authorization>
  </system.web>
  <location path="announce">
    <system.web>
      <authorization>
        <allow roles="staff, faculty" />
        <deny users="*" />
      </authorization>
    </system.web>
  </location>
</configuration>

后面登录页面代码的相关部分(重定向被注释掉所以我可以看到它们的作用):

  // Second time (back from CAS) there is a ticket= to validate
  // CAS 3 doesn't list attributes through /serviceValidate, so lets build a SAML request and use that instead.
  string SAMLstr = "<?xml version='1.0'?><SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
    "<SOAP-ENV:Header/><SOAP-ENV:Body><samlp:Request xmlns:samlp=\"urn:oasis:names:tc:SAML:1.0:protocol\" " +
    "MajorVersion=\"1\" MinorVersion=\"1\" RequestID=\"_" + Request.ServerVariables["REMOTE_HOST"] + "." + DateTime.Now.Ticks + "\"" +
    "IssueInstant=\"" + DateTime.Now + "\"><samlp:AssertionArtifact>" + tkt + "</samlp:AssertionArtifact>" +
    "</samlp:Request></SOAP-ENV:Body></SOAP-ENV:Envelope>";
  string validateurl = CASHOST + "samlValidate?TARGET=" + service + "&ticket=" + tkt;
  // Set up an xml document to catch the SAML responce and then parse it.
  XmlDocument xmlDoc = new XmlDocument();
  xmlDoc.LoadXml(new WebClient().UploadString(validateurl, SAMLstr));
  // The response uses multiple namespaces, whee!
  var nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
  nsmgr.AddNamespace("s", "http://schemas.xmlsoap.org/soap/envelope/");
  nsmgr.AddNamespace("nsR", "urn:oasis:names:tc:SAML:1.0:protocol");
  nsmgr.AddNamespace("nsA", "urn:oasis:names:tc:SAML:1.0:assertion");
  // Let's see if they authed successfully:
  if (xmlDoc.SelectSingleNode("/s:Envelope/s:Body/nsR:Response/nsR:Status/nsR:StatusCode", nsmgr).Attributes["Value"].Value == ("samlp:Success")) {
    // They authenticated successfully; populate their id and roles.
    string netid = xmlDoc.SelectSingleNode("/s:Envelope/s:Body/nsR:Response/nsA:Assertion/nsA:AuthenticationStatement/nsA:Subject/nsA:NameIdentifier", nsmgr).InnerText; 
    // Grab the "memberOf" attriubute - it contains a child node for each group distinguished name.
    XmlNode memberOf = xmlDoc.SelectSingleNode("/s:Envelope/s:Body/nsR:Response/nsA:Assertion/nsA:AttributeStatement/nsA:Attribute[@AttributeName='memberOf']", nsmgr);
    // I don't want to iterate through that, so I'll just grab all the text and split it with a regex to split up the OUs and DCs.
    string[] groups = Regex.Split(memberOf.InnerText.ToLower().Substring(3), "cn=(.*?),ou=");
    HttpContext.Current.User = new GenericPrincipal(new GenericIdentity(netid),new string[] {"staff", "union", "UTech"});

    // Splicing together the groups names but droping the OUs (every other entry)
    string roles=groups[1];
    for (int x=3;x<groups.Length;x+=2) roles+="|"+groups[x];
    // Let's bake cookies with the netid, roles, and persistence.  1: Make the ticket.
    FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, netid, DateTime.Now, DateTime.Now.AddMinutes(2880), isPersistent, roles, FormsAuthentication.FormsCookiePath);
    // 2. Encrypt the ticket.
    string hash = FormsAuthentication.Encrypt(ticket);
    // 3. Turn the encrypted ticket into a cookie!
    HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, hash);
    // 4. Set expiration on the cookie to match the ticket.
    if (ticket.IsPersistent) cookie.Expires = ticket.Expiration;
    // 5. Put the cookie in the jar.
    Response.Cookies.Add(cookie);
    // We're done!  Send them home.
    // [TEMP, next 3 lines are debug] Response.Redirect(service + Session["backto"], true);
    Label1.Text = "Welcome, "+netid+"!";
    Label2.Text = roles;
    Label3.Text = (User.IsInRole("staff"))?"True":"False";

我不想回答我自己的问题,但经过一周的搜索和阅读教程后,我解决了这个问题。

上述问题中的cookies和forms认证没问题;未加载角色的原因是因为我的 global.asax 中没有 PostAuthenticateRequest(实际上根本没有 global.asax)。 This question was most helpful, not from the accepted answer but by an appended item by P.Hoxha. Which had 0 voted but I upvoted. A Microsoft tutorial here 也有助于更正我赞成的回复中无效的内容。

添加 global.asax 通过捕获 PostAuthenticateRequest 和 运行 下面的代码加载角色解决了问题:

<%@ Application Language="C#" %>
<%@ Import Namespace="System.Security.Principal" %>
<%@ Import Namespace="System.Threading" %>

<script runat="server">
void Application_OnPostAuthenticateRequest(object sender, EventArgs e) {
  HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
  if (authCookie == null || authCookie.Value == "") return;
  FormsAuthenticationTicket authTicket;
  try {
    authTicket = FormsAuthentication.Decrypt(authCookie.Value);
  } catch {
    return;
  }

  // retrieve roles from UserData
  string[] roles = authTicket.UserData.Split('|');

  if (Context.User != null) Context.User = new GenericPrincipal(Context.User.Identity, roles);
}
</script>