![](/img/trans.png)
[英]AngularJs application unable to authenticate user login with Wcf Service
[英]WCF: authenticate user before executing some service operation
我的问题如下。
客户端通过Web界面(HTTP)与我的WCF服务进行交互。 某些服务操作要求客户端通过提供用户名和密码进行身份验证。 为了简单起见,我们假设这些信息是通过查询字符串参数传递的(或在HTTP Auth中的Authorization标头中)传递的。
例如,可以通过http://myhost.com/myservice/myop?user=xxx&password=yyy调用服务操作
由于多个服务操作需要这种身份验证,因此我想将身份验证代码从单个操作中分解出来。
通过环顾四周,我了解了服务行为并提出了以下代码:
public class MyAuthBehaviorAttribute : Attribute, IServiceBehavior, IDispatchMessageInspector {
/********************/
/* IServiceBehavior */
public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
System.ServiceModel.ServiceHostBase serviceHostBase) {
// It’s called right after the runtime was initialized
foreach (ChannelDispatcher chDisp in serviceHostBase.ChannelDispatchers) {
foreach (EndpointDispatcher epDisp in chDisp.Endpoints) {
epDisp.DispatchRuntime.MessageInspectors.Add(new MyAuthBehaviorAttribute());
}
}
}
/*...*/
/*****************************/
/* IDispatchMessageInspector */
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request,
System.ServiceModel.IClientChannel channel,
System.ServiceModel.InstanceContext instanceContext) {
object correlationState = null;
var prop = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
var parts = HttpUtility.ParseQueryString(prop.QueryString);
string user = parts["user"];
string password = parts["password"];
if (AuthenticateUser(user,password)) {
// ???????????????????????????
}
else {
throw new Exception("...");
}
return correlationState;
}
/*...*/
}
然后,通过以下方式对服务进行注释
[MyAuthBehavior]
public class Service : IContract
{
// implementation of the IContract interface
}
现在,我设法在执行任何服务操作之前执行我的行为。 但是,我有以下问题:
关于最后一点,我看了看IOperationBehavior,但是在那种情况下,我只能附加IParameterInspectors而不附加IDispatchMessageInspectors。 这是不希望的,因为我可能需要查看消息标头,例如,当我决定在支持HTTP基本身份验证时考虑考虑Authorization HTTP标头时。
作为一个相关的问题,我还想问一下您对我的方法的看法,以及是否有更好的(简单的)方法。
我建议将不需要身份验证的所有方法隔离到它们自己的服务中。 例如:
IPublicService.cs和PublicService.svc
以及需要认证的:
IPrivateService.cs和PrivateService.svc
对于PrivateService.svc的身份验证,我建议使用MessageCredential并将该用户名用于该绑定:
<wsHttpBinding>
<binding name="wsHttpEndpointBinding" closeTimeout="00:30:00" openTimeout="00:30:00" receiveTimeout="00:30:00" sendTimeout="00:30:00" maxReceivedMessageSize="500000000">
<readerQuotas maxDepth="500000000" maxStringContentLength="500000000" maxArrayLength="500000000" maxBytesPerRead="500000000" maxNameTableCharCount="500000000" />
<security mode="MessageCredential">
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
并添加一个自定义的用户名验证器类:
public class CustomUserNameValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
if (username!="test" && password!="test")
{
throw new FaultException("Unknown username or incorrect password.");
}
return;
}
}
并在web.config中注册您的课程:
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpsGetEnabled="true" />
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="MyProgram.CustomUserNameValidator,MyProgram" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
经过研究,这是我当前的解决方案。
首先,我使用自定义属性标记我的服务操作:
public class RequiresAuthAttribute : Attribute { }
public partial class MyService {
[RequiresAuth]
WebGet(UriTemplate = "...")]
public Tresult MyServiceOperation(...){ ... }
然后,我检索此信息来确定是否必须执行该行为
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request,
System.ServiceModel.IClientChannel channel,
System.ServiceModel.InstanceContext instanceContext) {
if(AuthenticationNeeded()){ ... }
}
public bool AuthenticationNeeded() {
// 1) Get the current operation's description
OperationDescription od = GetOperationDescription(OperationContext.Current);
// 2) Check if the service operation is annotated with the [RequiresAuth] attribute
Type contractType = od.DeclaringContract.ContractType;
object[] attr = contractType.GetMethod(od.Name).GetCustomAttributes(typeof(RequiresAuthAttribute), false);
if (attr == null || attr.Length == 0) return false;
return true;
}
// See http://www.aspnet4you.com/wcf/index.php/2013/01/30/message-interception-auditing-and-logging-at-wcf-pipeline/
private OperationDescription GetOperationDescription(OperationContext operationContext) {
OperationDescription od = null;
string bindingName = operationContext.EndpointDispatcher.ChannelDispatcher.BindingName;
string methodName;
if (bindingName.Contains("WebHttpBinding")) {
//REST request
methodName = (string)operationContext.IncomingMessageProperties["HttpOperationName"];
}
else {
//SOAP request
string action = operationContext.IncomingMessageHeaders.Action;
methodName = operationContext.EndpointDispatcher.DispatchRuntime.Operations.FirstOrDefault(o => o.Action == action).Name;
}
EndpointAddress epa = operationContext.EndpointDispatcher.EndpointAddress;
ServiceDescription hostDesc = operationContext.Host.Description;
ServiceEndpoint ep = hostDesc.Endpoints.Find(epa.Uri);
if (ep != null) {
od = ep.Contract.Operations.Find(methodName);
}
return od;
}
服务行为将作为
OperationContext.Current.IncomingMessageProperties.Add("myInfo", myInfo);
而服务操作将
object myInfo = null;
OperationContext.Current.IncomingMessageProperties.TryGetValue("myInfo", out myInfo);
也可以通过以下方式为服务操作的参数设置一个值
WebOperationContext.Current.IncomingRequest.UriTemplateMatch.BoundVariables["MYPARAM"] = myParam;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.