Sharepoint 2013中的联合身份validation:获取rtFa和FedAuth cookie

场景如下:我需要对用户(使用他的大学帐户)执行联盟身份validation到他大学的Sharepoint站点,并获得FedAuth和rtFa cookie (我必须将其传递给SharePoint REST webservices)为了访问资源)。

我做了一些尝试,但每个问题至少有一个问题:

1)使用Microsoft.SharePoint.Client库

ClientContext context = new ClientContext(host); SharePointOnlineCredentials creds = new SharePointOnlineCredentials(user, passw); context.Credentials = creds; Uri sharepointuri = new Uri(host); string authCookie = creds.GetAuthenticationCookie(sharepointuri); Web web = context.Web; context.Load(web, w=>w.Lists); context.ExecuteQuery(); fedAuthString = authCookie.Replace("SPOIDCRL=", string.Empty); 

这样我设法获得FedAuth cookie,但我无法获得rtFa cookie

我怎么能在这一点上获得rtFa cookie? 我是否可以拦截此类操作中涉及的HTTP请求(即context.ExecuteQuery()) – 其中可能包含标题中的rtFa cookie? 或者,我是否可以通过仅利用FedAuth cookie获得rtFa cookie?

2)使用MsOnlineClaimsHelper

这是一个帮助类,可以在Internet上找到(例如,这里http://blog.kloud.com.au/tag/msonlineclaimshelper/ )。

该类实际上与普通身份validation一起使用,但在联合身份validation时失败

所以我调整它以使它在这种情况下工作。 只要我理解,步骤如下:

  1. 使用用户名和密码对大学的STS ADFS服务(“联合方”或ISSUER)进行身份validation – 此处依赖方为Sharepoint O365 STS(“ https://login.microsoftonline.com/extSTS.srf ”)
  2. 如果auth成功,我会收到包含声明和安全令牌的SAML断言
  3. 现在,我通过传递安全令牌对SharePoint站点进行身份validation
  4. 如果令牌被识别,我会收到包含两个cookie的响应(FedAuth和rtFa)

我不是这方面的专家,我提出以下代码:

这是调用上述方法并尝试通过两个步骤从凭证中获取FedAuth和rtFa的代码(步骤1:从联合方获取SAML令牌;步骤2:将令牌从联合方传递到Sharepoint):

  private List GetCookies(){ // 1: GET SAML XML FROM FEDERATED PARTY THE USER BELONGS TO string samlToken = getResponse_Federation(sts: "https://sts.FEDERATEDDOMAIN.com/adfs/services/trust/13/usernamemixed/", realm: "https://login.microsoftonline.com/extSTS.srf"); // 2: PARSE THE SAML ASSERTION INTO A TOKEN var handlers = FederatedAuthentication.ServiceConfiguration.SecurityTokenHandlers; SecurityToken token = handlers.ReadToken(new XmlTextReader(new StringReader(samlToken ))); // 3: REQUEST A NEW TOKEN BASED ON THE ISSUED TOKEN GenericXmlSecurityToken secToken = GetO365BinaryTokenFromToken(token); // 4: NOW, EASY: I PARSE THE TOKEN AND EXTRACT FEDAUTH and RTFA ............... } private string getResponse_Federation(string stsUrl, string relyingPartyAddress) { var binding = new Microsoft.IdentityModel.Protocols.WSTrust.Bindings.UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential); binding.ClientCredentialType = HttpClientCredentialType.None; var factory = new WSTrustChannelFactory(binding, stsUrl); factory.Credentials.UserName.UserName = "username"; factory.Credentials.UserName.Password = "password"; factory.Credentials.SupportInteractive = false; factory.TrustVersion = TrustVersion.WSTrust13; IWSTrustChannelContract channel = null; try { var rst = new RequestSecurityToken { RequestType = WSTrust13Constants.RequestTypes.Issue, AppliesTo = new EndpointAddress(relyingPartyAddress), //("urn:sharepoint:MYFEDERATEDPARTY"), ReplyTo = relyingPartyAddress, KeyType = WSTrust13Constants.KeyTypes.Bearer, TokenType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0", RequestDisplayToken = true, }; channel = (WSTrustChannel)factory.CreateChannel(); RequestSecurityTokenResponse response = null; SecurityToken st = channel.Issue(rst, out response); var genericToken = st as GenericXmlSecurityToken; return genericToken.TokenXml.OuterXml; } catch (Exception e) { return null; } } private GenericXmlSecurityToken GetO365BinaryTokenFromToken(SecurityToken issuedToken) { Uri u = new Uri("https://login.microsoftonline.com/extSTS.srf"); WSHttpBinding binding = new WSHttpBinding(SecurityMode.TransportWithMessageCredential); binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None; binding.Security.Mode = SecurityMode.TransportWithMessageCredential; binding.Security.Message.ClientCredentialType = MessageCredentialType.IssuedToken; Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannelFactory channel = new Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannelFactory( binding, new EndpointAddress("https://login.microsoftonline.com/extSTS.srf")); channel.TrustVersion = TrustVersion.WSTrust13; channel.Credentials.SupportInteractive = false; GenericXmlSecurityToken token = null; try { RequestSecurityToken rst = new RequestSecurityToken(WSTrust13Constants.RequestTypes.Issue, WSTrust13Constants.KeyTypes.Bearer) { }; rst.AppliesTo = new EndpointAddress("urn:sharepoint:MYFEDERATEDPARTY"); channel.ConfigureChannelFactory(); var chan = (Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannel)channel.CreateChannelWithIssuedToken(issuedToken); RequestSecurityTokenResponse rstr = null; token = chan.Issue(rst, out rstr) as GenericXmlSecurityToken; return token; } catch (Exception ex){ Trace.TraceWarning("WebException in getO365BinaryTokenFromADFS: " + ex.ToString()); throw; } } 

我设法从大学STS获得了一个SAML令牌。 但是,在解析时,生成的SecurityToken没有安全密钥(即,SecurityKeys集合为空)

没有密钥,我得到GetO365BinaryTokenFromToken(),但当我尝试将令牌发送到SharePoint身份validation服务时 – 我收到以下错误:“签名令牌Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken没有密钥。安全性token在需要它执行加密操作的上下文中使用,但令牌不包含加密密钥。令牌类型不支持加密操作,或者特定令牌实例不包含加密密钥。检查配置以确保加密在需要加密操作的上下文中未指定禁用的令牌类型(例如,UserNameSecurityToken)(例如,支持支持令牌)。

我认为还有一些我无法直接控制的配置问题,双方(大学STS ADFS和Sharepoint STS)。

我希望更多的专家能够在这个过程中提供清晰度,甚至提供建议,以实际使这个场景有效。

文件下载function

通过以下function,我可以通过发出FedAuth和rtFa cookie来下载文件(给定一个URL,例如https://myfederatedparty.sharepoint.com/sites/MYSITE/path/myfile.pdf )。 如果我没有通过rtFa cookie,我会收到“未经授权”的回复。

  public static async Task TryRawWsCall(String url, string fedauth, string rtfa, CancellationToken ct, TimeSpan? timeout = null) { try { HttpClientHandler handler = new HttpClientHandler(); handler.CookieContainer = new System.Net.CookieContainer(); CookieCollection cc = new CookieCollection(); cc.Add(new Cookie("FedAuth", fedauth)); cc.Add(new Cookie("rtFa", rtfa)); handler.CookieContainer.Add(new Uri(url), cc); HttpClient _client = new HttpClient(handler); if (timeout.HasValue) _client.Timeout = timeout.Value; ct.ThrowIfCancellationRequested(); var resp = await _client.GetAsync(url); var result = await resp.Content.ReadAsByteArrayAsync(); if (!resp.IsSuccessStatusCode) return null; return result; } catch (Exception) { return null; } } 

事实上,在SharePoint Online / Office 365身份validation方面,只有FedAuth cookie是必需的。

根据SharePoint Online中的远程身份validation使用基于声明的身份validation :

FedAuth cookie启用联合授权, rtFA cookie允许从所有SharePoint站点注销用户,即使注销过程从非SharePoint站点启动也是如此。

因此,为了在SharePoint Online / Office 365中执行身份validation,提供SPOIDCRL HTTP标头就足够了,例如:

 var request = (HttpWebRequest)WebRequest.Create(endpointUri); var credentials = new SharePointOnlineCredentials(userName,securePassword); var authCookie = credentials.GetAuthenticationCookie(webUri); request.Headers.Add(HttpRequestHeader.Cookie, authCookie); 

以下示例演示如何通过提供FedAuth cookie在SharePointOnline / Office 365中执行活动身份validation。

示例1:通过SharePoint 2013 REST API检索FormDigest( MsOnlineClaimsHelper class

 public static string GetFormDigest(Uri webUri, string userName, string password) { var claimsHelper = new MsOnlineClaimsHelper(webUri, userName, password); var endpointUri = new Uri(webUri,"/_api/contextinfo"); var request = (HttpWebRequest)WebRequest.Create(endpointUri); request.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f"); request.Method = WebRequestMethods.Http.Post; request.Accept = "application/json;odata=verbose"; request.ContentType = "application/json;odata=verbose"; request.ContentLength = 0; var fedAuthCookie = claimsHelper.CookieContainer.GetCookieHeader(webUri); //FedAuth are getting here request.Headers.Add(HttpRequestHeader.Cookie, fedAuthCookie); //only FedAuth cookie are provided here //request.CookieContainer = claimsHelper.CookieContainer; using (var response = (HttpWebResponse) request.GetResponse()) { using (var streamReader = new StreamReader(response.GetResponseStream())) { var content = streamReader.ReadToEnd(); var t = JToken.Parse(content); return t["d"]["GetContextWebInformation"]["FormDigestValue"].ToString(); } } } 

示例2:通过SharePoint 2013 REST API检索FormDigest(使用SharePointOnlineCredentials class

 public static string GetFormDigest(Uri webUri, string userName, string password) { var endpointUri = new Uri(webUri, "/_api/contextinfo"); var request = (HttpWebRequest)WebRequest.Create(endpointUri); request.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f"); request.Method = WebRequestMethods.Http.Post; request.Accept = "application/json;odata=verbose"; request.ContentType = "application/json;odata=verbose"; request.ContentLength = 0; var securePassword = new SecureString(); foreach (char c in password) { securePassword.AppendChar(c); } request.Credentials = new SharePointOnlineCredentials(userName,securePassword); using (var response = (HttpWebResponse)request.GetResponse()) { using (var streamReader = new StreamReader(response.GetResponseStream())) { var content = streamReader.ReadToEnd(); var t = JToken.Parse(content); return t["d"]["GetContextWebInformation"]["FormDigestValue"].ToString(); } } } 

更新

用于下载文件的示例的修改版本:

 public static async Task DownloadFile(Uri webUri,string userName,string password, string relativeFileUrl, CancellationToken ct, TimeSpan? timeout = null) { try { var securePassword = new SecureString(); foreach (var c in password) { securePassword.AppendChar(c); } var credentials = new SharePointOnlineCredentials(userName, securePassword); var authCookie = credentials.GetAuthenticationCookie(webUri); var fedAuthString = authCookie.TrimStart("SPOIDCRL=".ToCharArray()); var cookieContainer = new CookieContainer(); cookieContainer.Add(webUri, new Cookie("SPOIDCRL", fedAuthString)); HttpClientHandler handler = new HttpClientHandler(); handler.CookieContainer = cookieContainer; HttpClient _client = new HttpClient(handler); if (timeout.HasValue) _client.Timeout = timeout.Value; ct.ThrowIfCancellationRequested(); var fileUrl = new Uri(webUri, relativeFileUrl); var resp = await _client.GetAsync(fileUrl); var result = await resp.Content.ReadAsByteArrayAsync(); if (!resp.IsSuccessStatusCode) return null; return result; } catch (Exception) { return null; } } 

我创建了一个基于https://stackoverflow.com/users/1375553/vadim-gremyachev的回答https://github.com/nddipiazza/SharepointOnlineCookieFetcher的github项目,该项目可以生成这些cookie。

它有适用于Windows,Centos7和Ubuntu16的版本,我使用mono develop来构建它,以便它与平台无关。

适用于未在c#中使用CSOM编写程序但仍希望能够轻松获取cookie的用户。

用法

一次性步骤:(请参阅访问路径“/ etc / mono / registry”被拒绝 )

 sudo mkdir /etc/mono sudo mkdir /etc/mono/registry sudo chmod uog+rw /etc/mono/registry 

运行程序:

Linux: ./SharepointOnlineSecurityUtil -u youruser@yourdomain.com -w https://tenant.sharepoint.com

Windows: SharepointOnlineSecurityUtil.exe -u youruser@yourdomain.com -w https://tenant.sharepoint.com

提示时输入密码

stdout的结果将具有SPOIDCRL cookie。