How to implement WebServiceHost Authentication?

后端 未结 3 1293
萌比男神i
萌比男神i 2020-12-21 18:59

I\'m aware that the authentication on the webservicehost class does not adhere fully to authentication standards (returns 403 forbidden rather than prompting for another set

相关标签:
3条回答
  • 2020-12-21 19:39

    Shaydo, you are the best! Thank you! That is what I searched for for weeks! I expanded the vb Code in order to use it with https: VB.NET:

    Public Class AuthenticatedWebServiceHost
       Inherits WebServiceHost
        Public Sub New(ByVal type As Type, ByVal url As Uri, MyThumbprint As String)
            Dim desc As IDictionary(Of String, ContractDescription) = Nothing
            MyBase.InitializeDescription(type, New UriSchemeKeyedCollection())
            MyBase.CreateDescription(desc)
            Dim val = desc.Values.First()
            Dim binding As WebHttpBinding = New WebHttpBinding()
            'binding.Security.Mode = WebHttpSecurityMode.TransportCredentialOnly
            binding.Security.Mode = BasicHttpsSecurityMode.TransportWithMessageCredential
            binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic
            MyBase.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom
            MyBase.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = New CustomUserNamePasswordValidator()
            MyBase.Credentials.ClientCertificate.SetCertificate(System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine, System.Security.Cryptography.X509Certificates.StoreName.My, System.Security.Cryptography.X509Certificates.X509FindType.FindByThumbprint, MyThumbprint)
            MyBase.AddServiceEndpoint(val.ContractType, binding, url)
        End Sub
    
        Public Shared ReadOnly Property UserName As String
            Get
                If OperationContext.Current Is Nothing Then Return Nothing
                If OperationContext.Current.ServiceSecurityContext Is Nothing Then Return Nothing
                If OperationContext.Current.ServiceSecurityContext.PrimaryIdentity Is Nothing Then Return Nothing
                Return OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.Name
            End Get
        End Property
    
        Public Class CustomUserNamePasswordValidator
            Inherits UserNamePasswordValidator
            Public Overrides Sub Validate(ByVal userName As String, ByVal password As String)
                If userName <> password Then
                    Console.WriteLine("Error: Access denied")
                    Throw New SecurityAccessDeniedException()
                End If
            End Sub
        End Class
    End Class
    
    0 讨论(0)
  • 2020-12-21 19:42

    The answer provided by I4V worked like a charm, converted to VB and copied below in case anyone else needs it in future after spending many hours hunting the web.

    The Line to call it is as per the code provided by I4V.

    Dim varWebService = New AuthenticatedWebServiceHost(GetType(MyWebService), New Uri("http://0.0.0.0/"))
    

    VB.Net Code

    Imports System.IdentityModel.Selectors
    Imports System.ServiceModel
    Imports System.ServiceModel.Description
    Imports System.ServiceModel.Security
    Imports System.ServiceModel.Web
    
    Public Class AuthenticatedWebServiceHost
        Inherits WebServiceHost
    
        Public Sub New(ByVal type As Type, ByVal url As Uri)
            Dim desc As IDictionary(Of String, ContractDescription) = Nothing
            MyBase.InitializeDescription(type, New UriSchemeKeyedCollection())
            MyBase.CreateDescription(desc)
            Dim val = desc.Values.First()
            Dim binding As WebHttpBinding = New WebHttpBinding()
            binding.Security.Mode = WebHttpSecurityMode.TransportCredentialOnly
            binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic
            MyBase.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom
            MyBase.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = New CustomUserNamePasswordValidator()
            MyBase.AddServiceEndpoint(val.ContractType, binding, url)
        End Sub
    
        Public Shared ReadOnly Property UserName As String
            Get
                If OperationContext.Current Is Nothing Then Return Nothing
                If OperationContext.Current.ServiceSecurityContext Is Nothing Then Return Nothing
                If OperationContext.Current.ServiceSecurityContext.PrimaryIdentity Is Nothing Then Return Nothing
                Return OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.Name
            End Get
        End Property
    
        Public Class CustomUserNamePasswordValidator
            Inherits UserNamePasswordValidator
    
            Public Overrides Sub Validate(ByVal userName As String, ByVal password As String)
                If userName <> password Then Throw New SecurityAccessDeniedException()
            End Sub
        End Class
    End Class
    
    0 讨论(0)
  • 2020-12-21 20:03

    You can write a custom WebServiceHost by inheriting from it and change some default parameters like below.

    The only change in your code would be

    Dim varWebService = New AuthenticatedWebServiceHost(GetType(MyWebService), New Uri("http://0.0.0.0/"))
    

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.IdentityModel;
    using System.IdentityModel.Selectors;
    using System.ServiceModel;
    using System.ServiceModel.Web;
    using System.ServiceModel.Security;
    using System.ServiceModel.Description;
    
    namespace StackOverflow
    {
        public class AuthenticatedWebServiceHost : WebServiceHost
        {
            public AuthenticatedWebServiceHost(Type type, Uri url)
            {
                IDictionary<string, ContractDescription> desc = null;
                base.InitializeDescription(type, new UriSchemeKeyedCollection());
                base.CreateDescription(out desc);
                var val = desc.Values.First();
    
                WebHttpBinding binding = new WebHttpBinding();
                binding.Security.Mode = WebHttpSecurityMode.TransportCredentialOnly;
                binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
    
                base.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
                base.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new CustomUserNamePasswordValidator();
    
                base.AddServiceEndpoint(val.ContractType, binding, url);
            }
    
            //Possible next question:
            //"How can I get the name of the authenticated user?"
            public static string UserName
            {
                get
                {
                    if (OperationContext.Current == null) return null;
                    if (OperationContext.Current.ServiceSecurityContext == null) return null;
                    if (OperationContext.Current.ServiceSecurityContext.PrimaryIdentity == null) return null;
                    return OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.Name;
                }
            }
    
    
    
            public class CustomUserNamePasswordValidator : UserNamePasswordValidator
            {
                public override void Validate(string userName, string password)
                {
                    //Your logic to validate username/password
                    if (userName != password)
                        throw new SecurityAccessDeniedException();
                }
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题