如何在 ServiceAuthorizationManager 中设置身份/用户名?
Posted
技术标签:
【中文标题】如何在 ServiceAuthorizationManager 中设置身份/用户名?【英文标题】:How to set identity/username in ServiceAuthorizationManager? 【发布时间】:2011-11-10 03:00:10 【问题描述】:我在服务器端有一个 WCF 4.0 REST 服务(托管在 IIS 中)和一个 android 客户端。 Android 客户端在自定义 HTTP 标头中发送加密的安全令牌,以便对用户进行身份验证。我已经实现了一个自定义ServiceAuthorizationManager
,它从标头中提取安全令牌。令牌包含我可以从令牌中读取的用户名:
public class MyAuthorizationManager : ServiceAuthorizationManager
protected override bool CheckAccessCore(OperationContext operationContext)
var requestMessage = operationContext.RequestContext.RequestMessage;
var requestProperty = (HttpRequestMessageProperty)requestMessage
.Properties[HttpRequestMessageProperty.Name];
var token = requestProperty.Headers["X-MyCustomHeader"];
if (!string.IsNullOrEmpty(token))
var userName = GetUserNameFromToken(token);
if (!string.IsNullOrEmpty(userName))
// How to save userName now so that I can
// retrieve it in the service operations?
return true;
return false;
现在,我的问题是我在各种服务操作中还需要经过身份验证的用户的名称(主要是访问用户配置文件数据),我打算这样检索它:
public void MyServiceOperation()
string userName = OperationContext.Current
.ServiceSecurityContext.PrimaryIdentity.Name;
// check profile store for that userName and do something depending on
// profile settings
如何在CheckAccessCore
中设置此用户名?
像这样非常幼稚的试验......
operationContext.ServiceSecurityContext.PrimaryIdentity.Name = userName;
...不起作用,因为PrimaryIdentity.Name
是只读的。我认为需要更复杂的代码。
【问题讨论】:
【参考方案1】:经过一番研究,我没有找到在ServiceAuthorizationManager.CheckAccessCore
中设置身份的方法。当用户的身份已经设置(可能是“匿名”(IsAuthenticated
是false
))并且无法再更改时,此方法似乎在处理管道中调用得太晚了。 ServiceAuthorizationManager
用于授权,而不是身份验证,因此它是实施自定义身份验证的错误位置。
我终于找到了解决我的问题的三种可能方法:
正如@TheCodeKing 的答案中链接的文章中所解释的,使用 WCF REST Starter Kit 提供了编写自定义 RequestInterceptor
的选项,该选项足够早地挂接到管道中,允许访问传入的请求并允许例如,根据自定义 HTTP 标头设置用户的身份。不幸的是,WCF REST Starter Kit 是旧的(基于 WCF 3.5),并且显然已经放弃了开发。它的一些功能已被合并到 WCF 4.0 中,但有些还没有,RequestInterceptor
就是其中之一。尽管如此,我现在已经使用了这个解决方案,并将 Starter Kit 中的 Microsoft.ServiceModel.Web
程序集混合到我的 WCF 4.0 解决方案中。经过几次简单的测试后,它似乎工作至今。
如果身份不是真正需要的,而只是用户名,则将用户名写入新请求标头的简单“技巧”/“hack”可以工作(也在CheckAccessCore
中):
// ...
var userName = GetUserNameFromToken(token);
if (!string.IsNullOrEmpty(userName))
requestProperty.Headers["X-UserName"] = userName;
return true;
// ...
然后在服务方法中:
public void MyServiceOperation()
string userName = WebOperationContext.Current.IncomingRequest
.Headers["X-UserName"];
// ...
另一个更底层的选项是编写一个自定义的HttpModule
,它拦截传入的请求并设置身份。 Microsoft Pattern & Practices 团队的here 是一个示例(请参阅本文中间的“HTTP 模块代码”示例)。
【讨论】:
等等,所以不可能以任何方式检索带有operationContext.ServiceSecurityContext.PrimaryIdentity.Name
的用户名?【参考方案2】:
看看this的文章。它有设置IAuthorizationPolicy
实例的示例。通过创建您自己的实现,您可以控制在上下文中传递的IPrincipal
和IIdentity
实例的创建。这一切都来自一个服务拦截器。
internal class AuthorizationPolicyFactory
public virtual IAuthorizationPolicy Create(Credentials credentials)
var genericIdentity = new GenericIdentity(credentials.UserName);
var genericPrincipal = new GenericPrincipal(genericIdentity,
new string[] );
return new PrincipalAuthorizationPolicy(genericPrincipal);
【讨论】:
我记得前几天我看过这篇文章,但没有详细了解,因为它需要 WCF REST starter Kit。但实际上套件中的RequestInterceptor
可能是解决我问题的关键。我没有找到另一种方法来在管道中及早拦截和评估消息(除非可能是自定义 HttpModule)在安全上下文和身份已经创建之前。遗憾的是,这个有用的钩子没有内置到 WCF4 中。我将试验文章中的代码。感谢您的提示!
是的,它有点隐蔽,不是吗:)
我现在就这样走了。我不喜欢使用旧的 WCF REST Starter Kit,但其他选项(请参阅我自己的答案)甚至更少。 IMO,MS 仍有很多机会改进 WCF REST。再次感谢您的帮助!以上是关于如何在 ServiceAuthorizationManager 中设置身份/用户名?的主要内容,如果未能解决你的问题,请参考以下文章
如何在异步任务中调用意图?或者如何在 onPostExecute 中开始新的活动?