我无法让页面从 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>
我已经将以前在 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>