我无法让页面从 cookie 加载用户角色
Posted
技术标签:
【中文标题】我无法让页面从 cookie 加载用户角色【英文标题】:I can't get the pages to load user roles from cookies 【发布时间】:2017-05-20 07:14:29 【问题描述】:我在 iis7 上采用了曾经是经过广告验证的网站并将其迁移到 iis 8.5。在此过程中,我尝试切换到表单身份验证。并非站点上的每个页面都是 aspx 页面 - 那里有静态内容和文件,因为它是一个 Intranet 站点 - 但基本表单身份验证有效,因为我将应用程序池设置为集成模式并将 web.config 设置为使用表单身份验证。我很高兴如果您尝试加载 pdf 文件,它会确保您首先登录到 cas 服务器。
我的问题在于角色 - 我已经在登录页面中设置了 cookie 以在其中包含角色(我从 CAS 属性中提取),当我在该页面上执行 User.IsInRole() 时,它说是,人有角色。但是,如果我移动到一个新页面,不管是否是 aspx,这个人仍然是经过身份验证的,但没有角色。我试图避免使用角色管理器,因为我不想与提供者打交道(cas 服务器正在处理身份验证部分),但如果必须,我将使用角色管理器。到目前为止,我运气不好。我相信角色没有从 cookie 中加载,因为我把相同的 (User.IsInRole("staff"))?"True":"False";如下所示的主页中的行,但在登录页面上它返回true,在主页上它返回false。
我将附加我的网络配置和登录页面代码的相关部分。
Web 配置(仅通告文件夹使用角色 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";
【问题讨论】:
【参考方案1】:我不想回答自己的问题,但经过一周的搜索和阅读教程后,我解决了问题。
上述问题中的cookies和forms认证没问题;未加载角色的原因是因为我的 global.asax 中没有 PostAuthenticateRequest (实际上根本没有 global.asax)。 This question 最有帮助,不是来自接受的答案,而是来自 P.Hoxha 的附加项目。其中有 0 人投了票,但我投了赞成票。 Microsoft 教程 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>
【讨论】:
以上是关于我无法让页面从 cookie 加载用户角色的主要内容,如果未能解决你的问题,请参考以下文章
如果页面仅加载一次,如何在 SPA 中获取 Laravel cookie