我有一个WCF服务,它承载在一个 ASP.NET MVC应用程序内部(如 http://msdn.microsoft.com/en-us/library/aa702682.aspx 中所述)。部分 MVC 操作和 WCF 服务操作受到保护,我对两者使用 ASP.NET 窗体身份验证:
// protected MVC action
[Authorize]
public ActionResult ProtectedMvcAction(string args)
// protected WCF operation
[PrincipalPermission(SecurityAction.Demand, Role = "User")]
public void ProtectedWcfOperation(string args)
我的 WCF 客户端确保在每次 WCF 调用时将窗体身份验证.ASPXAUTH
cookie 传输到服务器。
这在很长一段时间内都非常有效。现在,我使用 SSL
证书向我的服务器添加HTTPS
加密。这要求我对 Web.config 进行以下更改:
<basicHttpBinding>
<binding name="ApiServiceBinding">
<security mode="Transport">
<transport clientCredentialType="None" />
</security>
</binding>
</basicHttpBinding>
服务被激活,客户端可以调用服务器操作。但是,受保护服务器操作前面的 [PrincipalPermission]
属性会突然阻止所有服务调用。我发现了以下内容:
- 在 HTTP 情况下(不带
<security mode="Transport">
),Thread.CurrentPrincipal
和HttpContext.Current.User
都设置为RolePrincipal
实例,RolePrincipal.Identity
属性中有一个FormsIdentity
实例。在这种情况下,一切正常。 - 在 HTTPS 情况下(在 web.config 中带有
<security mode="Transport">
),属性HttpContext.Current.User
仍设置为RolePrincipal/FormsIdentity
组合。但是,属性Thread.CurrentPrincipal
突然设置为WindowsPrincipal/WindowsIdentity
实例,这使得[PrincipalPermission]
属性引发异常。
我尝试了以下方法:
- 将
AppDomain.CurrentDomain.SetPrincipalPolicy
更改为所有可能的值(在Global.asax
的Application_Start
中),但这并没有改变任何东西。 - 在
Application_PostAuthenticate
中设置属性Thread.CurrentPrincipal
,但在Application_PostAuthenticate
和实际服务调用之间,Thread
的CurrentPrincipal
将再次更改为WindowsPrincipal
。
有什么提示吗?我做错了什么?
这解决了它:
http://www.codeproject.com/Articles/304877/WCF-REST-4-0-Authorization-with-From-Based-authent
我修改了此代码以涵盖Windows和窗体端点以及相同的服务 - 这也有效 -
public bool Evaluate( EvaluationContext evaluationContext, ref object state )
{
bool ret = false;
// get the authenticated client identity
HttpCookie formsAuth = HttpContext.Current.Request.Cookies[ ".MyFormsCookie" ];
if( null != formsAuth )
{
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt( formsAuth.Value );
if( null != ticket )
{
GenericIdentity client = new GenericIdentity( ticket.Name, "Forms" );
// set the custom principal
CustomPrincipal p = new CustomPrincipal( client );
p.RoleManagerProvider = "Internet";
evaluationContext.Properties[ "Principal" ] = p;
ret = true;
}
}
else
{
CustomPrincipal p = new CustomPrincipal( HttpContext.Current.User.Identity );
p.RoleManagerProvider = "Intranet";
evaluationContext.Properties[ "Principal" ] = p;
// assume windows auth
ret = true;
}
return ret;
}
它查找表单身份验证cookie,并尝试使用Windows身份验证(如果不存在)。 我还"翻转"内部和外部的角色提供者
这允许我将用户凭据从互联网(通过转发cookie)和内部网(使用Windows约束委派)传播到同一内部服务。
我在配置中进行了配置(而不是每个示例的代码),看起来很好。
对于行为,它类似于:
<behavior name="FormsPaymentsBehavior">
<serviceAuthorization principalPermissionMode="Custom" >
<authorizationPolicies>
<add policyType="FormsPolicy.AuthorizationPolicy,FormsPolicy" />
</authorizationPolicies>
</serviceAuthorization>
这用于两个终结点,因为 FormsPolicy(上面)处理这两个终结点,并且您不能为不同的终结点指定不同的行为。
绑定在相应的终结点上强制实施 Windows 凭据握手:
<basicHttpBinding>
<binding name="WindowsHttpBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</binding>
<binding name="FormsHttpBinding" allowCookies="true">
<security mode="None">
<transport clientCredentialType="None" />
</security>
</binding>
</basicHttpBinding>
传输方式可以更改为
<security mode="Transport">
<transport clientCredentialType="None" />
</security>
对于https,它工作正常。
对于您的自定义主体,我发现我必须显式调用角色管理器
...
public bool IsInRole( string role )
{
RoleProvider p = Roles.Providers[ RoleManagerProvider ];
return p.IsUserInRole( Identity.Name, role );
}
public String RoleManagerProvider { get; set; }
。
我想,这是因为我不再使用任何aspnet兼容的东西。 由于我正在根据我的身份验证类型翻转角色管理器,因此哼哼。
我也遇到过这个问题,这里有另一份报告(和我的)。 http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/8f424d4f-2f47-4f85-a6b0-00f7e58871f1/
此线程指向正确的解决方案是创建自定义授权策略 (http://msdn.microsoft.com/en-us/library/ms729794.aspx),此代码项目文章 (http://www.codeproject.com/Articles/304877/WCF-REST-4-0-Authorization-with-From-Based-authent) 似乎确切地解释了如何为 FormsAuth 执行此操作 - 根据 MS 注释设置 evaluationContext.Properties["Principal"] = new CustomPrincipal(client)。
我还没有实现这一点 - 我的"快速修复"是简单地恢复到一个普通的旧 asmx 服务 - 但我会"试一试"一段时间!
如果您找到其他解决方案 - 请告诉我。