方案如下:我需要对用户(使用他的大学帐户)执行联盟身份验证到他大学的Sharepoint站点,并获取FedAuth和rtFa cookie (我必须传递给SharePoint REST Web服务才能访问资源。)
我做了一些尝试,但每个问题至少有一个问题:
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?
这是一个帮助类,可以在互联网上找到(例如,http://blog.kloud.com.au/tag/msonlineclaimshelper/)。
此类实际上适用于普通身份验证,但使用联合身份验证失败。
所以我调整了它以使其在这种情况下起作用。 只要我理解,步骤如下:
我不是这方面的专家,我提出了以下代码:
这是调用上述方法的代码,尝试通过两个步骤从凭证中获取FedAuth和rtFa(步骤1:从联合方获取SAML令牌;步骤2:将令牌从联合方传递到Sharepoint):
private List<string> 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身份验证服务时 - 我收到以下错误: “签名令牌Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityToken没有密钥。安全令牌用于需要它执行加密操作的上下文中,但令牌不包含加密密钥。令牌类型不支持加密操作或者特定令牌实例不包含加密密钥。检查您的配置以确保在需要加密操作的上下文中未指定加密禁用的令牌类型(例如,UserNameSecurityToken)(例如,支持支持令牌)。 / p>
我认为还存在一些我无法直接控制的配置问题,双方(大学STS ADFS和Sharepoint STS)。
我希望更多的专家能够在这个过程中提供清晰度,甚至提供建议,以实际使这个场景有效。
使用以下功能,我可以通过发出 BOTH FedAuth和rtFa cookie来下载文件(给定https://myfederatedparty.sharepoint.com/sites/MYSITE/path/myfile.pdf等URL)。如果我没有通过rtFa cookie,我会收到“未经授权”的回复。
public static async Task<byte[]> 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; }
}
答案 0 :(得分:11)
事实上,在SharePoint Online / Office 365身份验证方面,只有FedAuth
cookie是必需的。
根据Remote Authentication in SharePoint Online Using Claims-Based Authentication:
FedAuth
Cookie启用联合授权,rtFA
cookie允许从所有SharePoint站点注销用户,即使 注销过程从非SharePoint站点开始。
因此,为了在SharePoint Online / Office 365中执行身份验证,提供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中执行活动身份验证。
示例1:通过SharePoint 2013 REST API检索FormDigest(uisng 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<byte[]> 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; }
}
答案 1 :(得分:0)
我根据https://stackoverflow.com/users/1375553/vadim-gremyachev的回答创建了一个github项目https://github.com/nddipiazza/SharepointOnlineCookieFetcher 一个可以生成这些cookie的项目。
它有适用于Windows,Centos7和Ubuntu16的版本,我使用mono develop来构建它,以便它独立于平台。
适用于未在c#中使用CSOM制作程序但仍希望能够轻松获取cookie的用户。
<强>用法强>
一次性步骤:(见Access to the path "/etc/mono/registry" is denied)
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。
答案 2 :(得分:0)
my purposes我仍然需要FedAuth和rtFa cookie。我尝试仅使用FedAuth,但没有两者都无法使用。 Another developer确认他也看到了相同的行为。
注意:传统身份验证必须为enabled in your tenant才能起作用。
这里有a thread可以帮助获取FedAuth和rtFa。
https://login.microsoftonline.com/extSTS.srf
。<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> <s:Header> <a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action> <a:ReplyTo> <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address> </a:ReplyTo> <a:To s:mustUnderstand="1">https://login.microsoftonline.com/extSTS.srf</a:To> <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> <o:UsernameToken> <o:Username>[username]</o:Username> <o:Password>[password]</o:Password> </o:UsernameToken> </o:Security> </s:Header> <s:Body> <t:RequestSecurityToken xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust"> <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"> <a:EndpointReference> <a:Address>[endpoint]</a:Address> </a:EndpointReference> </wsp:AppliesTo> <t:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</t:KeyType> <t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType> <t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType> </t:RequestSecurityToken> </s:Body> </s:Envelope>
wsse:BinarySecurityToken
节点的内容。wsse:BinarySecurityToken
内容。响应头将包含FedAuth和rtFa cookie。