我在这个问题上徘徊了好几天,而且没有一个答案可以解决这个问题。
我正在使用Windows 10系统并使用VisualStudio 2017实施。 我使用AspNetCore实现了以下项目:
1。)Web.AuthServer:用于身份验证的IdentityServer4。
2。)Web.ApiServer:第一个SignalR服务器。
3。)Web.ApiSwitch:第二个SignalR服务器。 它具有2个SignalR-Clients作为HostedService 两个SignalR-Server之间的“桥梁”。>
Web.ApiSwitch启动其HostedService,该HostedService连接到自身和Web.ApiServer,包括Web.AuthServer上的身份验证。只要它们带有一些“ localhost:PORT” URL,它就可以很好地工作。
现在我已经尝试使用“ MyIP:PORT”运行所有项目。 Web.AuthServer正在使用HTTPS和自签名证书(由OpenSSL生成)。 证书本身已使用以下命令行构建:
生成私钥:
openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout IdentityServer4Auth.key -out IdentityServer4Auth.crt -subj "/CN=example.com" -days 3650
生成证书:
openssl pkcs12 -export -out IdentityServer4Auth.pfx -inkey IdentityServer4Auth.key -in IdentityServer4Auth.crt -certfile IdentityServer4Auth.crt
该文件已添加到mmc:
1。)文件->添加或删除管理单元->证书->添加->计算机帐户->确定 2.)将证书(.cer)导入到个人->受信任的根证书颁发机构) 3.)将具有可导出私钥支持的pfx导入个人->证书。
Web.AuthServer的代码:
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseKestrel(options =>
{
options.Listen(IPAddress.Any, 5000, listenOptions =>
{
listenOptions.UseHttps();
});
})
.UseStartup<Startup>()
.ConfigureLogging(builder =>
{
builder.ClearProviders();
builder.AddSerilog();
})
.Build();
Web.AuthSever-ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
// Gets connection strings from "appsettings.json".
string csApplicationContext = Configuration.GetConnectionString("ApplicationContext");
string csConfigurationStore = Configuration.GetConnectionString("ConfigurationStore");
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
var settings = JsonFileManager<ServerSettings>.Load(AppDomain.CurrentDomain.BaseDirectory + "Config\\svConf.json");
// Add cross origin resource sharing.
services.AddCors(options =>
{
options.AddPolicy("default", policy =>
{
policy.WithOrigins(settings.CorsOrigins)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
// Add bearer token authentication.
services.AddAuthentication()
.AddJwtBearer(jwt =>
{
jwt.Authority = settings.JWTBearerSettings.Authority;
jwt.Audience = settings.JWTBearerSettings.Audience;
jwt.RequireHttpsMetadata = settings.JWTBearerSettings.RequireHttpsMetadata;
jwt.Validate();
});
services.AddPolicyServerClient(Configuration.GetSection("Policy"))
.AddAuthorizationPermissionPolicies();
// DB und User registieren für DI
services.AddDbContext<ApplicationDbContext>(builder =>
builder.UseSqlite(csApplicationContext, sqlOptions =>
sqlOptions.MigrationsAssembly(migrationsAssembly)));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddTransient<IClientStore, ClientService>();
// Add IS4 as authentication server.
var is4Builder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
options.Events.RaiseInformationEvents = true;
})
// Add config data (clients, resources, CORS).
.AddConfigurationStore(options =>
options.ConfigureDbContext = builder =>
builder.UseSqlite(csConfigurationStore, sqlOptions =>
sqlOptions.MigrationsAssembly(migrationsAssembly)))
.AddClientStore<ClientService>()
.AddAspNetIdentity<ApplicationUser>();
SigninCredentialExtension.AddSigninCredentialFromConfig(is4Builder, Configuration.GetSection("SigninKeyCredentials"), Logger);
services.AddMvc(options =>
{
// this sets up a default authorization policy for the application
// in this case, authenticated users are required (besides controllers/actions that have [AllowAnonymous]
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
options.SslPort = 5000;
options.Filters.Add(new RequireHttpsAttribute());
});
}
Web.AuthSever-配置:
public async void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
else
app.UseExceptionHandler("/Home/Error");
// Use specific cross origin resource sharing configuration.
app.UseCors("default");
app.UseDefaultFiles();
app.UsePolicyServerClaims();
app.UseStaticFiles();
app.UseHttpsRedirection();
app.UseIdentityServer();
// Adding test data to database.
await InitializeDbTestData.GenerateTestData(app);
app.UseMvcWithDefaultRoute();
}
Web.AuthSever-SigninCredentialExtension:
public static class SigninCredentialExtension
{
private const string KeyType = "KeyType";
private const string KeyTypeKeyFile = "KeyFile";
private const string KeyTypeKeyStore = "KeyStore";
private const string KeyTypeTemporary = "Temporary";
private const string KeyFilePath = "KeyFilePath";
private const string KeyFilePassword = "KeyFilePassword";
private const string KeyStoreIssuer = "KeyStoreIssuer";
public static IIdentityServerBuilder AddSigninCredentialFromConfig(
this IIdentityServerBuilder builder, IConfigurationSection options, ILogger logger)
{
string keyType = options.GetValue<string>(KeyType);
logger.LogDebug($"SigninCredentialExtension keyType is {keyType}");
switch (keyType)
{
case KeyTypeTemporary:
logger.LogDebug($"SigninCredentialExtension adding Developer Signing Credential");
builder.AddDeveloperSigningCredential();
break;
case KeyTypeKeyFile:
AddCertificateFromFile(builder, options, logger);
break;
case KeyTypeKeyStore:
AddCertificateFromStore(builder, options, logger);
break;
}
return builder;
}
public static X509Certificate2 GetCertificateByThumbprint(string thumbprint)
{
using (X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
certStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
if (certCollection.Count > 0) return certCollection[0];
}
return null;
}
private static void AddCertificateFromStore(IIdentityServerBuilder builder,
IConfigurationSection options, ILogger logger)
{
var keyIssuer = options.GetValue<string>(KeyStoreIssuer);
logger.LogDebug($"SigninCredentialExtension adding key from store by {keyIssuer}");
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindByIssuerName, keyIssuer, true);
if (certificates.Count > 0)
{
builder.AddSigningCredential(certificates[0]);
builder.AddValidationKey(certificates[0]);
}
else
logger.LogError("A matching key couldn't be found in the store");
}
private static void AddCertificateFromFile(IIdentityServerBuilder builder,
IConfigurationSection options, ILogger logger)
{
var keyFilePath = options.GetValue<string>(KeyFilePath);
var keyFilePassword = options.GetValue<string>(KeyFilePassword);
if (File.Exists(keyFilePath))
{
logger.LogDebug($"SigninCredentialExtension adding key from file {keyFilePath}");
builder.AddSigningCredential(new X509Certificate2(keyFilePath, keyFilePassword));
}
else
{
logger.LogError($"SigninCredentialExtension cannot find key file {keyFilePath}");
}
}
}
Web.ApiServer的代码:
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseKestrel(options =>
{
options.Listen(IPAddress.Any, 5004, listenOptions =>
{
listenOptions.UseHttps();
});
})
.UseStartup<Startup>()
.ConfigureLogging(builder =>
{
builder.ClearProviders();
builder.AddSerilog();
})
.Build();
Web.ApiServer-ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
// Add cross origin resource sharing.
services.AddCors(options =>
{
options.AddPolicy("default", policy =>
{
policy.WithOrigins(_settings.CorsOrigins)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
// Add bearer token authentication and our IS4 as authentication server.
services.AddAuthentication(_settings.ISAuthenticationSettings.DefaultScheme)
.AddIdentityServerAuthentication(options =>
{
options.Authority = _settings.ISAuthenticationSettings.Authority;
options.RequireHttpsMetadata = _settings.ISAuthenticationSettings.RequireHttpsMetadata;
options.ApiName = _settings.ISAuthenticationSettings.ApiName;
// Handling the token from query string in due to the reason
// that signalR clients are handling them over it.
options.TokenRetriever = new Func<HttpRequest, string>(req =>
{
var fromHeader = TokenRetrieval.FromAuthorizationHeader();
var fromQuery = TokenRetrieval.FromQueryString();
return fromHeader(req) ?? fromQuery(req);
});
options.Validate();
});
// Add singalR as event bus.
services.AddSignalR(options => options.EnableDetailedErrors = true);
services.AddMvcCore(options =>
{
options.SslPort = 5003;
options.Filters.Add(new RequireHttpsAttribute());
})
.AddAuthorization()
.AddJsonFormatters();
// Register ConnectionHost as hosted service with its wrapper class.
services.AddSingleton<Microsoft.Extensions.Hosting.IHostedService, ConnectionHost>();
}
Web.ApiServer-配置:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
app.UseHttpsRedirection();
// Has to be called before UseSignalR and UseMvc!
app.UseAuthentication();
// Use specific cross origin resource sharing configuration.
app.UseCors("default");
app.UseSignalR(routes => routes.MapHub<EventHub>("/live"));
app.UseMvc();
}
令牌请求或SignalR客户端:
public static async Task<TokenResponse> RequestTokenAsync(string authority, string clientID, string scope)
{
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync(authority);
if (disco.IsError) throw new Exception(disco.Error);
var response = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = clientID,
ClientSecret = "SomeTestSecret",
Scope = scope
});
if (response.IsError)
{
throw new Exception(response.Error);
}
return response;
}
来自Web.ApiServer的ConfigureServices的TokenRetriever仅用于获取对SignalR客户端的身份验证,因为它们正在通过查询字符串传递令牌。它完成了工作。
现在是问题:
Web.ApiServer的HostedService的客户端正在尝试从Web.AuthServer获取身份验证令牌(jwt承载),但是每次我得到 以下异常:
System.Security.Authentication.AuthenticationException: 'The remote certificate is invalid according to the validation procedure.'
如果我打开浏览器并输入Web.AuthServer“ MyIP:5000”的地址,那么在接受自签名证书之后,一切都可以正常工作。 但是Web.ApiServer的HostedService的客户端无法执行此操作。 如何排除此异常并获取一些有效的证书?我在客户端实施过程中缺少什么吗?希望有人可以帮助我-自4天多以来一直陷入困境。
答案 0 :(得分:0)
为使客户端信任服务器,客户端会检查服务器针对TLS公开的证书上的许多属性,例如“是预期域的证书”,“证书已过期”。客户端将检查的一件事是证书链,这是一个信任链。
https://knowledge.digicert.com/solution/SO16297.html
当您从证书颁发机构购买证书时,部分就是您要购买的-例如,让我们看看Facebook使用的证书。
它们具有可用于facebook的所有子域的通配符证书,并且受信任的根证书颁发机构为DigiCert(https://www.digicert.com/welcome/compatibility.htm)。通过使用广受信任的Digicert CA,客户端可以知道Facebook证书是由Digicert颁发的,因此可以信任Facebook证书。
这是您缺少的部分。您使用的是自签名证书,您的客户不了解根CA,也无法建立信任链。通过手动接受证书,您可以解决根本原因,但是显然,这对于您无法完全控制的客户端不起作用。
https://letsencrypt.org/提供了免费的CA服务,该服务现在可在大量客户中使用-在许多方面,它是一个不错的解决方案,支持自动证书续订。因此,与其使用自签名证书,不如使用letencrypt为您的服务器生成证书(有关如何执行此操作的文章很多)
使用由客户端信任的CA颁发的证书是解决此问题的正确方法。