簡體   English   中英

無法獲取WCF基本身份驗證以對用戶進行身份驗證

[英]Cannot Get WCF Basic authentication to authenticate user

因此,在此階段嘗試完成這項工作后,我現在要向您問很多這個問題。

是的,我經歷了一堆類似於我的堆棧溢出問題,但無濟於事。

我要做的就是通過SSL添加基本身份驗證。 從我翻閱的百萬零四萬四千零七教程中,這似乎確實是一個簡單的任務。

我得到的只是一個彈出對話框,要求我輸入用戶名和密碼,但是即使我將正確的憑據傳遞給它,它也會一次又一次彈出。

我也玩過iis express中的配置設置。 如果我禁用基本身份驗證,則會收到錯誤消息,提示該服務未打開。

我知道我正在金發,但是在我在屏幕上放一個洞之前,這是我的web.config

web.config

<?xml version="1.0"?>
<configuration>

  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5"/>
  </system.web>

  <!--DIAGNOSTICS-->
  <system.diagnostics>
    <trace autoflush="true"/>
    <sources>
      <source name="System.ServiceModel"
              switchValue="Information, ActivityTracing"
              propagateActivity="true">
        <listeners>
          <add name="ServiceModel"
               type="System.Diagnostics.XmlWriterTraceListener"
               initializeData="C:\ServiceModel.svclog" />
        </listeners>
      </source>
      <source name="System.ServiceModel.MessageLogging">
        <listeners>
          <add name="MessageLogging"
               type="System.Diagnostics.XmlWriterTraceListener"
               initializeData="C:\MessageLogging.svclog" />
        </listeners>
      </source>
    </sources>
  </system.diagnostics>

  <system.serviceModel>

    <diagnostics>
      <messageLogging logEntireMessage="True"
                      logMalformedMessages="False"
                      logMessagesAtServiceLevel="True"
                      logMessagesAtTransportLevel="False"
                      maxMessagesToLog="10000"
                      maxSizeOfMessageToLog="10000" />
    </diagnostics>

    <bindings>

      <webHttpBinding>
        <binding name="SSSLayer">
          <security mode="Transport">
            <transport clientCredentialType="Basic"></transport>
          </security>
        </binding>
      </webHttpBinding>
    </bindings>

    <services>
      <service behaviorConfiguration="serviceBehaviour" name="Booky.Machine_SVC">
        <endpoint address="" 
                  behaviorConfiguration="RESTBehaviour" 
                  binding="webHttpBinding"
                  bindingConfiguration="SSSLayer"
                  contract="Booky.IMachine_SVC" />
      </service>
    </services>


    <behaviors>
      <endpointBehaviors>
        <behavior name="RESTBehaviour">
          <webHttp/>
        </behavior>                  
      </endpointBehaviors>

      <serviceBehaviors>
        <behavior name="serviceBehaviour">
          <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="false" 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="Booky.Authentication, Booky" />
          </serviceCredentials>-->
        </behavior>
      </serviceBehaviors>
    </behaviors>


    <protocolMapping>
        <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>    
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <!--
        To browse web app root directory during debugging, set the value below to true.
        Set to false before deployment to avoid disclosing web app folder information.
      -->
    <directoryBrowse enabled="true"/>
  </system.webServer>

</configuration>

認證類別

namespace Booky
{
    public class Authentication : System.IdentityModel.Selectors.UserNamePasswordValidator
    {
        public override void Validate(string UserName, string Password)
        {
            if (UserName == null || Password == null)
            {
                throw new ArgumentNullException("Username or Password is Incorrect");
            }

            if (!(UserName == "wickd" && Password == "OMIG2015"))
            {
                throw new Exception("Invalid Credentials");
            }
        }
    }
}

機器_SVC

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple, IncludeExceptionDetailInFaults=true)]
    public class Machine_SVC : IMachine_SVC
    {
        /// <summary>
        /// Retrieves a serial number
        /// </summary>
        /// <param name="serial"></param>
        /// <returns></returns>
        Machine IMachine_SVC.GetMachine(string serial)
        {
            var res = Machine.GetMachine(serial);

            if (CheckContent(res))
                return res;
            else
                return null;
        }

        /// <summary>
        /// Creates a new machine object
        /// </summary>
        /// <param name="machine"></param>
        void IMachine_SVC.CreateMachine(Machine machine)
        {
            if (!Machine.CreateMachine(machine))
            {
                WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.PreconditionFailed;
                WebOperationContext.Current.OutgoingResponse.StatusDescription = "A serial and status needs to be specified for the machine to be created";
            }
        }

        /// <summary>
        /// This will update the machine information
        /// </summary>
        /// <param name="machine"></param>
        /// <param name="serial"></param>
        void IMachine_SVC.UpdateMachineInfo(Machine machine, string serial)
        {
            var result = Machine.UpdateMachineInfo(machine, serial);

            CheckUpdateCreateResult(result);
        }

        private bool CheckContent(object result, HttpStatusCode code = HttpStatusCode.NotFound)
        {
            if (result != null)
            {
                return true;
            }
            else
            {
                WebOperationContext.Current.OutgoingResponse.StatusCode = code;
                return false;
            }
        }

        private void CheckUpdateCreateResult(ReturnCodes result)
        {
            if (result == ReturnCodes.DATASETINCOMPLETE)
            {
                WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.PreconditionFailed;
                WebOperationContext.Current.OutgoingResponse.StatusDescription = "Not all the required attributes were provided. You need a serial, bitlocked, model and type attribute";
            }

            if (result == ReturnCodes.INVALIDDATA)
            {
                WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.PreconditionFailed;
                WebOperationContext.Current.OutgoingResponse.StatusDescription = "The serial provided in the url is not the same as in the json object";
            }

            if (result == ReturnCodes.NOTEXIST)
            {
                WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.NotFound;
                WebOperationContext.Current.OutgoingResponse.StatusDescription = "The serial you have provided does not exist yet! You need to create it first!";
            }
        }

        /// <summary>
        /// Retrieves a list of owners of the machine with the owners organized from last login first
        /// </summary>
        /// <param name="serial"></param>
        /// <returns></returns>
        List<MachineOwner> IMachine_SVC.GetMachineOwners(string serial)
        {
            var owners = MachineOwners.GetOwners(serial);

            if (CheckContent(owners))
                return owners;
            else
                return null;
        }

        /// <summary>
        /// Adds a new Machine owner. Only adds the serial, nothing else
        /// </summary>
        /// <param name="owner"></param>
        /// <param name="serial"></param>
        void IMachine_SVC.AddMachineOwner(MachineOwner owner, string serial)
        {
            var result = MachineOwners.AddOwner(owner, serial);

            CheckUpdateCreateResult(result);
        }

        /// <summary>
        /// Retrieves the statuses for a particular machine
        /// </summary>
        /// <param name="serial"></param>
        /// <returns></returns>
        List<MachineStatus> IMachine_SVC.GetMachineStatuses(string serial)
        {
            var statuses = MachineStatus.GetStatusList(serial);

            if (CheckContent(statuses))
                return statuses;
            else
                return null;
        }

        /// <summary>
        /// This will update a machine status.
        ///     - Checks that the operation is valid compared to last machine login
        ///     - Checks that status is indeed valid
        /// </summary>
        /// <param name="serial"></param>
        /// <param name="status"></param>
        void IMachine_SVC.UpdateMachineStatus(string serial, MachineStatus status)
        {
            var result = MachineStatus.UpdateStatus(serial, status);

            CheckUpdateCreateResult(result);
        }

        /// <summary>
        /// Retrieves a list of all machines ever registered on the network
        /// </summary>
        /// <returns></returns>
        List<Machine> IMachine_SVC.GetAllMachines()
        {
            var machines = Machines.GetAllMachines();

            if (CheckContent(machines))
                return machines;
            else
                return null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="search"></param>
        void IMachine_SVC.CreateMachineSearch(MachineSearch search)
        {
            throw new NotImplementedException();
        }
    }

IMachine_SVC

[ServiceContract]
    public interface IMachine_SVC
    {
        [OperationContract]
        [WebInvoke(Method="GET",
            RequestFormat=WebMessageFormat.Json,
            BodyStyle=WebMessageBodyStyle.Bare,
            ResponseFormat=WebMessageFormat.Json,
            UriTemplate="/machine/{serial}")]
        Machine GetMachine(string serial);

        [OperationContract]
        [WebInvoke(Method = "PUT",
            RequestFormat = WebMessageFormat.Json,
            BodyStyle = WebMessageBodyStyle.Bare,
            ResponseFormat = WebMessageFormat.Json,
            UriTemplate = "/machine/{serial}")]
        void UpdateMachineInfo(Machine machine, string serial);

        [OperationContract]
        [WebInvoke(Method = "POST",
            RequestFormat = WebMessageFormat.Json,
            BodyStyle = WebMessageBodyStyle.Bare,
            ResponseFormat = WebMessageFormat.Json,
            UriTemplate = "/machine")]
        void CreateMachine(Machine machine);

        [OperationContract]
        [WebInvoke(Method = "GET",
            RequestFormat = WebMessageFormat.Json,
            BodyStyle = WebMessageBodyStyle.Bare,
            ResponseFormat = WebMessageFormat.Json,
            UriTemplate = "/machine/{serial}/owners")]
        List<MachineOwner> GetMachineOwners(string serial);

        [OperationContract]
        [WebInvoke(Method = "POST",
            RequestFormat = WebMessageFormat.Json,
            BodyStyle = WebMessageBodyStyle.Bare,
            ResponseFormat = WebMessageFormat.Json,
            UriTemplate = "/machine/{serial}/owner")]
        void AddMachineOwner(MachineOwner owner, string serial);

        [OperationContract]
        [WebInvoke(Method = "GET",
            RequestFormat = WebMessageFormat.Json,
            BodyStyle = WebMessageBodyStyle.Bare,
            ResponseFormat = WebMessageFormat.Json,
            UriTemplate = "/machine/{serial}/statuses")]
        List<MachineStatus> GetMachineStatuses(string serial);

        [OperationContract]
        [WebInvoke(Method = "PUT",
            RequestFormat = WebMessageFormat.Json,
            BodyStyle = WebMessageBodyStyle.Bare,
            ResponseFormat = WebMessageFormat.Json,
            UriTemplate = "/machine/{serial}/status")]
        void UpdateMachineStatus(string serial, MachineStatus status);

        [OperationContract]
        [WebInvoke(Method = "GET",
            RequestFormat = WebMessageFormat.Json,
            BodyStyle = WebMessageBodyStyle.Bare,
            ResponseFormat = WebMessageFormat.Json,
            UriTemplate = "/machines")]
        List<Machine> GetAllMachines();

        [OperationContract]
        [WebInvoke(Method = "POST",
            RequestFormat = WebMessageFormat.Json,
            BodyStyle = WebMessageBodyStyle.Bare,
            ResponseFormat = WebMessageFormat.Json,
            UriTemplate = "/machines")]
        void CreateMachineSearch(MachineSearch search);
    }

如果要在IIS中托管此服務,則會遇到問題,因為自定義密碼驗證程序不是為IIS托管構建的。 與獨立托管服務器一起使用時,它將可以很好地工作。 請參閱此處的詳細信息其中Phil明確指出: 請注意,僅托管服務支持此功能

您可以通過擴展“ ServiceAuthorizationManager”類來實現此目的,而不必使用自定義驗證器。

public class RestAuthorizationManager: ServiceAuthorizationManager  
{  
    protected override bool CheckAccessCore(OperationContext operationContext)
    {
        //Extract the Authorization header, and parse out the credentials converting the Base64 string:
        var authHeader = WebOperationContext.Current.IncomingRequest.Headers["Authorization"];
        if ((authHeader != null) && (authHeader != string.Empty))
        {
            var svcCredentials = System.Text.ASCIIEncoding.ASCII
                    .GetString(Convert.FromBase64String(authHeader.Substring(6)))
                    .Split(':');
            var user = new { Name = svcCredentials[0], Password = svcCredentials[1] };
            if ((user.Name == "user1" && user.Password == "test"))
            {
                //User is authrized and originating call will proceed
                return true;
            }
            else
            {
                //not authorized
                return false;
            }
        }
        else
        {
            //No authorization header was provided, so challenge the client to provide before proceeding:
            WebOperationContext.Current.OutgoingResponse.Headers.Add("WWW-Authenticate: Basic realm=\"MyWCFService\"");
            //Throw an exception with the associated HTTP status code equivalent to HTTP status 401
            throw new WebFaultException("Please provide a username and password", HttpStatusCode.Unauthorized);
        }
    }
}

RestAuthorizationManager添加到服務行為。

<serviceBehaviors>  
  <behavior name="ServiceBehavior">  
    <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>  
    <serviceDebug includeExceptionDetailInFaults="true"/>  
    <serviceAuthorization   
      serviceAuthorizationManagerType  
        =" WcfWebHttpIISHostingSample.RestAuthorizationManager, WcfWebHttpIISHostingSample"/>  
  </behavior>  
</serviceBehaviors>  

這應該可以幫助您。

我編寫了完整的指南 ,以使用SSL基本身份驗證創建和保護WCF REST服務。 即使安全行為也是WCF REST和webhttp行為的擴展的一部分,您也可能要經歷一下。 請參閱CustomAuthenticationBehavior詳細信息。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM