- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试为 Office 365 构建一个 Multi-Tenancy 应用程序,该应用程序专注于 SharePoint Online 并使用 OAuth2 通过 Azure 进行身份验证。该问题特定于通过 Azure 登录进行 SharePoint 访问,但仅在使用此 API 通过 OAuth2 进行身份验证时才会发现。
在 Azure 和 Office 中正确注册应用程序和设置用户的许多机制虽然有些复杂,但只要投入适当的时间就可以克服。
即使是 Azure 的基本 OAuth2 协议(protocol)使用也相对顺利。但是,由于 SharePoint 的“资源”参数,我无法使我的应用程序真正成为 Multi-Tenancy 。这显然要求我的应用程序在最终用户完成登录序列之前知道他们的根 SharePoint 站点 URL。我不明白这怎么可能。有人请给我指出正确的方向。
以下是实际登录序列的示例:
GET /common/oauth2/authorize?client_id=5cb5e93b-57f5-4e09-97c5-e0d20661c59a
&redirect_uri=https://myappdomain.com/v1/oauth2_redirect/
&response_type=code&prompt=login&state=D79E5777 HTTP/1.1
Host: login.windows.net
Cache-Control: no-cache
当用户进行身份验证时,这会导致调用重定向,如下所示:
https://myappdomain.com/v1/oauth2_redirect/?code=AAABAAAAvPM1KaPlrEq...{blah*3}
到目前为止非常棒!三足身份验证的下一步是 POST 返回到/token 端点,以获取要在所有后续 REST 调用中使用的实际承载 token 。这只是经典的 OAuth2...
POST /common/oauth2/token HTTP/1.1
Host: login.windows.net
Accept: text/json
Cache-Control: no-cache
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="grant_type"
authorization_code
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="code"
AAABAAAAvPM1KaPlrEq...{blah*3}
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="client_id"
5cb5e93b-57f5-4e09-97c5-e0d20661c59a
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="client_secret"
02{my little secret}I=
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="redirect_uri"
https://myappdomain.com/v1/oauth2_redirect/
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="resource"
https://contoso.sharepoint.com/
----WebKitFormBoundaryE19zNvXGzXaLvS5C
这就是它变得棘手的地方。 “resource”参数是必需的,并且必须指向您要访问的特定于用户的端点。对于 Exchange 或 Azure,端点始终相同。 (https://graph.windows.net
或 https://outlook.office365.com
)但是 SharePoint 对于每个租户都有不同的端点。您实际上还没有让用户登录,但您已经需要有关您尚未拥有的用户的信息..
如果我部署的应用程序版本假定“contoso”作为租户名称(如上所述),则只有 contoso 租户中的用户才能成功使用我的应用程序读取 SharePoint 数据。一旦 fabrikam 中的另一个用户尝试使用它,我对 /token
端点的 POST
就会请求访问错误站点的权限...这就是问题所在。
如何在用户实际登录之前检测到 POST
到 /token
端点的正确端点?是否有一些我可以使用的隐藏信息?是否可以通过某种发现来检测租户的根 SharePoint URL?或者更好的是,是否有一个端点可以作为资源来传递,该资源可以代表租户的家(例如https://office.microsoft.com/sharepoint
) >)?然后可以从返回的 user_id JWT token 中收集它。这与其他服务类似,并且对于客户来说管理起来非常简单。不过,我没有看到这一点。
如果没有这些问题的明确答案或这些问题的解决方法,我不得不猜测不可能编写一个在 SharePoint Online O365 中进行身份验证的 Multi-Tenancy 应用程序...这似乎不对。请有人帮忙!
最佳答案
我想为上面评论中简要提到的解决方案添加详细信息 - 这对于在 Office 365 中开发 Multi-Tenancy 应用程序的任何人都非常重要,特别是如果该应用程序将访问包括 OneDrive 在内的 SharePoint 网站。
从 OAuth 2.0 的角度来看,这里的过程有点不标准,但在 Multi-Tenancy 世界中有意义。关键是重新使用从 Azure 返回的第一个代码。在这里关注我:
首先,我们遵循标准 OAuth 身份验证步骤:
GET /common/oauth2/authorize?client_id=5cb5e93b-57f5-4e09-97c5-e0d20661c59a
&redirect_uri=https://myappdomain.com/v1/oauth2_redirect/
&response_type=code&prompt=login&state=D79E5777 HTTP/1.1
Host: login.windows.net
Cache-Control: no-cache
这将重定向到用户登录的 Azure 登录页面。如果成功,Azure 将使用代码回调到您的端点:
https://myappdomain.com/v1/oauth2_redirect/?code=AAABAAAA...{ONE-CODE-To-RULE-THEM-ALL}xyz
现在我们 POST 回 /token
端点以获取要在后续 REST 调用中使用的实际承载 token 。再说一遍,这只是经典的 OAuth2...但请注意我们如何使用 /Discovery
端点作为资源 - 而不是我们实际用于收集数据的任何端点。此外,我们还要求 UserProfile.Read
范围。
POST /common/oauth2/token HTTP/1.1
Host: login.windows.net
Accept: text/json
Cache-Control: no-cache
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="grant_type"
authorization_code
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="code"
AAABAAAA...{ONE-CODE-To-RULE-THEM-ALL}xyz
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="client_id"
5cb5e93b-57f5-4e09-97c5-e0d20661c59a
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="client_secret"
02{my little secret}I=
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="redirect_uri"
https://myappdomain.com/v1/oauth2_redirect/
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="scope"
UserProfile.Read
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="resource"
https://api.office.com/discovery/
----WebKitFormBoundaryE19zNvXGzXaLvS5C
对此 POST 的响应将包含一个 access-token
,可用于对 /discovery
端点进行 REST 调用。
{
"refresh-token": "AAABsvRw-mAAWHr8XOY2lVOKZNLJ{BAR}xkSAA",
"resource": "https://api.office.com/discovery/",
"pwd_exp": "3062796",
"pwd_url": "https://portal.microsoftonline.com/ChangePassword.aspx",
"expires_in": "3599",
"access-token": "ey_0_J0eXAiOiJjsp6PpUhSjpXlm0{F00}-j0aLiFg",
"scope": "Contacts.Read",
"token-type": "Bearer",
"not_before": "1422385173",
"expires_on": "1422389073"
}
现在,使用此访问 token
,查询/Services
端点以了解Office 365中该用户还可以使用什么。
GET /discovery/v1.0/me/services HTTP/1.1
Host: api.office.com
Cache-Control: no-cache
----WebKitFormBoundaryE19zNvXGzXaLvS5D
Content-Disposition: form-data; name="Authorization"
Bearer ey_0_J0eXAiOiJjsp6PpUhSjpXlm0{F00}-j0aLiFg
----WebKitFormBoundaryE19zNvXGzXaLvS5D
结果将包括一组服务结构,描述各种端点和每个端点的功能。
{
"@odata.context": "https://api.office.com/discovery/v1.0/me/$metadata#allServices",
"value": [
{
"capability": "MyFiles",
"entityKey": "MyFiles@O365_SHAREPOINT",
"providerId": "72f988bf-86f1-41af-91ab-2d7cd011db47",
"serviceEndpointUri": "https://contoso-my.sharepoint.com/_api/v1.0/me",
"serviceId": "O365_SHAREPOINT",
"serviceName": "Office 365 SharePoint",
"serviceResourceId": "https://contoso-my.sharepoint.com/"
},
{
"capability": "RootSite",
"entityKey": "RootSite@O365_SHAREPOINT",
"providerId": "72f988bf-86f1-41af-91ab-2d7cd011db47",
"serviceEndpointUri": "https://contoso.sharepoint.com/_api",
"serviceId": "O365_SHAREPOINT",
"serviceName": "Office 365 SharePoint",
"serviceResourceId": "https://contoso.sharepoint.com/"
},
{
"capability": "Contacts",
"entityKey": "Contacts@O365_EXCHANGE",
"providerId": "72f988bf-86f1-41af-91ab-2d7cd011db47",
"serviceEndpointUri": "https://outlook.office365.com/api/v1.0",
"serviceId": "O365_EXCHANGE",
"serviceName": "Office 365 Exchange",
"serviceResourceId": "https://outlook.office365.com/"
}
]
}
现在是棘手的部分...此时,我们知道我们真正想要对其进行身份验证的端点 - 其中一些是特定于租户的。通常您会认为我们需要对每个端点重新进行 OAuth2 舞蹈。但在这种情况下,我们可以稍微作弊 - 只需 POST 我们最初从 Azure 收到的相同代码 - 使用上面相同的 HTTP 请求,仅更改 resource
和 scope
字段使用上面服务结构中的 serviceResourceId
和 capability
。像这样:
POST /common/oauth2/token HTTP/1.1
Host: login.windows.net
Accept: text/json
Cache-Control: no-cache
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="grant_type"
authorization_code
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="code"
AAABAAAA...{ONE-CODE-To-RULE-THEM-ALL}xyz
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="client_id"
5cb5e93b-57f5-4e09-97c5-e0d20661c59a
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="client_secret"
02{my little secret}I=
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="redirect_uri"
https://myappdomain.com/v1/oauth2_redirect/
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="scope"
MyFiles.Read
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="resource"
https://contoso-my.sharepoint.com/
----WebKitFormBoundaryE19zNvXGzXaLvS5C
然后对另外两个执行相同的操作:
...
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="scope"
RootSite.Read
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="resource"
https://contoso.sharepoint.com/
----WebKitFormBoundaryE19zNvXGzXaLvS5C
和
...
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="scope"
Contacts.Read
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="resource"
https://outlook.office365.com/
----WebKitFormBoundaryE19zNvXGzXaLvS5C
所有这三个调用都会产生类似于上面第一个 POST 的响应,为每个相应端点提供刷新 token 和访问 token 。所有这一切的代价只是一次用户身份验证。 :)
中提琴!谜团已解开 - 您可以为 O365 编写 Multi-Tenancy 应用程序。 :)
关于sharepoint - 为 SharePoint Online O365 构建 Multi-Tenancy 应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27303590/
在使用 requests 库中的状态代码时,我遇到了一些奇怪的事情。每个 HTTP 状态代码都有一个常量,有些具有别名(例如,包括 200 的复选标记): url = 'https://httpbin
这是我得到的代码,但我不知道这两行是什么意思: o[arr[i]] = o[arr[i]] || {}; o = o[arr[i]]; 完整代码: var GLOBAL={}; GLOBAL.name
所以这个问题的答案What is the difference between Θ(n) and O(n)? 指出“基本上,当我们说算法是 O(n) 时,它也是 O(n2)、O(n1000000)、O
这是一个快速的想法;有人会说 O(∞) 实际上是 O(1) 吗? 我的意思是它不依赖于输入大小? 所以在某种程度上它是恒定的,尽管它是无限的。 或者是唯一“正确”的表达方式 O(∞)? 最佳答案 无穷
这是真的: log(A) + log(B) = log(A * B) [0] 这也是真的吗? O(log(A)) + O(log(B)) = O(log(A * B)) [1] 据我了解 O(f
我正在解决面试练习的问题,但我似乎无法找出以下问题的时间和空间复杂度的答案: Given two sorted Linked Lists, merge them into a third list i
我了解 Big-Oh 表示法。但是我该如何解释 O(O(f(n))) 是什么意思呢?是指增长率的增长率吗? 最佳答案 x = O(n)基本上意味着 x <= kn对于一些常量 k . 因此 x = O
我正在编写一个函数,该函数需要一个对象和一个投影来了解它必须在哪个字段上工作。 我想知道是否应该使用这样的字符串: const o = { a: 'Hello There' }; funct
直觉上,我认为这三个表达式是等价的。 例如,如果一个算法在 O(nlogn) + O(n) 或 O(nlogn + n) 中运行(我很困惑),我可以假设这是一个O(nlogn) 算法? 什么是真相?
根据 O'Reilly 的 Python in a Nutshell 中的 Alex Martelli,复杂度类 O(n) + O(n) = O(n)。所以我相信。但是我很困惑。他解释说:“N 的两个
O(n^2)有什么区别和 O(n.log(n)) ? 最佳答案 n^2 的复杂性增长得更快。 关于big-o - 大 O 符号 : differences between O(n^2) and O(n
每当我收到来自 MS outlook 的电子邮件时,我都会收到此标记 & nbsp ; (没有空格)哪个显示为?在 <>. 当我将其更改为 ISO-8859-1 时,浏览器页面字符集编码为 UTF-8
我很难理解 Algorithms by S. Dasgupta, C.H. Papadimitriou, and U.V. Vazirani - page 24 中的以下陈述它们将 O(n) 的总和表
我在面试蛋糕上练习了一些问题,并在问题 2给出的解决方案使用两个单独的 for 循环(非嵌套),解决方案提供者声称他/她在 O(n) 时间内解决了它。据我了解,这将是 O(2n) 时间。是我想错了吗,
关于 Java 语法的幼稚问题。什么 T accept(ObjectVisitorEx visitor); 是什么意思? C# 的等价物是什么? 最佳答案 在 C# 中它可能是: O Accept(
假设我有一个长度为 n 的数组,我使用时间为 nlogn 的排序算法对它进行了排序。得到这个排序后的数组后,我遍历它以找到任何具有线性时间的重复元素。我的理解是,由于操作是分开发生的,所以时间是 O(
总和 O(1)+O(2)+ .... +O(n) 的计算结果是什么? 我在某处看到它的解决方案: O(n(n+1) / 2) = O(n^2) 但我对此并不满意,因为 O(1) = O(2) = co
这个问题在这里已经有了答案: 11 年前关闭。 Possible Duplicate: Plain english explanation of Big O 我想这可能是类里面教的东西,但作为一个自学
假设我有两种算法: for (int i = 0; i 2)更长的时间给定的一些n - 其中n这种情况的发生实际上取决于所涉及的算法 - 对于您的具体示例, n 2)分别时间,您可能会看到: Θ(n)
这个问题在这里已经有了答案: Example of a factorial time algorithm O( n! ) (4 个回答) 6年前关闭。 我见过表示为 O(X!) 的 big-o 示例但
我是一名优秀的程序员,十分优秀!