ASP.NET MVC+API 和 WCF,具有针对自定义数据库和活动目录的 claims-based 授权和身份验证

ASP.NET MVC+API and WCF with claims-based authorization and authentication against a custom DB and active directory

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

这两个应用程序通过托管在与 MVC 应用程序相同的 ASP.NET 应用程序中的单独 WebApis 使用相同的 WCF 服务。 API 和 web-based 应用程序 (HTML/JS) 访问。

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

TL;DR 摘要: 我们需要了解有关 WCF 服务中 web-user 的内容。

我们需要:

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

到目前为止我有:

我没有的:

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

即使您只是帮助我更好地理解术语和技术,也将不胜感激。

P.S.: 我们是 运行 .NET 4.6

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

我的下一步是想弄清楚 UseIdentityServerBearerTokenAuthentication 如何阻止我cessing 任何东西以及为什么如果我删除任何必需的范围它不会...

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

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

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

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

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

为了验证每个请求的令牌,我将 CookieAuthenticationOptionsProvider 配置为 new CookieAuthenticationProvider(),我在 [=42] 期间查询 IdentityServer 的 introspection-endpoint =]:

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 所做的唯一事情,因为我有几条通往多个 Apis 的路线,并且在设置多个地图时遇到了麻烦通过 Ninject...).

进行依赖注入
 app.Map(
    "/api",
    inner =>
    {
            inner.UseIdentityServerBearerTokenAuthentication(identityServerBearerTokenAuthenticationOptions);
    });

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

查看:

<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/

基本上可以归结为:

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

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

在 WCF-side 上,我删除了所有 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

现在我已经获得所有用户的身份验证,阻止访问 non-authenticated 用户(重定向登录)并保证只有授权用户才能访问特定方法。 :)

OAuth2 和 OpenID Connect 等新的 Internet 安全协议不能很好地与 WCF SOAP 服务配合使用。

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

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

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

P.S。 IdentityServer and Katana(Microsoft 的 OWIN 实现)都是开源的,因此您可以了解所有这些代码的实际作用。

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

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