WCF Rest 自托管证书安全服务 returns 401 未授权
WCF Rest self hosted certificate secured service returns 401 unauthorized
我想创建一个 WCF 控制台自托管 - 使用证书的服务器端身份验证 - 休息服务。
我在实际调用服务时遇到问题,因为我总是收到 401 未经授权的响应。
因为这是一个 "One-way" 身份验证,其中服务向客户端标识自己,为什么我作为客户端应用程序总是会收到 401 未自动响应(好像客户端需要向服务访问其资源?)
谁能帮我查明我哪里出错了,以及如何让我的客户服务通信最终正常工作?
简单服务合同:
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebGet(UriTemplate = "Students", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
List<Student> GetStudentDetails();
// TODO: Add GetMethod with parameter
[OperationContract]
[WebGet(UriTemplate = "Student/{id}", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
Student GetStudentWithId(string id);
//TODO: add one post method here
[OperationContract]
[WebInvoke(Method="POST", UriTemplate = "Student/New", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
void NewStudent(Stream stream);
//TODO: add one post method here
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "Student/NewS", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
void NewStudentS(Student stream);
}
// Use a data contract as illustrated in the sample below to add composite types to service operations.
[DataContract]
public class Student
{
[DataMember]
public int ID
{
get;
set;
}
[DataMember]
public string Name
{
get;
set;
}
}
服务实施:
public class Service1 : IService1
{
public List<Student> GetStudentDetails()
{
return new List<Student>() { new Student() { ID = 1, Name = "Goran" } };
}
public Student GetStudentWithId(string id)
{
return new Student() { ID = Int32.Parse(id), Name = "Ticbra RanGo" };
}
public void NewStudent(Stream stream)
{
using(stream)
{
// convert Stream Data to StreamReader
StreamReader reader = new StreamReader(stream);
var dataString = reader.ReadToEnd();
Console.WriteLine(dataString);
}
}
public void NewStudentS(Student student)
{
Console.WriteLine(student.Name);
}
}
我的控制台应用程序运行服务:
static void Main(string[] args)
{
Uri httpUrl = new Uri("https://localhost:8080/TestService");
using (WebServiceHost host = new WebServiceHost(typeof(Service1)))
{
// Create the binding.
WSHttpBinding binding = new WSHttpBinding();
binding.Name = "binding1";
binding.Security.Mode = SecurityMode.Transport;
host.AddServiceEndpoint(typeof(IService1), binding, httpUrl/*"rest"*/);
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
host.Description.Behaviors.Add(smb);
//Add host certificate to service wcf for identification
host.Credentials.ServiceCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.My,
X509FindType.FindBySubjectName,
"localhost");
host.Open();
foreach (ServiceEndpoint se in host.Description.Endpoints)
Console.WriteLine("Service is host with endpoint " + se.Address);
Console.WriteLine("Host is running... Press < Enter > key to stop");
Console.ReadLine();
host.Close();
}
//Console.WriteLine("ASP.Net : " + ServiceHostingEnvironment.AspNetCompatibilityEnabled);
Console.WriteLine("Host is running... Press < Enter > key to stop");
Console.ReadLine();
}
请注意,我通过 KeyStore Explorer 创建根证书和子证书,并将它们适当地放置在 windows 上的个人和可信根证书中。
我通过CMD把服务器证书映射到了8080端口
我使用的客户端是 SOAPUI,以及我手动编码的客户端。
客户代码:
WebRequest request = HttpWebRequest.Create(urlTextBox.Text);
var webResponse = request.GetResponse();
using (Stream dataStream = webResponse.GetResponseStream())
{
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader(dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd();
// Display the content.
Console.WriteLine(responseFromServer);
HttpResonseTextBox.Text = responseFromServer;
}
致以最诚挚的问候,并在此先感谢您
您正在将证书映射到端口 8080 以使 https
协议正常工作。
直到这一刻都很好。
但是 401 错误意味着服务需要来自客户端的一些凭据(如果有的话会引发 401 错误)
请尝试删除(或评论)SetCertificate
方法调用,如下所示
//Add host certificate to service wcf for identification
//host.Credentials.ServiceCertificate.SetCertificate(
// StoreLocation.LocalMachine,
// StoreName.My,
// X509FindType.FindBySubjectName,
// "localhost");
并尝试它是否有效。只是为了检查
带证书的 Wcf 传输安全性也要求客户端指定证书 like it is documented。
我不确定您是否可以使用带有 HttpWebRequest
的 soap 协议通过证书身份验证来使用 wcf 服务。它需要使用 SetCertificate
方法的 wcf 客户端:
// The client must specify a certificate trusted by the server.
cc.ClientCredentials.ClientCertificate.SetCertificate(
StoreLocation.CurrentUser,
StoreName.My,
X509FindType.FindBySubjectName,
"contoso.com");
(这是文档中的示例)
WSHttpBinding binding = new WSHttpBinding();
binding.Name = "binding1";
binding.Security.Mode = SecurityMode.Transport;
以上代码以windows认证方式作为对客户端的认证方式
//this is the default value unless we specify it manually.
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
因此,我们应该通过提供 windows 凭据在客户端调用它。
另外这种WCF服务不叫RestAPI,叫SOAP web service。我们通常使用客户端代理来调用它。
https://docs.microsoft.com/en-us/dotnet/framework/wcf/accessing-services-using-a-wcf-client
然后设置 windows 凭据并调用该方法。
ServiceReference1.ServiceClient client = new ServiceReference1.ServiceClient();
//these are windows accounts on the server-side.
client.ClientCredentials.Windows.ClientCredential.UserName = "administrator";
client.ClientCredentials.Windows.ClientCredential.Password = "123456";
var result = client.Test();
Console.WriteLine(result);
如果我们要创建rest服务,请使用Webhttpbinding来创建服务。
WebHttpBinding binding = new WebHttpBinding();
binding.Security.Mode = WebHttpSecurityMode.Transport;
//this is default value.
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
同样,我们需要将证书绑定到特定端口,以使传输层安全可用。
netsh http add sslcert ipport=0.0.0.0:8000 certhash=0000000000003ed9cd0c315bbb6dc1c08da5e6 appid={00112233-4455-6677-8899-AABBCCDDEEFF}
Netsh Http 命令。
https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-configure-a-port-with-an-ssl-certificate
因此,验证客户端的安全模式是HttpClientCredentialType.None
。我们不需要在客户端提供 windows 凭据。
如果有什么我可以帮忙的,请随时告诉我。
我想创建一个 WCF 控制台自托管 - 使用证书的服务器端身份验证 - 休息服务。
我在实际调用服务时遇到问题,因为我总是收到 401 未经授权的响应。
因为这是一个 "One-way" 身份验证,其中服务向客户端标识自己,为什么我作为客户端应用程序总是会收到 401 未自动响应(好像客户端需要向服务访问其资源?)
谁能帮我查明我哪里出错了,以及如何让我的客户服务通信最终正常工作?
简单服务合同:
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebGet(UriTemplate = "Students", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
List<Student> GetStudentDetails();
// TODO: Add GetMethod with parameter
[OperationContract]
[WebGet(UriTemplate = "Student/{id}", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
Student GetStudentWithId(string id);
//TODO: add one post method here
[OperationContract]
[WebInvoke(Method="POST", UriTemplate = "Student/New", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
void NewStudent(Stream stream);
//TODO: add one post method here
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "Student/NewS", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
void NewStudentS(Student stream);
}
// Use a data contract as illustrated in the sample below to add composite types to service operations.
[DataContract]
public class Student
{
[DataMember]
public int ID
{
get;
set;
}
[DataMember]
public string Name
{
get;
set;
}
}
服务实施:
public class Service1 : IService1
{
public List<Student> GetStudentDetails()
{
return new List<Student>() { new Student() { ID = 1, Name = "Goran" } };
}
public Student GetStudentWithId(string id)
{
return new Student() { ID = Int32.Parse(id), Name = "Ticbra RanGo" };
}
public void NewStudent(Stream stream)
{
using(stream)
{
// convert Stream Data to StreamReader
StreamReader reader = new StreamReader(stream);
var dataString = reader.ReadToEnd();
Console.WriteLine(dataString);
}
}
public void NewStudentS(Student student)
{
Console.WriteLine(student.Name);
}
}
我的控制台应用程序运行服务:
static void Main(string[] args)
{
Uri httpUrl = new Uri("https://localhost:8080/TestService");
using (WebServiceHost host = new WebServiceHost(typeof(Service1)))
{
// Create the binding.
WSHttpBinding binding = new WSHttpBinding();
binding.Name = "binding1";
binding.Security.Mode = SecurityMode.Transport;
host.AddServiceEndpoint(typeof(IService1), binding, httpUrl/*"rest"*/);
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
host.Description.Behaviors.Add(smb);
//Add host certificate to service wcf for identification
host.Credentials.ServiceCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.My,
X509FindType.FindBySubjectName,
"localhost");
host.Open();
foreach (ServiceEndpoint se in host.Description.Endpoints)
Console.WriteLine("Service is host with endpoint " + se.Address);
Console.WriteLine("Host is running... Press < Enter > key to stop");
Console.ReadLine();
host.Close();
}
//Console.WriteLine("ASP.Net : " + ServiceHostingEnvironment.AspNetCompatibilityEnabled);
Console.WriteLine("Host is running... Press < Enter > key to stop");
Console.ReadLine();
}
请注意,我通过 KeyStore Explorer 创建根证书和子证书,并将它们适当地放置在 windows 上的个人和可信根证书中。
我通过CMD把服务器证书映射到了8080端口
我使用的客户端是 SOAPUI,以及我手动编码的客户端。 客户代码:
WebRequest request = HttpWebRequest.Create(urlTextBox.Text);
var webResponse = request.GetResponse();
using (Stream dataStream = webResponse.GetResponseStream())
{
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader(dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd();
// Display the content.
Console.WriteLine(responseFromServer);
HttpResonseTextBox.Text = responseFromServer;
}
致以最诚挚的问候,并在此先感谢您
您正在将证书映射到端口 8080 以使 https
协议正常工作。
直到这一刻都很好。
但是 401 错误意味着服务需要来自客户端的一些凭据(如果有的话会引发 401 错误)
请尝试删除(或评论)SetCertificate
方法调用,如下所示
//Add host certificate to service wcf for identification
//host.Credentials.ServiceCertificate.SetCertificate(
// StoreLocation.LocalMachine,
// StoreName.My,
// X509FindType.FindBySubjectName,
// "localhost");
并尝试它是否有效。只是为了检查
带证书的 Wcf 传输安全性也要求客户端指定证书 like it is documented。
我不确定您是否可以使用带有 HttpWebRequest
的 soap 协议通过证书身份验证来使用 wcf 服务。它需要使用 SetCertificate
方法的 wcf 客户端:
// The client must specify a certificate trusted by the server.
cc.ClientCredentials.ClientCertificate.SetCertificate(
StoreLocation.CurrentUser,
StoreName.My,
X509FindType.FindBySubjectName,
"contoso.com");
(这是文档中的示例)
WSHttpBinding binding = new WSHttpBinding();
binding.Name = "binding1";
binding.Security.Mode = SecurityMode.Transport;
以上代码以windows认证方式作为对客户端的认证方式
//this is the default value unless we specify it manually.
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
因此,我们应该通过提供 windows 凭据在客户端调用它。
另外这种WCF服务不叫RestAPI,叫SOAP web service。我们通常使用客户端代理来调用它。
https://docs.microsoft.com/en-us/dotnet/framework/wcf/accessing-services-using-a-wcf-client
然后设置 windows 凭据并调用该方法。
ServiceReference1.ServiceClient client = new ServiceReference1.ServiceClient();
//these are windows accounts on the server-side.
client.ClientCredentials.Windows.ClientCredential.UserName = "administrator";
client.ClientCredentials.Windows.ClientCredential.Password = "123456";
var result = client.Test();
Console.WriteLine(result);
如果我们要创建rest服务,请使用Webhttpbinding来创建服务。
WebHttpBinding binding = new WebHttpBinding();
binding.Security.Mode = WebHttpSecurityMode.Transport;
//this is default value.
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
同样,我们需要将证书绑定到特定端口,以使传输层安全可用。
netsh http add sslcert ipport=0.0.0.0:8000 certhash=0000000000003ed9cd0c315bbb6dc1c08da5e6 appid={00112233-4455-6677-8899-AABBCCDDEEFF}
Netsh Http 命令。
https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-configure-a-port-with-an-ssl-certificate
因此,验证客户端的安全模式是HttpClientCredentialType.None
。我们不需要在客户端提供 windows 凭据。
如果有什么我可以帮忙的,请随时告诉我。