问题
I have a WCF Service Application which server to different type of clients. While calling the service methods I want to send some spesific information within the service header.
While using a newer version of .NET Framework I can handle the situation using MessageHeader. Since the consumer can see the service as WCF Service there is no problem.
[DataContract]
public class AuthToken
{
[DataMember]
public string Username { get; set; }
[DataMember]
public string Password { get; set; }
}
Client-side:
AuthWCFSvc.Service1Client client = new AuthWCFSvc.Service1Client();
using (OperationContextScope scope = new OperationContextScope(client.InnerChannel))
{
SvcAuthClient.AuthWCFSvc.AuthToken token = new AuthWCFSvc.AuthToken();
token.Username = "wcfuser";
token.Password = "wcfpass";
MessageHeader<SvcAuthClient.AuthWCFSvc.AuthToken> header = new MessageHeader<SvcAuthClient.AuthWCFSvc.AuthToken>(token);
var untyped = header.GetUntypedHeader("Identity", "http://www.my-website.com");
OperationContext.Current.OutgoingMessageHeaders.Add(untyped);
client.TestHeader();
}
Server-side :
MessageHeaders headers = OperationContext.Current.IncomingMessageHeaders;
AuthToken token = headers.GetHeader<AuthToken>("Identity", "http://www.my-website.com");
But there are Windows Mobile 6 devices with .NET Framework 3.5 Compact Edition, using these services as well. Due to technological restrictions, they only can process the WCF services as Web Services.
If a client is consuming a WCF service as Web Service how can add spesific header information and resolve the header information at the service method?
回答1:
As you know, on .NET CF 3.5 you can only use WCF as standard webservice on SOAP way. Therefore, you can't use any of WCF native security resources.
I figured out how to use Basic Http Authentication, configuring client and server sides and I can explain it as follows:
Client Side
On client side (on your device with .Net CF 3.5), its easy. Just inform your credentials configuring your clientServiceProxy by using:
var service = new YourServiceNamespace.YourService();
service.Credentials = new NetworkCredential("login", "12345");
service.PreAuthenticate = true;
This will make your client deal with the "WWW-Authenticate" header from the server response and pass your credentials automatically via the response header "Authorization: Basic".
Server Side
On the WCF configuration on your web.config, you should configure security just for Transport, and use HTTPS (this is enough to protect your message from sniffers).
<basicHttpBinding>
<binding>
<security mode="Transport">
<transport clientCredentialType="None" />
</security>
</binding>
</basicHttpBinding>
Now, as WCF doesn't has native support for Basic Http Authentication, we have to use a custom HTTP module to deal with it.
public class BasicHttpAuthentication : IHttpModule
{
public delegate bool AuthenticateDelegate( string username, string password );
public static AuthenticateDelegate AuthenticateMethod;
public void Dispose() { }
public void Init( HttpApplication application )
{
application.AuthenticateRequest += this.OnAuthenticateRequest;
application.EndRequest += this.OnEndRequest;
}
private void DenyAccess( HttpApplication app )
{
app.Response.StatusCode = 401;
app.Response.StatusDescription = "Access Denied";
// Write to response stream as well, to give user visual
// indication of error during development
app.Response.Write( "401 Access Denied" );
app.CompleteRequest();
}
private void OnAuthenticateRequest( object source, EventArgs eventArgs )
{
if ( AuthenticateMethod == null )
return;
var app = ( HttpApplication )source;
//the Authorization header is checked if present
string authHeader = app.Request.Headers["Authorization"];
if ( !string.IsNullOrEmpty( authHeader ) )
{
string authStr = app.Request.Headers["Authorization"];
if ( string.IsNullOrEmpty( authStr ) )
return; // No credentials; anonymous request
authStr = authStr.Trim();
if ( authStr.IndexOf( "Basic", 0 ) != 0 )
// header is not correct...we'll pass it along and
// assume someone else will handle it
return;
authStr = authStr.Trim();
string encodedCredentials = authStr.Substring( 6 );
byte[] decodedBytes = Convert.FromBase64String( encodedCredentials );
string s = new ASCIIEncoding().GetString( decodedBytes );
string[] userPass = s.Split( new[] { ':' } );
string username = userPass[0];
string password = userPass[1];
if ( !AuthenticateMethod( username, password ) )
this.DenyAccess( app );
}
else
{
app.Response.StatusCode = 401;
app.Response.End();
}
}
private void OnEndRequest( object source, EventArgs eventArgs )
{
//the authorization header is not present
//the status of response is set to 401 and it ended
//the end request will check if it is 401 and add
//the authentication header so the client knows
//it needs to send credentials to authenticate
if ( HttpContext.Current.Response.StatusCode == 401 )
{
HttpContext context = HttpContext.Current;
context.Response.StatusCode = 401;
context.Response.AddHeader( "WWW-Authenticate", "Basic Realm=\"Please inform your credentials\"" );
}
}
}
To enable the HTTP module, add the following to your web.config file in the system.webServer section:
<system.webServer>
<modules>
<add name="BasicHttpAuthentication"
type="BasicHttpAuthentication, YourAssemblyName"/>
</modules>
Now you have to inform to the module a Function to use for validating the credentials from the client. You can see that there's a static delegate inside the module called "AuthenticateMethod", so you can inform a function on your Application_Start of your global.asax:
BasicHttpAuthentication.AuthenticateMethod = ( username, password ) => username == "login" && password == "12345";
来源:https://stackoverflow.com/questions/24409794/adding-custom-message-header-to-a-wcf-service-which-is-consumed-from-windows-mob