使用ServiceSelf解决.NET应用程序做服务的难题
Posted 老九
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用ServiceSelf解决.NET应用程序做服务的难题相关的知识,希望对你有一定的参考价值。
1 ServiceSelf
为.NET 泛型主机的应用程序提供自安装为服务进程的能力,支持windows和linux平台。
功能
- 自我服务安装
- 自我服务卸载
- 自我服务日志监听
2 自我服务安装
虽然.NetCore提供了Microsoft.Extensions.Hosting.Systemd
和Microsoft.Extensions.Hosting.WindowsServices
两个服务生命周期包,但在服务安装这块目前还非常不便:在windows平台,需要管理员身份使用sc.exe工具来安装服务;在linux平台,需要自己手动写服务单元文件和使用systemctl加载服务。不常用的sc和服务单元文件的内容知识,就像学了外语之后又长期不用外语的我们一样,时间一久就忘记。而且windows服务进程的默认工作目录是%SystemRoot%\\System32
,在没有日志组件的帮助下,sc.exe
安装的服务在运行后我们可能就掉到工作目录的坑里,影响包括但不限于配置文件的读取、asp.netcore
的ContentRoot、wwwroot静态文件等。
ServiceSelf提供自我服务安装
的能力,它提供了windows服务和linux的systemd服务的公共参,同时另外提供windows独有的服务配置和systemd独有的完整服务配置,此外还解决了windows服务没有工作目录
配置的缺陷。
现在,你可以在使用ServiceSelf描述服务:
var serviceName = "myapp";
var serviceOptions = new ServiceOptions
Arguments = new[] new Argument("key", "value") ,
Description = "这是演示示例应用",
;
serviceOptions.Linux.Service.Restart = "always";
serviceOptions.Linux.Service.RestartSec = "10";
serviceOptions.Windows.DisplayName = "演示示例";
serviceOptions.Windows.FailureActionType = WindowsServiceActionType.Restart;
// serviceName和serviceOptions甚至可以为null
if (Service.UseServiceSelf(args, serviceName, serviceOptions))
var host = Host.CreateDefaultBuilder(args)
// 为Host配置UseServiceSelf()
.UseServiceSelf()
.Build();
host.Run();
然后在控制台下以管理员或root身份执行如下命令:
./myapp start // 安装并启动服务
3 自我服务卸载
在控制台下以管理员或root身份执行如下命令:
./myapp stop // 停止并删除服务
4 自我服务日志监听
虽然有文件日志、大型的日志采集平台或框架等,但他们也取代不了控制台实时显示的日志,相反他们是互补的。控制台模式启动时,我们很容易直接在控制台看到实时日志的打印,但安装为服务后,查看控制台日志变得不容易或无法实现,在linux平台有journalctl
,它是基于管道的,它无法知道一条日志内容的边界,很难把符合过滤特征的日志完整显示;windows平台有session隔离机制,服务进程和桌面用户进程不在同一个session,所以桌面用户看不到服务进程的控制台,也没有管道可以重定向来读取服务进程的控制输出。
ServiceSelf为服务进程集成了"自研的"的基于管道传输的Google.Protobuf结构化日志提供者,在监听者开启监听之后,这个日志提供者才会工作,把结构化的日志传输给监听者,监听者可以使用关键词来过滤得到完整的一条结构化日志,而不是只过滤得一条日志内容的某一行或几行,再把完整的结构化日志打印到监听者的Console上。也就是它不会在服务进程上让日志无脑地输出到串行化输出的低性能控制台,也不会让服务进程在没有监听者的情况下无脑的输出Google.Protobuf结构化日志,即这个日志组件对服务进程没有性能影响。
之所以要自己实现基于管道传输的Google.Protobuf结构化日志提供者,而不直接使用Microsoft的EventSourceLoggerProvider
,是因为跨进程读取日志时需要依赖Microsoft.Diagnostics.Tracing.TraceEvent
,这个包非常大而全,其依赖项也特别多,而我们仅仅日志这一小功能而已。
由于监听者与服务进程是同一个应用程序的不同进程,当应用程序的OutputType是WinExe模式且运行在windows时,这时候是没有Console的,ServiceSelf做为监听者角色时会检测和动态创建Console然后将日志输出到Console。
现在输入logs子命令,就在Console上输出服务进程的实时日志:
./myapp logs // 控制台输出服务的日志
./myapp logs filter="key words" // 控制台输出匹配了"key words"的服务的日志
5 后记
ServiceSelf在api设计上十分精炼,你只要关注Service.UseServiceSelf()
和IHostBuilder.UseServiceSelf()
两个函数即可,但可以为你的服务进程提供非常完整的解决方案,您可以到 github上关注此项目。
使用 Keycloak 作为 ASP.Net 应用程序的单一登录解决方案
【中文标题】使用 Keycloak 作为 ASP.Net 应用程序的单一登录解决方案【英文标题】:Using Keycloak as a Single Sign On solution for ASP.Net applications 【发布时间】:2018-02-26 01:37:44 【问题描述】:我正在尝试将 Keycloak 用作多个 ASP.Net 应用程序的单点登录服务器。我在 Github 上找到了一个名为 KeycloakOwinAuthentication 的库,我克隆了两次内提供的示例代码,以用作两个不同的应用程序。
我在 Keycloak 的同一个领域中配置了 2 个应用程序(App1 和 App2),创建了具有所有角色的测试用户并尝试登录。
预期行为:从应用程序一登录,刷新应用程序2,您将自动登录
实际结果:从 App 1 登录,刷新 App 2 并得到“'/' 应用程序中的服务器错误。如果我从 App1 注销并尝试刷新 App2,它会恢复正常!
可以找到我的 2 个示例应用程序 here... 我的受保护页面的 ActionResult(在视图中显示令牌)如下所示:
[Authorize]
public ActionResult About()
ViewBag.Message = "Your application description page.";
var userPrincipal = User as ClaimsPrincipal;
ViewBag.Something = userPrincipal.Identity.ToString();
return View(userPrincipal);
我的启动页面如下所示:
public class Startup
public void Configuration(IAppBuilder app)
const string persistentAuthType = "Keycloak_Cookies";
app.UseCookieAuthentication(new CookieAuthenticationOptions
AuthenticationType = persistentAuthType
);
app.SetDefaultSignInAsAuthenticationType(persistentAuthType);
app.UseKeycloakAuthentication(new KeycloakAuthenticationOptions
Realm = "MyRealm",
ClientId = "App3",
ClientSecret = "KeycloakClientSecret",
KeycloakUrl = "http://localhost:8080/auth",
SignInAsAuthenticationType = persistentAuthType
);
是否有我遗漏的特定配置?我使用了我的测试领域以及具有 3 个不同应用程序(不是 Asp)的工作领域,但我未能在所有客户端上登录。我使用的是带有两个选项卡的相同浏览器,以确保所有 cookie 都可以访问......
错误文本:
Server Error in '/' Application.
IDX10214: Audience validation failed. Audiences: 'App1'. Did not match: validationParameters.ValidAudience: 'null' or validationParameters.ValidAudiences: 'null, App2'
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.IdentityModel.Tokens.SecurityTokenInvalidAudienceException: IDX10214: Audience validation failed. Audiences: 'App1'. Did not match: validationParameters.ValidAudience: 'null' or validationParameters.ValidAudiences: 'null, App2'
Source Error: An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
【问题讨论】:
您需要在此处发布您的示例代码,而不是任何明天可以更改或消失的第三方网站:minimal reproducible example 【参考方案1】:我深入研究了整个库的代码,发现有一个标志禁用了受众检查。它应该在 Startup.cs 文件的 Keycloak 配置中设置。 DisableAudienceValidation
标志默认为 false,但通过将其添加到配置并将其值设置为 true,将跳过受众验证。
app.UseKeycloakAuthentication(new KeycloakAuthenticationOptions
Realm = "DotNetApps",
ClientId = "TestingApp",
ClientSecret = "Client_Secret_Goes_Here",
KeycloakUrl = "http://localhost:8080/auth",
SignInAsAuthenticationType = persistentAuthType,
DisableAudienceValidation = true
);
【讨论】:
【参考方案2】:较新版本的 Keycloak 服务器 (>=6.x) 更改了在访问令牌中设置受众声明 (“aud”) 的方式。库 (Owin.Security.Keyclaok-3) 假定 Keycloak 客户端 ID 在“aud”声明中,默认情况下不再是这种情况。
修复它的一种方法是在 Keycloak 管理 UI 中,将映射器添加到正在使用的 Keycloak 客户端中。
名称:观众 映射器类型:受众 包含的客户受众:[客户 ID] 另外,确保 .NET 库配置了选项
DisableAllRefreshTokenValidation = true
在 startup.cs
中,这是较新的 Keycloak 服务器版本所必需的(无论如何,刷新令牌的验证都是在服务器端完成的)。
【讨论】:
以上是关于使用ServiceSelf解决.NET应用程序做服务的难题的主要内容,如果未能解决你的问题,请参考以下文章
使用 Keycloak 作为 ASP.Net 应用程序的单一登录解决方案
为什么在一个解决方案中混合.Net Standard和.Net Core项目?
ASP.NET 4.0尚未在 Web 服务器上注册 解决方法