我在服务器端(在IIS中托管)和Android客户端上有一个WCF 4.0 REST服务。 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
是只读的。我认为需要更复杂的代码。
答案 0 :(得分:3)
经过一些研究后,我找不到在ServiceAuthorizationManager.CheckAccessCore
中设置身份的方法。当用户的身份已经设置(可能为“匿名”(IsAuthenticated
为false
))并且无法再进行更改时,此方法似乎在处理管道中调用太晚。 ServiceAuthorizationManager
用于授权,而不是身份验证,因此实施自定义身份验证是错误的。
我终于找到了解决问题的三种方法:
正如在@TheCodeKing的答案中链接的文章中所解释的那样,使用WCF REST入门套件提供了编写自定义RequestInterceptor
的选项,该自定义RequestInterceptor
可以尽早挂接到管道中,允许访问传入请求并允许根据自定义HTTP标头设置用户的身份。不幸的是,WCF REST入门套件已经过时了(基于WCF 3.5),显然已经放弃了开发。它的一些功能已经合并到WCF 4.0中,但有些功能没有,而Microsoft.ServiceModel.Web
就是其中之一。尽管如此,我现在已经使用了这个解决方案,并将Starter Kit中的CheckAccessCore
程序集混合到我的WCF 4.0解决方案中。经过一些简单的测试后,它似乎工作到目前为止。
如果身份确实不是必需但仅包含用户名,则将用户名写入新请求标头的简单“技巧”/“黑客”可以正常工作(也在// ...
var userName = GetUserNameFromToken(token);
if (!string.IsNullOrEmpty(userName))
{
requestProperty.Headers["X-UserName"] = userName;
return true;
}
// ...
中):< / p>
public void MyServiceOperation()
{
string userName = WebOperationContext.Current.IncomingRequest
.Headers["X-UserName"];
// ...
}
然后在服务方法中:
HttpModule
另一个更低级别的选项是编写一个自定义{{1}},它拦截传入的请求并设置标识。微软模式和版本中的here就是一个例子。实践团队(请参阅本文中间的“HTTP模块代码”示例)。
答案 1 :(得分: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);
}
}