public class UserIdentity : GenericIdentity { private readonly bool m_isAuthenticated; public string Password { get; } public override bool IsAuthenticated { get { return base.IsAuthenticated && m_isAuthenticated; } } public UserIdentity(IIdentity existingIdentity,string password) : base(existingIdentity.Name) { m_isAuthenticated = existingIdentity.IsAuthenticated; Password = password; } }
我试图通过以下方式转发密码,所有这些都没有运气:
>实现自定义UserNamePasswordValidator,它可以访问密码,但只能处理身份验证.没有办法创建或修改IIdentity.
>创建自定义ServiceCredentials as described in this article,当绑定安全性设置为传输时,它可以正常工作.然而,这需要与服务的HTTPS连接,这对我来说是不可行的,因为传输级安全性由上游的负载平衡器处理.服务本身必须是HTTP.因此,安全性设置为TransportCredentialOnly.这样做的结果是自定义ServiceCredentials类永远不会被WCF运行时初始化(与安全性设置为Transport不同).
>直接在app.config中配置自定义AuthorizationPoliciy.在这种情况下,自定义授权策略已初始化,但在密码信息已不再可用的位置调用(当使用ServiceCredentials初始化时,这不是问题,因为它在初始化期间确实接收到密码).
自定义ServiceCredentials和AuthorizationPolicy实现如下:
public class UserServiceCredentials : ServiceCredentials { public UserServiceCredentials() { } protected UserServiceCredentials(ServiceCredentials other) : base(other) { } protected override ServiceCredentials CloneCore() { return new UserServiceCredentials(this); } public override SecurityTokenManager CreateSecurityTokenManager() { if (UserNameAuthentication.UserNamePasswordValidationMode == UserNamePasswordValidationMode.Custom) { return new UserSecurityTokenManager(this); } return base.CreateSecurityTokenManager(); } } internal class UserSecurityTokenManager : ServiceCredentialsSecurityTokenManager { public UserSecurityTokenManager(UserServiceCredentials credentials) : base(credentials) { } public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement,out SecurityTokenResolver outOfBandTokenResolver) { outOfBandTokenResolver = null; UserNamePasswordValidator validator = ServiceCredentials.UserNameAuthentication.CustomUserNamePasswordValidator; return new UserSecurityTokenAuthenticator(validator ?? new Validator()); } } internal class UserSecurityTokenAuthenticator : CustomUserNameSecurityTokenAuthenticator { public UserSecurityTokenAuthenticator(UserNamePasswordValidator validator) : base(validator) { } protected override ReadOnlyCollection<IAuthorizationPolicy> ValidateUserNamePasswordCore(string userName,string password) { ReadOnlyCollection<IAuthorizationPolicy> currentPolicies = base.ValidateUserNamePasswordCore(userName,password); List<IAuthorizationPolicy> policies = new List<IAuthorizationPolicy>(currentPolicies); policies.Add(new UserAuthorizationPolicy(userName,password)); return policies.AsReadOnly(); } } public class UserAuthorizationPolicy : IAuthorizationPolicy { private string m_userName; private string m_password; //Called when used with service credentials public UserAuthorizationPolicy(string userName,string password) { m_userName = userName; m_password = password; } //Called when directly configured in the config file public UserAuthorizationPolicy() { } public Claimset Issuer { get; } = Claimset.System; public string Id { get; } = Guid.NewGuid().ToString(); public bool Evaluate(EvaluationContext evaluationContext,ref object state) { bool hasIdentities = evaluationContext.Properties.TryGetValue("Identities",out object rawIdentities); if (rawIdentities is IList<IIdentity> identities) { var identityQry = from id in identities where String.Equals(id.Name,m_userName,StringComparison.OrdinalIgnoreCase) select id; IIdentity identity = identityQry.FirstOrDefault(); if (identity == null) { return false; } UserIdentity userIdentity = new UserIdentity(identity,m_password); identities.Remove(identity); identities.Add(userIdentity); evaluationContext.Properties["PrimaryIdentity"] = userIdentity; evaluationContext.Properties["Principal"] = new GenericPrincipal(userIdentity,null); return true; } else { return false; } } }
我正在使用的app.config是这样的:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <system.serviceModel> <bindings> <webHttpBinding> <binding name="TestBinding"> <security mode="TransportCredentialOnly"> <transport clientCredentialType="Basic"> </transport> </security> </binding> </webHttpBinding> </bindings> <behaviors> <serviceBehaviors> <behavior name="TestServiceBehavior"> <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="true"/> <!-- Custom service credentials: Works when binding security is Transport. Is not invoked when security TransportCredentialOnly--> <serviceCredentials type="WcfTestServices.UserServiceCredentials,WcfTestServices"> <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WcfTestServices.Validator,WcfTestServices"/> </serviceCredentials> <serviceAuthorization principalPermissionMode="Custom"> <!-- Authorization policy works when binding security is TransportCredentialOnly,but has no password --> <authorizationPolicies> <add policyType="WcfTestServices.UserAuthorizationPolicy,WcfTestServices"/> </authorizationPolicies> </serviceAuthorization> </behavior> </serviceBehaviors> <endpointBehaviors> <behavior name="TestEndpointBehavior"> <webHttp/> </behavior> </endpointBehaviors> </behaviors> <services> <service name="WcfTestServices.TestService" behaviorConfiguration="TestServiceBehavior"> <endpoint address="" binding="webHttpBinding" bindingConfiguration="TestBinding" behaviorConfiguration="TestEndpointBehavior" contract="WcfTestServices.ITestService"/> <host> <baseAddresses> <add baseAddress="http://localhost:12700/"/> </baseAddresses> </host> </service> </services> </system.serviceModel> </configuration>
解决方法
服务方
创建一个实现IDispatchMessageInspector的类
public class IdentityMessageInspector : IdispatchMessageInspector { public object AfterReceiveRequest(ref Message request,System.ServiceModel.IClientChannel channel,System.ServiceModel.InstanceContext instanceContext) { var messageProperty = (HttpRequestMessageProperty) OperationContext.Current.IncomingMessageProperties[HttpRequestMessageProperty.Name]; string cookie = messageProperty.Headers.Get("Set-Cookie"); if (cookie == null) // Check for another Message Header - SL applications { cookie = messageProperty.Headers.Get("Cookie"); } if (cookie == null) cookie = string.Empty; //You can get the credentials from here,do something to them,on the service side }
注意,根据链接的MSDN链接,行OperationContext.IncomingMessageProperties Property可用于获取消息的incomming消息属性,
Use this property to inspect or modify the message properties for a request message in a service operation or a reply message in a client proxy
,然后创建一个实现IServiceBehvaior的类,例如
public class InterceptorBehaviorExtension : BehaviorExtensionElement,IServiceBehavior,
你需要实现界面,并修改
ApplydispatchBehavior
方法如下
public void ApplydispatchBehavior(ServiceDescription serviceDescription,System.ServiceModel.ServiceHostBase serviceHostBase) { foreach (Channeldispatcher dispatcher in serviceHostBase.Channeldispatchers) { foreach (var endpoint in dispatcher.Endpoints) { endpoint.dispatchRuntime.MessageInspectors.Add(new IdentityMessageInspector()); } } }
,然后procceed将其添加到您的web.config / app.config文件中
<extensions> <behaviorExtensions> <add name="interceptorBehaviorExtension" type="test.InterceptorBehaviorExtension,test,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"/> </behaviorExtensions> </extensions>
,然后包括该行
<interceptorBehaviorExtension />
在您的行为元素标记中.
客户
在客户端,您需要使用IClientMessageInspector修改httpmessage并修改
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request,
System.ServiceModel.IClientChannel channel)
接下来,将其添加到实现IEndpointBehavior的类中,
internal class InterceptorBehaviorExtension : BehaviorExtensionElement,IEndpointBehavior
并修改
public void ApplyClientBehavior(ServiceEndpoint endpoint,System.ServiceModel.dispatcher.ClientRuntime clientRuntime) { clientRuntime.MessageInspectors.Add(new CookieMessageInspector()); }
方法,然后将上面的代码添加到WCF客户端代码中的端点行为列表中,
虽然我想你可以使用HttpClient或WebClient添加代码,并在连接到服务时使用它来提供凭据.
更新:
var messageProperty = (HttpRequestMessageProperty)OperationContext.Current .IncomingMessageProperties[HttpRequestMessageProperty.Name];
这允许您访问授权标头,如下所示:
string authorization = message.Headers.Get("Authorization");
由于OperationContext可从服务本身读取,因此可以直接从服务读取和解析授权数据.在基本身份验证的情况下,这包括用户名和密码.不需要消息检查器(尽管您需要在验证时忽略密码的其他UserNamePasswordValidator).
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。