0

我正在尝试为具有一些通用功能的移动和常规站点开发 WEB API,其中之一是身份验证服务。我采用 VS2013 SPA 模板的服务器端部分并尝试基于个人帐户实现安全性。主要要求是能够使用用户名/密码登录并通过 facebook 登录。当 OWIN 中间件和客户端在同一个项目中(例如 VS2013 SPA 的示例)时,VS2013 的 Tempate 工作正常,但是当它不存在时,应该进行一些修改,我想分享:

  1. 在用作 OWIN 中间件 CORS 的 WEB API 上应启用。
  2. 在每个 AJAX 请求的 UI 客户端(这是单独的项目/解决方案)中,应添加额外的标头“X-Requested-With”:“XMLHttpRequest”。我注意到如果没有它,WEB API 返回 XML 而不是 JSON 有效负载,这会导致 UI 示例中的映射不正确(failJSON 无法解析错误消息)。
  3. VS2013 SPA 模板中的 UI 示例设置为与同一主机一起使用,因此应为 UI 客户端中的每个请求添加一些带有基本 url(WEB API url)的前缀。

因此,我通过实现自定义 UserManager、UserStore、IIdentityValidator 和 IPasswordHasher 成功扩展了解决方案,它们与我现有的 MongoDB 数据库和自定义逻辑一起使用。用户名/密码验证工作得很好——我从 WEB API 接收不记名令牌,并且可以在后续调用中使用它。但我在外部身份验证方面遇到的主要问题是:在我的情况下是 facebook。实际上,所有外部登录流程都很顺利,并预测到最后一步,即:

授权端点检查外部登录 cookie 主体并找到关联的应用程序用户,然后将用户作为 Bearer 身份验证类型登录到授权服务器中间件。由于授权服务器看到请求参数 response_type 是令牌(在步骤 1 中),它将触发隐式流程,这将创建访问令牌并将其作为 URL 片段附加到 redirect_uri(步骤 1)。例如:

HTTP/1.1 302 找到 Cache-Control: no-cache Pragma: no-cache Expires: -1 Location: /#access_token=asd2342SDIUKJdsfjk3234&token_type=bearer&expires_in=1200&state=06hwltIjvnTn44hc Set-Cookie: .AspNet.External=; 路径=/; expires=Thu, 01-Jan-1970 00:00:00 GMT Set-Cookie: .AspNet.Cookies=WJgdyZQs9N8TG20EWnik-j0; 路径=/; HttpOnly 内容长度:0

在这里您可以看到为 WEB API 主机生成的 /#access_token,完整的 url 如下所示:

http://mobileapi.example.loc:61707/#access_token=zn73ihR7Uve3TNfTWNnB..AHHwOQijdOaeMDDccUIBbhPiEOVsGn2A&token_type=bearer&expires_in=1209600&state=jZbC35_Inoi8Zop22Mg7xyAzV-BUdE3Zj2k0bMcG5

因此它不会重定向到发起身份验证的客户端(例如 [http://localhost:2108]),而是重定向到中间件主机本身(在我的例子中是http://mobileapi.example.loc:61707)。肯定会给出 404 Not Found,因为 WEB API 仅包含服务器端并且无法解析这个 #access_token - UI 客户端应该这样做。

所以我的问题是如何正确拦截重定向并将服务器主机替换为客户端主机?

PS 我找到了构建这个#access_token 字符串的源代码。它在 OAuthAuthorizationServerHandler 类的 ApplyResponseGrantAsync 方法中,该实例是从 OAuthAuthorizationServerMiddleware 创建的。它具有内部可访问性。所以为了改变这种行为,我应该重写整个中间件,我认为这不是很好的方法。

我看到的 PSS 另一个选项是在 WEB API 上创建全局处理程序,它拦截所有传入的请求,如果它找到一个从 #access_token 开始的请求,那么它将从请求中获取引用主机并重定向到它。但它很丑。

提前谢谢。

4

1 回答 1

2

它比我想象的要简单得多。从客户端,您只需将 returnUrl 设置为您需要的。服务器端没有变化。

例如,我在客户端上有调用获取外部登录的功能:

function externalLoginsUrl(returnUrl, generateState) {
    return baseurl + "/api/Account/ExternalLogins?returnUrl=" + (encodeURIComponent(returnUrl)) +
        "&generateState=" + (generateState ? "true" : "false");
}

self.getExternalLogins = function (returnUrl, generateState) {
    return $.ajax(externalLoginsUrl(returnUrl, generateState), {
        cache: false,
        headers: getAllHeaders()
    });
};

返回网址:

self.returnUrl = window.location.origin;

就这些 :)

于 2014-04-22T15:45:49.537 回答