【发布时间】: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.UseCookieAuthentication和app.UseIdentityServerBearerTokenAuthentication来设置身份验证,但我不太确定这到底意味着什么,所以我现在不太适应。 - 登录时(通过 MVC)我通过
TokenClient.RequestResourceOwnerPasswordAsync(username, password, scopes)从 IdentityServer 请求(访问?)令牌,尝试通过访问用户信息来构建ClaimsIdentity?端点并通过Request.GetOwinContext().Authentication.SignIn(id);设置cookie。 - 基本了解基于令牌的身份验证和基于声明的授权的工作原理
我没有的:
- 相信我完全了解基于声明的授权的工作原理,并且可以在 WCF、MVC 和 WebApi 中实现
- 知道不同的 OWIN 中间件和扩展(
UseIdentityServerBearerTokenAuthentication、UseOpenIdConnectAuthentication等)的实际作用、它们应该如何使用、何时使用以及有什么安全隐患。 - 知道
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);
陷阱:自省端点需要作为范围进行身份验证,而不是客户端 - 这在示例中有点误导,其中客户端和范围具有相同的名称
为了验证每个请求的令牌,我将CookieAuthenticationOptions 的Provider 配置为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 更改为使用 https 和 44300 到 44399 范围内的端口,以便在 localhost 上进行开发(因为 Windows 已经预配置了证书和访问控制这些端口的列表 - 对于部署,您必须添加您的证书并允许 WCF-host-user 在那里托管 HTTP/SSL 服务)。
如果任何元数据端点可见,那么它们也必须使用 HTTPS(binding="mexHttpsBinding" 和 <serviceMetadata httpGetEnabled="false" />)
为了使用该绑定,安全性必须是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