Jenkins 订书机请求失败,没有有效的碎屑

Jenkins stapler requests fail with no valid crumb

我正在开发一个 Jenkins 插件,但我现在遇到了一个问题,我正在尝试使用 JavaScript 代理获取方法的 return 值 JavaScript here.

我只想调用这个 kotlin 方法:

@JavaScriptMethod
fun getMonitoredJobsAsJSON(): JSONArray = toJSON(getObjectMapper().writeValueAsString(getMonitoredJobs())) as JSONArray

来自果冻脚本使用这个:

<script>
  var board = <st:bind value="${it}"/>

  board.getMonitoredJobsAsJSON(function(data) {
   //
  })
</script>

当我在 Jenkins 服务器上禁用 CSRF 保护时,这实际上有效,但我显然不想这样做。启用 CSRF 保护后,我总是会收到“未找到有效面包屑”403 错误:

POST http://localhost:8080/$stapler/bound/36dc05fc-c12d-4182-a008-60bcf5c49307/getMonitoredJobsAsJSON 403 (No valid crumb was included in the request)

我知道如何从 crumbIssuer 端点检索面包屑以与 Jenkins rest 交互 api 但我几乎没有找到关于如何使其适用于插件中订书机请求的资源.

此外,当我检查请求时,实际上在请求中设置了碎屑 header:

在此先感谢您的帮助。

几周后,我终于找到了解决办法。

问题在于,由于某种原因,默认情况下附加到请求的 crumb header 的名称实际上是错误的。如我问题中的屏幕截图所示,它是 Crumb,但对于旧版本的 Jenkins,它实际上应该是 Jenkins-Crumb.crumb

我所做的是找到一种方法来在页面最初加载时从服务器检索面包屑 和正确的 header 名称 ,然后附加此crumb header 对任何后续 xhr 请求使用正确的名称。

我为面包屑定义了一个实体:

class RemoteRequestCrumb {
    @JsonIgnore private val crumbIssuer: CrumbIssuer? = Jenkins.getInstance()?.getCrumbIssuer()
    val fieldName: String? = crumbIssuer?.crumbRequestField
    val crumbValue: String? = crumbIssuer?.crumb
}

然后将此实体作为属性添加到插件中:

fun getRemoteRequestCrumb(): JSONObject = toJSON(
    SerializationUtils.getObjectWriter().writeValueAsString(RemoteRequestCrumb())
) as JSONObject

现在您可以像使用任何其他插件属性一样从 jelly 脚本请求面包屑数据:${it.getRemoteRequestCrumb()}

最后一步实际上是将正确的 header 附加到所有 XHR 请求:

appendCrumbHeaderToAllRequests: function () {
  let crumb = JSON.parse(this.remoteRequestCrumb);
  let open = XMLHttpRequest.prototype.open;

  XMLHttpRequest.prototype.open = function() {
    let mutatedPrototype = open.apply(this, arguments);
    this.setRequestHeader(crumb.fieldName, crumb.crumbValue);
    return mutatedPrototype;
  }
}