WCF LoginRegisterService本地正常,RadminVPN远程访问触发AuthenticationException与SSPI错误求助
Hey there, let's break down why your LoginRegisterService works locally but throws an AuthenticationException (SSPI error) when your friend connects via RadminVPN, and get it fixed step by step.
Why This Happens
The core issue is your current WCF setup uses Windows authentication, which relies on either:
- A shared Active Directory domain (you and your friend aren't on the same domain), or
- Trusted local Windows accounts (your friend's machine doesn't trust your local account, and vice versa)
SSPI fails because it can't verify Windows credentials across the RadminVPN network where machines aren't in a trusted relationship.
Let's Fix the Configuration
Below are three solutions, ordered from simplest (test-only) to most secure (production-ready):
1. Quick Test: Disable Authentication (Not for Production)
If you just want to verify the connection works first, temporarily turn off authentication:
Service端 app.config 修改:
Update your netTcpBinding security section:
<netTcpBinding> <binding name="NetTcpBinding_ILoginRegisterService"> <security mode="None"> <transport clientCredentialType="None" protectionLevel="EncryptAndSign" /> <message clientCredentialType="None" /> </security> </binding> </netTcpBinding>
Client端 App.config 修改:
Make the exact same security change as the service:
<netTcpBinding> <binding name="NetTcpBinding_ILoginRegisterService"> <security mode="None"> <transport clientCredentialType="None" protectionLevel="EncryptAndSign" /> <message clientCredentialType="None" /> </security> </binding> </netTcpBinding>
⚠️ Warning: Don't use this in production—this removes all identity verification, leaving your service unprotected.
2. Secure Solution: Use Username/Password Authentication
This is the best approach for non-domain networks like RadminVPN. It lets you validate users against your own database or custom logic.
Step 1: Update Service端 Configuration
- Modify the
netTcpBindingsecurity to useTransportWithMessageCredential(encrypts the connection and sends credentials in the message):
<netTcpBinding> <binding name="NetTcpBinding_ILoginRegisterService"> <security mode="TransportWithMessageCredential"> <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" /> <message clientCredentialType="UserName" /> </security> </binding> </netTcpBinding>
- Add a custom credential validator to your service behavior. In the
<serviceBehaviors>section'smexBehbehavior:
<behavior name="mexBeh"> <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false" /> <!-- Add this block --> <serviceCredentials> <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="BlazeRPServer.CustomUserValidator, BlazeRPServer" /> </serviceCredentials> </behavior>
- Create the custom validator class in your service project:
using System.IdentityModel.Selectors; using System.ServiceModel; namespace BlazeRPServer { public class CustomUserValidator : UserNamePasswordValidator { public override void Validate(string userName, string password) { // Replace this with your actual validation logic (e.g., check your UserDB) if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password)) throw new FaultException("Username or password cannot be empty."); // Example: Validate against hardcoded values (replace with DB check) if (userName != "testUser" || password != "testPass123") throw new FaultException("Invalid username or password."); } } }
Step 2: Update Client端 Configuration
- Match the service's binding security settings:
<netTcpBinding> <binding name="NetTcpBinding_ILoginRegisterService"> <security mode="TransportWithMessageCredential"> <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" /> <message clientCredentialType="UserName" /> </security> </binding> </netTcpBinding>
- In your client code, set the username and password before calling service methods:
var client = new LoginRegisterService.LoginRegisterServiceClient(); client.ClientCredentials.UserName.UserName = "testUser"; client.ClientCredentials.UserName.Password = "testPass123"; // Now call your service methods, e.g.: var result = client.SomeServiceMethod();
3. Stick With Windows Authentication (Advanced)
If you want to keep Windows auth, you need to set up trusted local accounts and configure Service Principal Names (SPNs):
- Create matching local accounts: On both your service machine and your friend's client machine, create a local user account with the exact same username and password.
- Register SPN on the service machine: Run Command Prompt as Administrator and execute:
setspn -s net.tcp/[YourServiceMachineName]:8968 [YourLocalAccountName]
- Update client endpoint identity: Replace the empty
<servicePrincipalName>with the registered SPN:
<endpoint ...> <identity> <servicePrincipalName value="net.tcp/[YourServiceMachineName]:8968" /> </identity> </endpoint>
- Update addresses: Replace all
*.*.*.*in both configs with the actual RadminVPN IP of the service machine (e.g.,10.0.0.2). The service's base address can use0.0.0.0to listen on all network adapters.
Final Checks
- Firewall Rules: Ensure your service machine's firewall allows inbound TCP connections on port 8968.
- RadminVPN Port Forwarding: Confirm RadminVPN isn't blocking traffic on port 8968.
- Address Accuracy: The client's endpoint address must point to your service's RadminVPN IP, not a wildcard.
内容的提问来源于stack exchange,提问作者Aubergine

