证书
以管理员身份打开VS开发人员命令提示符工具
然后输入如下字符串
makecert -sr localmachine -ssRoot -n CN=GreenWhale -sky exchange -pe -r。
回车返回Success即可。
然后打开“C:\Windows\System32\en-US\certlm.msc” Zh-CN也可 “C:\Windows\System32\zh-CN\certlm.msc”
别打错了不是这个“certmgr.msc”
然后就可以看到证书了,导出证书,软件发布后让客户端安装此证书即可。
服务端
服务端回调接口:
using
System
.
ServiceModel
;
using
System
.
Net
;
namespace
WcfService1
{
public
interface
IServerCallClient
{
[
OperationContract
]
IPEndPoint
ServerRequestClientIP
();
[
OperationContract
]
void
ServerSayMsg
(
string
text);
}
}
服务接口
using
System
;
using
System
.
ServiceModel
;
namespace
WcfService1
{
[
ServiceContract
(CallbackContract
=
typeof
(
IServerCallClient
))]
public
interface
IClientCallServer
{
[
OperationContract
]
void
ClientSayToServer
(
string
text);
[
OperationContract
]
DateTime
ClientRequestDateTime
();
}
}
WCF 服务端代码实现
请注意,服务端回调客户端时 客户端的链接必须没有断开,断开了就无法回调了,
using
System
;
using
System
.
Collections
.
Generic
;
using
System
.
Diagnostics
;
using
System
.
IO
;
using
System
.
Linq
;
using
System
.
Net
;
using
System
.
Runtime
.
Serialization
;
using
System
.
ServiceModel
;
using
System
.
ServiceModel
.
Web
;
using
System
.
Text
;
using
System
.
Timers
;
namespace
WcfService1
{
// 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码、svc 和配置文件中的类名“Service1”。
// 注意: 为了启动 WCF 测试客户端以测试此服务,请在解决方案资源管理器中选择 Service1.svc 或 Service1.svc.cs,然后开始调试。
[
ServiceBehavior
(InstanceContextMode
=
InstanceContextMode
.
Single
)]
public
class
ClientCallServer
:
IClientCallServer
,
IDisposable
{
Timer
timer
=
new
Timer
();
public
ClientCallServer
()
{
timer
.
Elapsed
+=
Timer_Elapsed
;
timer
.
Interval
=
5000
;
timer
.
Start
();
}
private
void
Timer_Elapsed
(
object
sender,
ElapsedEventArgs
e)
{
if
(
ServerCallClient
!=
null
)
{
foreach
(
var
item
in
ServerCallClient
)
{
item
.
ServerSayMsg
(
"SB"
);
}
}
}
public
static
List
<
IServerCallClient
> ServerCallClient
=
new
List
<
IServerCallClient
>();
public
DateTime
ClientRequestDateTime
()
{
return
DateTime
.
Now
;
}
public
void
ClientSayToServer
(
string
text)
{
var
channel
=
OperationContext
.
Current
.
GetCallbackChannel
<
IServerCallClient
>();
ServerCallClient
.
Add
(
channel
);
Debug
.
WriteLine
(
OperationContext
.
Current
.
SessionId
);
Debug
.
WriteLine
(
text
);
}
public
void
Dispose
()
{
ServerCallClient
.
Clear
();
}
}
}
身份认证
实现身份认证需要服务端继承System.IdentityModel.Selectors.UserNamePasswordValidator类。同理需要引用System.IdentityModel.Dll;
namespace
WcfService1
{
///
<summary>
/// WCF身份认证
///
</summary>
public
class
WCFValidator
:
System
.
IdentityModel
.
Selectors
.
UserNamePasswordValidator
{
///
<summary>
/// 身份及密码验证
///
</summary>
///
<param
name
=
"userName"
></param>
///
<param
name
=
"password"
></param>
public
override
void
Validate
(
string
userName,
string
password)
{
if
(
userName
==
"1234"
&&
password
==
"1234"
)//身份验证不过则报错,过了就过了。
{
}
else
{
throw
new
System
.
IdentityModel
.
Tokens
.
SecurityTokenException
(
"Unknown Username or Password"
);
}
}
}
}
Web配置文件
<?xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<configuration>
<system.web>
<compilation
debug
=
"true"
targetFramework
=
"4.0"
/>
</system.web>
<system.serviceModel>
<services>
<service
name
=
"WcfService1.ClientCallServer"
>
<endpoint
address
=
""
binding
=
"wsDualHttpBinding"
bindingConfiguration
=
"NewBinding0"
contract
=
"WcfService1.IClientCallServer"
>
</endpoint>
</service>
</services>
<bindings>
<wsDualHttpBinding>
<binding
name
=
"NewBinding0"
>
<security
mode
=
"Message"
><--消息加密->
<message
clientCredentialType
=
"UserName"
/>
</security>
</binding>
</wsDualHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceCredentials>
<--findValue 公司名称=GreenWhale,storeLocation==存储位置本机,储存位置:Root(根证书区)->
<serviceCertificate
findValue
=
"CN=GreenWhale"
x509FindType
=
"FindBySubjectDistinguishedName"
storeLocation
=
"LocalMachine"
storeName
=
"Root"
></serviceCertificate>
<clientCertificate>
<authentication
certificateValidationMode
=
"None"
></authentication>
</clientCertificate>
<---customUserNamePasswordValidatorType,先放动态库的命名空间+类名,然后时逗号然后是dll名称,我这里时WcfService1--->
<userNameAuthentication
userNamePasswordValidationMode
=
"Custom"
includeWindowsGroups
=
"false"
customUserNamePasswordValidatorType
=
"WcfService1.WCFValidator,WcfService1"
></userNameAuthentication>
</serviceCredentials>
<!-- 为避免泄漏元数据信息,请在部署前将以下值设置为 false -->
<serviceMetadata
httpGetEnabled
=
"true"
/>
<!-- 要接收故障异常详细信息以进行调试,请将以下值设置为 true。在部署前设置为 false 以避免泄漏异常信息 -->
<serviceDebug
includeExceptionDetailInFaults
=
"false"
/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment
aspNetCompatibilityEnabled
=
"false"
multipleSiteBindingsEnabled
=
"true"
/>
</system.serviceModel>
<system.webServer>
<modules
runAllManagedModulesForAllRequests
=
"true"
/>
<!--
若要在调试过程中浏览 Web 应用程序根目录,请将下面的值设置为 True。
在部署之前将该值设置为 False 可避免泄露 Web 应用程序文件夹信息。
-->
<directoryBrowse
enabled
=
"true"
/>
</system.webServer>
</configuration>
客户端
客户端很简单,首先引用WCF服务,然后在连接时输入对应的账号和密码即可。
using
System
;
using
System
.
Collections
.
Generic
;
using
System
.
ComponentModel
;
using
System
.
Data
;
using
System
.
Drawing
;
using
System
.
Linq
;
using
System
.
Net
;
using
System
.
ServiceModel
;
using
System
.
Text
;
using
System
.
Windows
.
Forms
;
using
System
.
Xml
;
using
WindowsFormsApp1
.
Service
;
namespace
WindowsFormsApp1
{
public
partial
class
Form1
:
Form
,
IClientCallServerCallback
{
public
Form1
()
{
InitializeComponent
();
context
=
new
InstanceContext
(
this
);
client
=
new
ClientCallServerClient
(
context
);
}
public
IPEndPoint
ServerRequestClientIP
()
{
throw
new
NotImplementedException
();
}
InstanceContext
context;
public
void
ServerSayMsg
(
string
text)
{
MessageBox
.
Show
(
text
);
}
ClientCallServerClient
client;
private
void
button1_Click
(
object
sender,
EventArgs
e)
{
// certificateValidationMode
client
.
ClientCredentials
.
UserName
.
UserName
=
"1234"
;
client
.
ClientCredentials
.
UserName
.
Password
=
"1234"
;
client
.
ClientSayToServer
(
"Fuck"
);
}
}
}