2014年7月22日,星期二

主机Web陷阱上的远程事件接收器–没有上下文令牌/ ClientContext


2015年8月更新-新信息!

Vesa Juvonen(微软) 与我联系时,他和SharePoint工程团队一直在对此进行一些测试,尽管我在下面讨论的“仅应用程序”变通办法的确有效,但实际上仅在某些条件下才需要。事实证明,仅当事件接收者未*使用应用程序身份验证*进行注册时(例如,在AppInstalled事件中。如果RER是使用*用户身份验证*注册的,例如使用SharePointOnlineCredentials类,您将在此处看到详细的行为-因此,您的选择要么是通过应用程序身份验证注册RER,要么使用下面代码中详细介绍的仅应用程序的方法。

换句话说,远程事件接收者最初的注册方式对代码运行产生了影响。

感谢您的信息维萨:)

I’我花了很多时间在SharePoint 2013 / Office 365中使用远程事件接收器,在早期,我遇到了一个问题,最初使我认为他们没有’根本无法正常工作。现在有了一些帮助,我意识到主要是必须以某种方式编写代码-但是它’确实有一个限制会影响您可能想做的某些事情,这也是正确的。无论如何,这是我的陷阱’我开始看到其他人受到打击,所以我认为’值得谈论。
特别是在以下情况下会发生此问题:
  • The event receiver is on the host web (rather than the 应用程式 web)
  • 事件接收器使用CSOM代码与SharePoint对话(而不仅仅是使用NON-SharePoint代码)

发出症状

当你 ’在尝试在主机网络上使用RER时,您可能会看到事件接收器始终触发(而您的远程代码 确实 执行),则您的代码无法通过身份验证返回SharePoint。您’尝试获取或使用ClientContext对象时将得到401错误–这可能会在您的代码中体现为其中之一:
System.NullReferenceException:对象引用未设置为对象的实例
..但是如果您查看HTTP请求/响应,’将看到HTTP 401(拒绝访问)。
这意味着您可以有效地’用SharePoint做任何事情–当然,通常 整点 你的事件接收者的是’在SharePoint中要做一些事情!也许您需要在某处创建,更新或删除项目,或执行其他与SharePoint中的网站,用户或数据有关的操作。
其他资讯:
  • 没有上下文令牌
    • 如果您进行一些调试,则您’会发现事件接收器未从SharePoint / Office 365接收到有效的上下文令牌-SPRemoteEventProperties.ContextToken为空字符串。这将导致所有以后的代码失败。此时,您可能认为’SharePoint / Office 365的一个错误–即使在这一切结束时,我’我个人不确定是否有错误’s just a “by-design”局限性。幸运的是,正如我们将看到的,有一种变通办法适用于这种(大多数?)这种情况。
  • The same code works on the 应用程式 web
    • 如果您进行更广泛的测试,也许看看是否存在代码问题– you’ll find the very same code works fine in other contexts. For example, if you use the same code in a RER on the 应用程式 web (e.g. “ItemAdded” on a list in the 应用程式 web), everything works fine. The issue is purely related to RERs on the host web.
  • No change if you use ProcessOneWayEvent (asynchronous events such as 添加, deleted etc.) or ProcessEvent (synchronous events such as 加, deleting etc.)
    • 远程事件接收器与传统的完全信任事件接收器相似,因为异步事件和同步事件都可以使用。这里的一个例子是“added” and “adding”事件分别–这些对应于Remote Event Receiver类中的ProcessOneWayEvent和ProcessEvent方法。供参考,我们’在这两种情况下重新讨论都体现出来。
这些就是症状。在我们深入研究之前“Why it happens” and “The workaround” sections, it’值得提醒我们自己特别是远程代码和身份验证的某些方面。如果您愿意,可以跳过’熟悉这个领土。

提醒远程验证代码

For those just starting out with Remote Event Receivers (or SharePoint 应用程式s/remote code in general), it’s worth noting that an 应用程式 is required for any form of remote code. 更正确地说,我们可以扩展为 在服务器端运行并使用的任何远程代码“app authentication” - 相对于“user authentication”指定用户名/密码的位置。无论如何,对于某些事件(例如远程事件接收器),这似乎有些奇怪,’t really an “app”(例如带有某些页面的页面),用户将在其中输入/单击鼠标等。RER所拥有的只是一个没有前端的远程WCF服务!但是,这就是信任模型用于远程代码的方式–所以首先要说的是,如果你不’t have an 应用程式 registered and installed/trusted for your remote code, it will not work.
如果我们要总结远程SharePoint代码的常见身份验证选项,则可以将其分解如下:
  • 用户认证
    • 用户名/密码“named user” is passed – usually a very poor 应用程式roach that brings security risks. Effectively this led OAuth to be invented as an alternative.
  • 应用验证–分为:
    • User + 应用程式 authentication – the default. Permissions of both the user *and* the 应用程式 are checked
    • 仅应用验证 – the permissions of just the 应用程式 are checked (thus allowing operations that the user themselves 确实 not have permission to do)

为什么会发生

如果你’重新解决该问题,您的RER很可能具有以某种方式实例化ClientContext对象的代码-特别是,您’ll be using “user + 应用程式” authentication. The vast majority of MSDN and blog samples use this 应用程式roach. 这最终是问题-  “user + 应用程式”身份验证当前不适用于与主机Web相关的事件。我的测试表明,Office 365和SharePoint 2013本地安装都是这种情况。 Along the journey of realising this, I tried some different coding 应用程式roaches –而且我看到其他开发人员可能也在做同样的事情(例如在论坛帖子中)。由于可以通过多种方式获取CSOM ClientContext对象,因此您可以为此尝试不同的代码–但是一些不能在RER中使用,并且会失败。下表显示了一些“wrong” 应用程式roaches.
Code 应用程式roaches which will NOT work:
方法
代码样例
为什么不
使用typical RER 应用程式roach ClientContext clientContext = TokenHelper.CreateRemoteEventReceiverClientContext(properties) This is the 应用程式roach that “should work” (and the 应用程式roach that most samples use). However, it only works for events on the 应用程式 web –主机网站上的NOT事件。
使用构造函数创建ClientContext ClientContext clientContext =新的ClientContext(properties.ItemEventProperties.WebUrl) 不能以处理身份验证的方式获得上下文。这就是TokenHelper方法的用途。

边注–可以通过 用户认证 代替 应用程式 authentication,如果使用SharePointOnlineCredentials对象附加了命名帐户的用户名/密码。 但是那’la脚,安全风险大! Instead, we want to use OAuth (via 应用程式 authentication) of course, to avoid storing/passing passwords.
使用“app event” 应用程式roach ClientContext clientContext = TokenHelper.CreateAppEventClientContext(properties,false) The context is obtained in a way which is only 应用程式ropriate for 应用程式 事件(例如AppInstalled / AppUpgraded / AppUninstalled等)。由于我们在主机网络上使用事件,因此身份验证不起作用。

如表所示,“应该”工作的是第一个(TokenHelper.CreateRemoteEventReceiverClientContext())– but this relies on “user + 应用程式” authentication which 应用程式ears to be the problem. So to work around this we need a different 应用程式roach. 

解决方法

代替“user + 应用程式”我们可以使用的身份验证“app-only” authentication. This requires 2 东西:
  1. The 应用程式 permission policy to specify that 应用程式-only calls are allowed (something the person installing the 应用程式 must agree to). This is enabled in the 应用程式 manifest in Visual Studio:

    Allow 应用程式-only auth
  2. The code in the 应用程式 (i.e. in the Remote Event Receiver) to obtain ClientContext using an 应用程式-only access token. The expected way to do this in C# is to use the correct TokenHelper methods – big thanks to 柯克·埃文斯(Kirk Evans) 为我提供帮助。该代码应如下所示:

    ** N.B.这里有一个代码示例,但不会在RSS阅读器中显示- 点击这里查看全文 **
如果你 ensure these two 东西 are in place, your Remote Event Receiver will work fine.

局限性–当解决方法不好时

以便’s the answer then. However, you might hit occasions where the 应用程式-only 应用程式roach isn’真的是您想要的。如果您的远程代码将任何数据写入SharePoint(例如添加列表项),则您’ll notice that the SharePoint user interface makes it clear that the list item was 添加/modified by an 应用程式. Specifically, the username will be in the following format:
上次在[时间]由[应用名称]代表[用户名]修改
This is a pretty useful feature for some business requirements, since it makes it clear that although the 命名用户 was involved, they didn't make the change in SharePoint "directly". Unfortunately, use of the 应用程式-only authentication route to solve our problem changes this – specifically, with 应用程式-only auth only the 应用程式 name is recorded as the user who made the change. Effectively, we lose the information which tells us which actual person was involved in the change - in some circumstances, this could be a problem if some kind of audit trail is required. To make 东西 clear, here are two screenshots which show the difference:
List item 添加 by "应用程式 + user" authentication:
List item 添加 with App and User context
List item 添加 by "应用程式-only" authentication:
List item 添加 with App Only context
相关的旁注(出于好奇!):
如果你’想知道SharePoint / Office 365如何在后台进行处理,您’会发现所有列表现在都有2个新的隐藏列-“App Created By” and “App Modified By”。这些存储对当前站点的“用户信息”列表中条目的引用(就像其他“人员/组”列一样):
AppCreatedByModifiedByColumnsOnList AppCreatedByModifiedByColumnsOnList2
所以现在你知道了;)

概要

It’使用“远程事件接收器”很容易出错,这是我的陷阱’谈到身份验证和CSOM代码绝对是一大难题– I certainly see other people hitting this. 如果你 have remote code which modifies data in SharePoint somewhere, the way around the problem is to use 应用程式-only authentication - by ensuring the 应用程式’的权限策略允许*并*还编写您的CSOM代码以这种方式进行身份验证。这可以通过使用以下命令获取访问令牌来完成: TokenHelper.GetAppOnlyAccessToken() 方法,然后使用此令牌获取ClientContext对象。
However, with this 应用程式roach comes a trade-off - the loss of user information (in “pseudo-audit”条款)与更改背后的用户有关。开发人员在使用远程事件接收器时应牢记这一点。

2014年7月14日,星期一

第7次续签为SharePoint MVP

MVP_header_logo

我最近收到一个喜讯,我将继续担任SharePoint MVP一年–我的第七次。一如既往’在过去的12个月中,与社区的互动非常愉快。每年我都喜欢在我的博客上写下社区“things” I did in the previous year - this list is mainly for me rather than you, but here are some the 东西 I got up to:

It’成为社区的一分子是我的荣幸,我希望在未来的12个月中继续发挥作用!