【问题标题】:ASP.NET MVC+API and WCF with claims-based authorization and authentication against a custom DB and active directoryASP.NET MVC+API 和 WCF 以及针对自定义 DB 和活动目录的基于声明的授权和身份验证
【发布时间】:2016-02-20 05:59:17
【问题描述】:

我们正在开发两个(新的)ASP.NET 应用程序,它们使用 MVC/Razor 视图作为模板引擎来处理 I18N/L10N(当前文化存储在每个请求的 cookie 中并从中检索;文本从资源中加载)。

两个应用程序通过托管在与 MVC 应用程序相同的 ASP.NET 应用程序中的单独 WebApi 使用相同的 WCF 服务。 API 和由基于 Web 的应用程序 (HTML/JS) 访问。

应用程序 A 目前使用表单身份验证(用户凭据由 WCF 服务针对我们的应用程序数据库进行验证),应用程序 B 使用 Windows 身份验证。

TL;DR 摘要:我们需要在 WCF 服务中了解有关网络用户的信息。

我们需要:

  • 有权访问 WCF 服务中的当前(Web)用户身份
  • 传输有关该身份的可验证信息(例如角色、组成员身份等)并使用该信息来限制查询和操作(即只允许查看和编辑您自己组中的数据)。
  • 在应用程序 B 中透明地验证用户身份(我们不希望 IE 中出现单独的登录屏幕或弹出窗口)
  • 支持通过一次性键盘登录,其中身份验证在某种程度上是带外的(例如,用户获得一个仅短暂有效的访问代码,它对他/她自己进行身份验证,就好像他使用他的凭据登录一样)
  • 具有短暂的会话/令牌生命周期,但使用滑动到期 - 用户活动应延长其会话/令牌生命周期。非活动用户应在短暂不活动后退出。
  • 在与 WCF 服务通信时具有消息安全性(由于需要模拟/委托,最好使用证书而不进行基于证书的实际身份验证?)

到目前为止,我对此的想法和印象是:

  • 基于声明的授权可以很好地将授权与业务逻辑分离
  • 基于声明的身份验证/授权似乎可以传输我们限制可用数据和操作所需的所有信息(即从声明中读取组成员身份)
  • 基于令牌的身份验证是基于声明的身份验证(令牌是声明的传输方式)
  • 我需要一个安全令牌服务来验证用户身份 (IdentityServer)
  • 可以在 WCF 通道创建期间以某种方式使用令牌来传输当前(声明)主体或相应令牌。
  • 从表单迁移到基于令牌的身份验证时,我必须为我们的 Web 客户端使用资源所有者密码凭据流

到目前为止我有:

  • 使用 ROPC 流通过单个客户端设置 IdentityServer
  • 将 OWIN 添加到 Web 应用程序 A 并使用 app.UseCookieAuthenticationapp.UseIdentityServerBearerTokenAuthentication 来设置身份验证,但我不太确定这到底意味着什么,所以我现在不太适应。
  • 登录时(通过 MVC)我通过 TokenClient.RequestResourceOwnerPasswordAsync(username, password, scopes) 从 IdentityServer 请求(访问?)令牌,尝试通过访问用户信息来构建 ClaimsIdentity?端点并通过Request.GetOwinContext().Authentication.SignIn(id);设置cookie。
  • 基本了解基于令牌的身份验证和基于声明的授权的工作原理

我没有的:

  • 相信我完全了解基于声明的授权的工作原理,并且可以在 WCF、MVC 和 WebApi 中实现
  • 知道不同的 OWIN 中间件和扩展(UseIdentityServerBearerTokenAuthenticationUseOpenIdConnectAuthentication 等)的实际作用、它们应该如何使用、何时使用以及有什么安全隐患。
  • 知道wS2007FederationHttpBinding 的作用和用途,或者我必须在Startup.cs 中配置什么以及需要在web.config 或其他地方配置什么
  • 如何使用适当的证书、SSL/HTTPS 保护所有内容,以便我可以在团队开发和部署期间使用它(我们为每个开发人员以及每个构建配置都进行了配置转换)

基本上,我已经阅读了很多关于 WCF、WIF 等的信息,以至于我不清楚哪些信息是最新的、最佳实践是什么以及如何实现符合我们所有要求的东西。 IdentityServer 中的示例对我没有太大帮助,因为对我来说,它们没有任何解释。

如果您能帮助我更好地理解术语和技术,我们将不胜感激。

P.S.:我们正在运行 .NET 4.6

更新: 我已经设法从 IdentityServer 检索令牌并查询用户信息端点以获取声明。结果我使用了ClaimTypes.Email 等而不是Constants.ClaimTypes.Email - 也只有身份范围会在 userinfo 端点上产生声明,我在我的资源范围中指定了它们。

我的下一步是试图弄清楚UseIdentityServerBearerTokenAuthentication 如何阻止我访问任何内容,以及如果我删除任何必需的范围,为什么它不会...

更新: (2015-12-01) 我目前正在尝试以某种方式透明地将令牌传输到 WCF 服务,并根据令牌提供当前委托人(或声明委托人)。我还没有明确的方法来实现这一点。

MVC 和 WebApi 的东西现在似乎可以正常工作了:

使用 OWIN cookie 中间件保护 MVC 应用程序 - 在使用表单身份验证的登录时,我使用 TokenClient 和资源所有者密码凭据流。然后我查询userinfo(通过UserInfoClient)和introspection(通过IntrospectionClient)端点来构建声明,创建ClaimsIdentity并将其保存在cookie中。我还将访问令牌保存为声明。

var id = new ClaimsIdentity(listOfClaims, "Cookies");
this.Request.GetOwinContext().Authentication.SignIn(id);

陷阱:自省端点需要作为范围进行身份验证,而不是客户端 - 这在示例中有点误导,其中客户端和范围具有相同的名称

为了验证每个请求的令牌,我将CookieAuthenticationOptionsProvider 配置为new CookieAuthenticationProvider(),其中我在OnValidateIdentity 期间查询IdentityServer 的自省端点:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = "Cookies",
    LoginPath = new PathString("/User/Login"),
    LogoutPath = new PathString("/User/Logoff"),
    // setup more options etc..

    Provider = new CookieAuthenticationProvider()
    {
        OnValidateIdentity = async context =>
        {
            // validate etc. - and if it fails call: context.RejectIdentity();
        }
    }
}

通过扩展用于设置 WebApi 的Register(HttpConfiguration config),WebApi 与 cookie 身份验证分离:

// prevent use of owin cookie auth
config.SuppressDefaultHostAuthentication();

// need bearer token auth
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

此外,我已经在 owin 启动中设置了 IdentityServerBearerTokenAuthentication(基本上,这是我对 WebApi 所做的唯一事情,因为我有多个路由到多个 API,并且无法通过 Ninject 设置具有依赖注入的多个映射...)。

 app.Map(
    "/api",
    inner =>
    {
            inner.UseIdentityServerBearerTokenAuthentication(identityServerBearerTokenAuthenticationOptions);
    });

当然,仅此一项仅意味着 Angular 应用程序将无法获取任何数据 - 因此在单页应用程序视图中,我将令牌作为常量注入到配置中,并通过配置 @ 设置默认标头987654351@:

查看:

<script type="text/javascript">
  angular.module("myApp").constant("authorizationHeader", "Bearer @Model");
</script>

应用程序:

myApp.config(["$httpProvider", "authorizationHeader", function ($httpProvider, authorizationHeader) {
    $httpProvider.defaults.headers.common["Authorization"] = authorizationHeader;
}]);

...所以我现在唯一的问题几乎是如何将令牌传输到 WCF 服务以及如何在服务上从该令牌设置声明身份(无需明确地这样做 - 即我没有想要在我的合约上有一个单独的参数)

P.S.:我只能在 app.config 中配置 WCF 服务,因为我们使用的是自托管的 WCF 服务和 Ninject...

更新 2015-12-10 成功!

我将参考令牌传输到 WCF 服务,类似于 Dominick Baier 建议的方式:http://leastprivilege.com/2015/07/02/give-your-wcf-security-architecture-a-makeover-with-identityserver3/

基本上可以归结为:

  • 将引用令牌包装在 Claim 中的新 ClaimsIdentity
  • 为该身份创建一个新的SecurityTokenDescriptor
  • Saml2SecurityTokenHandler 包装
  • 包装在GenericXmlSecurityToken
  • 通过this.ChannelFactory.CreateChannelWithIssuedToken(xmlToken); 创建通道(提示:缓存ChannelFactory 但不缓存Channel

这意味着我必须将绑定更改为 ws2007FederationHttpBinding,将所有 url 更改为使用 https4430044399 范围内的端口,以便在 localhost 上进行开发(因为 Windows 已经预配置了证书和访问控制这些端口的列表 - 对于部署,您必须添加您的证书并允许 WCF-host-user 在那里托管 HTTP/SSL 服务)。 如果任何元数据端点可见,那么它们也必须使用 HTTPS(binding="mexHttpsBinding"&lt;serviceMetadata httpGetEnabled="false" /&gt;

为了使用该绑定,安全性必须是TransportWithMessageCredentials

在 WCF 端,我删除了所有 SecurityTokenHandler 并添加了我自己的,它解开引用令牌并使用来自 IdentityServer 的 introspection 端点来构建身份(非常类似于来自 IdentityServer 的示例)。

这可以在代码或 app.config 中完成(ServiceCommunication 是我保存所有这些东西的程序集):

<configSections>
  <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
</configSections>
<system.identityModel>
<identityConfiguration name="identity">
  <securityTokenHandlers>
    <clear />
    <add type="ServiceCommunication.Authentication.IdentityServerWrappedReferenceTokenHandler, ServiceCommunication" />
  </securityTokenHandlers>
  <claimsAuthorizationManager type="ServiceCommunication.Authorization.ClaimsAuthorizationManager, ServiceCommunication"/>
</identityConfiguration>

对于授权,我定义了自己的ClaimsAuthorizationManager,它处理被覆盖的CheckAccess 方法中的所有内容。注意:每个 WCF 合同方法也会调用该方法。

授权可以通过ClaimsPrincipalPermissionAttribute 使用任何纯代码或通过ClaimsPrincipalPermission 显式授权。甚至不必是 WCF。在 .NET 4.5 中开箱即用

我在 WCF 服务以及 MVC/API 控制器操作中尽可能深入地使用ClaimsPrincipalPermissionAttribute

现在我已经对所有用户进行了身份验证,防止访问未经身份验证的用户(重定向到登录)并保证只有授权用户才能访问特定方法。 :)

【问题讨论】:

  • ...所以我编辑了很长时间,最终被注销。我现在被困在 WCF 部分 - MVC 和 API 就像一个魅力:)

标签: asp.net wcf authentication claims-based-identity identityserver3


【解决方案1】:

新的互联网安全协议(如 OAuth2 和 OpenID Connect)不能很好地与 WCF SOAP 服务配合使用。

授权服务器在使用 OAuth2 和 OpenID Connect 协议时发出 JWT 令牌。但是,当您使用 WS2007FederationHttpBinding 时,您的 SOAP WCF 服务在信封头中需要一个 SAML 令牌。 IndentityServer does not issue SAML tokens.

虽然可以使用来自 JWT 令牌的声明来颁发和签署您自己的 SAML 令牌,但将 WCF SOAP 服务替换为基于 REST 的服务可能更容易,该服务需要 Authorization 标头中的 JWT 令牌。

我不确定您为什么要使用资源所有者密码凭据流程。您可能想查看using IdentityServer with an existing database 并改用身份验证代码流。

附: IdentityServerKatana(微软的 OWIN 实现)都是开源的,所以你可以弄清楚所有这些代码实际上在做什么。

【讨论】:

  • 感谢您提供有关 JWT 和 SAML 的信息。我想使用 ROPCF,因为我们有多个用户,我们需要将数据范围限定为每个用户。据我了解,授权代码流将更多地用于内部服务器到服务器的通信,因为客户端将通过自己的秘密进行身份验证。在这种情况下,我们只有一两个客户端,但可能有数百个用户。
  • 不,在身份验证代码流中,客户端和用户都经过身份验证(尽管本身不需要客户端身份验证)。资源所有者密码凭证用于您没有涉及类似浏览器的代理的情况(因为身份验证代码和隐式流是基于重定向的)。仅在客户端凭据流中没有用户身份验证。
  • 我在身份验证代码流中看不到用户身份验证。此外,由于我会为每个浏览器发起的请求(存储在 cookie 中)使用访问令牌(和刷新令牌),因此首先获取身份验证代码将是无用的,因为我只需要令牌并且不需要长期访问非- 用户操作。我的流量来源是github.com/IdentityServer/Documentation/issues/73
  • 身份验证代码流将您的浏览器重定向到用户登录的授权服务器。它通过浏览器将身份验证代码发送回客户端应用程序,客户端应用程序使用它来获取访问令牌。这样,访问令牌永远不会暴露给浏览器,因此不会被盗。 OAuth2 的真正来源是tools.ietf.org/html/rfc6749
  • 好的。由于我们正在从表单身份验证迁移,并且我们需要适当的本地化和企业设计/用户体验以进行登录等,所以我们现在非常犹豫是否使用任何基于重定向的方法。
【解决方案2】:

所以我终于解决了这一切:

  • 我使用 IdentityServer3 对所有声明进行身份验证和管理
  • app.UseCookieAuthentication 和我自己的 CookieAuthenticationProvider 来验证 MVC 中的引用令牌并像以前的表单身份验证一样处理登录
  • app.UseIdentityServerBearerTokenAuthentication 用于 API(每个 http 请求必须设置一个标头 Authorize 和值 Bearer your-token-here,大多数 JS 框架允许您全局设置)。身份验证针对 IdentityServer,我配置了 ClientId 和 ClientSecret(实际上是 ScopeId 和 ScopeSecret),以便它使用自省端点。 注意:您只会收到针对该特定范围的声明,而不是针对其他范围的声明。
  • 各种IdentityModel 客户端进行身份验证和验证(并从用户信息和自省端点获取声明)
  • 通过 Ws2007FederationHttpBindingTransportWithMessageCredentials 安全性包装引用或 jwt 令牌
  • 我自己的 SecurityTokenHandler 在 WCF 服务上解开该令牌并验证它(并设置身份/主体)
  • 我自己的ClaimsAuthorizationManager通过ClaimsPrincipalPermission(Attribute)授权

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多