从SOAP请求主体节点读取ClientCredentials,并根据自定义验证器进行验证

本文关键字:验证 自定义 请求 SOAP 主体 节点 ClientCredentials 读取 | 更新日期: 2023-09-27 17:58:26

我有一个方法GetColors,它将GetColorIdsRQ作为参数并返回GetColorIdsRSGetColorIdsRQ是SOAP请求,GetColorIdsRS是SOAP响应。以下是每一个的实现细节:

GetColorIdsRQ:

[DataContract]
public class GetColorIdsRQ
{
    [DataMember(Name="UserCredentials",Order=0,IsRequired=true)]
    public UserCredentials UserCredentials { get; set; }
}

用户凭据:

[DataContract(Name="UserCredentials")]
public class UserCredentials
{
    [DataMember(Name="Username",Order=0,IsRequired=true)]
    public string UserName { get; set; }
    [DataMember(Name="Password",Order=1,IsRequired=true)]
    public string Password { get; set; }
}

GetColorIdsRS:

[DataContract]
public class GetColorIdsRS
{
    [DataMember(Name = "ColorsIds", IsRequired = true, Order = 0)]
    public List<Color> ColorIds { get; set; }
}

颜色:

[DataContract(Name="Color")]
public class Color    
{
   [DataMember(Name="Code",IsRequired=true,Order=0)]
   public string Code { get; set; }
   [DataMember(Name="Name",IsRequired=true,Order=1)]
   public string Name { get; set; }  
}

这就是方法在接口中的声明方式:

[OperationContract(Name = "GetColorIds")]
GetColorIdsRS GetColorsIds(GetColorIdsRQ req);

这是GetColorIds:的实现

public GetColorIdsRS GetColors(GetColorIdsRQ req)
{
    GetColorIdsRS getColorIdsRS = new GetColorIdsRS();
    List<Color> colorIds = new List<Color>();
    req.UserCredentials.UserName = "test";
    req.UserCredentials.Password = "test";
    //Validate Username and Password
    DataTable dtColorIds = Data.GetDataTable("GetColors");
    foreach (DataRow item in dtColorIds.Rows)
    {
        colorIds.Add(new Color { Name = item["Name"].ToString(), 
                                 Code = item["ColorId"].ToString() });
    }
    getColorIdsRS.ColorIds = colorIds;
    return getColorIdsRS;
}

当我从WCF测试客户端调用GetColors时,请求主体是:

<s:Body>
    <GetColors xmlns="http://test.com/2011/05">
      <req xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
       <UserCredentials>
         <Username>test</Username>
         <Password>test2</Password>
       </UserCredentials>
     </req>
   </GetColors>
 </s:Body>

上面的问题是,我想将UsernamePassword节点用于CustomUserNameValidator。我不知道如何让CustomUserNameValidator重新识别GetColorIdsRQ的用户名和密码节点,以便它可以对此进行验证。如果你在GetColors方法中注意到以上内容,我正在设置:

req.UserCredentials.UserName = "test";
req.UserCredentials.Password = "test";

但这显然没有得到验证。以下是我的CustomUserNamePasswordValidator:的实现

public class CustomUserNameValidator : UserNamePasswordValidator
{
    public override void Validate(string userName, string password)
    {
        // check if the user is not test
        if (userName != "test" || password != "test")
            throw new FaultException("Username and Password Failed");
    }
}

所以基本上,如果我通过:

req.UserCredentials.UserName = "test";
req.UserCredentials.Password = "test2";

CustomUserNameValidator应引发FaultException。

注意,在GetColors方法中,我有一个注释"//验证用户名和密码",我知道我可以做:

CustomUserNameValidator val = new CustomUserNameValidator();
val.Validate(req.UserCredentials.UserName,req.UserCredentials.Password)

上面会调用Validate方法,但我意识到它应该是自动的,然后我必须在每个方法中都这样做。

调用CustomUserNameValidator的唯一方法是在代理代码中设置ClientCredentials,例如:

proxy.ClientCredentials.UserName.UserName = "test;
proxy.ClientCredentials.UserName.Password = "test;

上面的操作会导致调用Validate方法,但如果我无法执行上面的操作,并且我将用户名和密码设置为请求对象的属性,则不会调用Validate,因此我的另一个选择是在每个需要它的操作中都有我自己的Validate方法。如果ClientCredentials失败,将不会调用操作,但在我的情况下,我需要一个操作来调用,然后它返回一个SOAP响应,其中包含一个错误节点,类似于"无效用户名和/或密码",而不是抛出FaultException。

基于以上内容,在我的情况下,最好避免使用CustomUserNamePasswordValidator吗?

如果用户名和密码不能通过代理的客户端凭据设置,而是通过soap请求的主体发送,那么处理这一问题的方法似乎是在操作的基础上处理验证,这是否会影响安全设置(例如,是否有一个clientCredentialType="username")

从SOAP请求主体节点读取ClientCredentials,并根据自定义验证器进行验证

除非深入研究WCF安全管道并实现自定义安全令牌(即使在那时你也会发现这是不可能的)以及与此实现相关的所有内容,否则这是不可行的-这是一项艰巨的工作。

为什么不使用标准的用户名身份验证?

编辑:

如果您想在消息正文中的自定义元素中传递凭据(并且您不会更改安全管道),验证由您决定——它不会被路由到用户名验证器。您必须在操作中执行此操作,或者您可以构建自定义消息检查器(实现IMessageInspector)并用自定义端点行为(IEndpointBehavior)包装它,以便集中验证。