如何跨多个服务器在 OWIN 中为 IIS 使用 OAuth AccessTokenFormat?
How do I use OAuth AccessTokenFormat in OWIN for IIS across multiple servers?
我正在使用 C# 开发 Web 应用程序。
目前(不记名)身份验证和令牌生成全部发生在一个地方。
理赔完成后,我们有以下代码来取票:-
var ticket = new AuthenticationTicket(identity, properties);
context.Validated(ticket);
稍后,我们检查传回给我们的票,使用以下代码获取票:-
OAuthAuthenticationOptions.AccessTokenFormat.Unprotect(token);
当代码全部托管在一台机器上时,一切正常。
当我拆分代码在不同的机器上工作时,我无法通过调用 AccessTokenFormat.Unprotect 方法取回 AuthenticationTicket。
看完这篇文章后OWIN Bearer Token Authentication - 我尝试在新机器的 web.config 文件中设置 MachineKey 以匹配现有服务器的 MachineKey .
结果是解密过程不再抛出错误,但它 returns 令牌为 null。
(当我没有正确的machineKey时,出现解密运行时的错误。)
如果我在这里犯了一个明显的错误,请有人告诉我吗?
此外,由于我是 OWIN 管道的新手;我在新项目中可能缺少一个配置步骤。
谢谢,
大卫 :-)
2016-05-23:代码来自 Startup.Configuration
public class Startup
{
public void Configuration(IAppBuilder app)
{
// Build IoC Container
var container = new Container().Initialize();
// Initialize Logging and grab logger.
MyCustomLogger.Configure();
var logger = container.GetInstance<IMyCustomLogger>();
var userIdProvider = container.GetInstance<IUserIdProvider>();
var azureSignalRInterface = new SignalRInterface();
GlobalHost.DependencyResolver.Register(typeof(ITokenService), container.GetInstance<ITokenService>);
GlobalHost.DependencyResolver.Register(typeof(IMyCustomLogger), () => logger);
GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => userIdProvider);
GlobalHost.DependencyResolver.Register(typeof(IExternalMessageBus), () => azureSignalRInterface);
GlobalHost.DependencyResolver.Register(typeof(ISerializer<>), () => typeof(JsonSerializer<>));
app.Use<ExceptionHandlerMiddleware>(logger, container);
app.Use<StructureMapMiddleware>(container);
// Setup Authentication
var authConfig = container.GetInstance<OwinAuthConfig>();
authConfig.ConfigureAuth(app);
// Load SignalR
app.MapSignalR("/signalR", new HubConfiguration()
{
EnableDetailedErrors = false,
EnableJSONP = true,
EnableJavaScriptProxies = true
});
}
}
Container().Initialize 只是为StructureMap 的依赖注入设置了一些注册表,代码如下:-
public static IContainer Initialize(this IContainer container)
{
container.Configure(x => {
x.AddRegistry<ServiceRegistry>();
x.AddRegistry<AlertsRegistry>();
x.AddRegistry<SignalRRegistry>();
});
return container;
}
此外,我的 Global.asax.cs 文件如下所示:-
protected void Application_Start()
{
//GlobalConfiguration.Configure(WebApiConfig.Register);
GlobalConfiguration.Configure(config =>
{
AuthConfig.Register(config);
WebApiConfig.Register(config);
});
}
AuthConfig class 看起来像这样:-
public static class AuthConfig
{
/// <summary>
/// Registers authorization configuration with global HttpConfiguration.
/// </summary>
/// <param name="config"></param>
public static void Register(HttpConfiguration config)
{
// Forces WebApi/OAuth to handle authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
}
}
其中 OAuthDefaults.AuthenticationType
是字符串常量。
最后我的OwinAuthConfig代码如下:-
public class OwinAuthConfig
{
public static OAuthAuthorizationServerOptions OAuthAuthorizationOptions { get; private set; }
public static OAuthBearerAuthenticationOptions OAuthAuthenticationOptions { get; private set; }
public static string PublicClientId { get; private set; }
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the application for OAuth based flow
PublicClientId = "MyCustom.SignalRMessaging";
OAuthAuthorizationOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Authenticate"), // PathString.FromUriComponent("https://dev.MyCustom-api.com/Authenticate"),
Provider = new MyCustomDbLessAuthorizationProvider(
PublicClientId),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// TODO: change when we go to production.
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthAuthorizationServer(OAuthAuthorizationOptions);
OAuthAuthenticationOptions = new OAuthBearerAuthenticationOptions
{
Provider = new MyCustomDbLessAuthenticationProvider()
};
app.UseOAuthBearerAuthentication(OAuthAuthenticationOptions);
}
public static AuthenticationTicket UnprotectToken(string token)
{
return OAuthAuthenticationOptions.AccessTokenFormat.Unprotect(token);
}
public void ConfigureHttpAuth(HttpConfiguration config)
{
config.Filters.Add(new AuthorizeAttribute());
}
}
2016-05-26:添加了配置文件片段。
所以这是生成令牌的服务器上的配置:-
<system.web>
<machineKey
validationKey="..."
decryptionKey="..." validation="SHA1" decryption="AES" />
<authentication mode="None" />
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<customErrors mode="Off" />
</system.web>
这是尝试使用令牌的 SignalR 服务器上的配置:-
<system.web>
<machineKey
validationKey="..."
decryptionKey="..." validation="SHA1" decryption="AES" />
<authentication mode="None" />
<compilation debug="true" targetFramework="4.5.2" />
<httpRuntime targetFramework="4.5.2" />
<customErrors mode="Off" />
</system.web>
在资源服务器中你应该使用OAuthBearerAuthenticationOptions.AccessTokenFormat
property instead of OAuthAuthorizationServerOptions.AccessTokenFormat
。请参阅文档链接。
对于 AuthenticationTokenReceiveContext
在 IAuthenticationTokenProvider.Receive()
方法中你也可以做 context.DeserializeTicket(context.Token);
.
如您所指,两台服务器中的 MachineKey 应该相同。
希望对您有所帮助。
编辑 (2016-05-24)
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
context.DeserializeTicket(context.Token);
// Now you can access to context.Ticket
...
}
另一种可能是web.config中的machine key在web部署后发生了变化,导致编译后的dll中的machine key与dll中的machine key不匹配。
我正在使用 C# 开发 Web 应用程序。 目前(不记名)身份验证和令牌生成全部发生在一个地方。
理赔完成后,我们有以下代码来取票:-
var ticket = new AuthenticationTicket(identity, properties);
context.Validated(ticket);
稍后,我们检查传回给我们的票,使用以下代码获取票:-
OAuthAuthenticationOptions.AccessTokenFormat.Unprotect(token);
当代码全部托管在一台机器上时,一切正常。
当我拆分代码在不同的机器上工作时,我无法通过调用 AccessTokenFormat.Unprotect 方法取回 AuthenticationTicket。
看完这篇文章后OWIN Bearer Token Authentication - 我尝试在新机器的 web.config 文件中设置 MachineKey 以匹配现有服务器的 MachineKey .
结果是解密过程不再抛出错误,但它 returns 令牌为 null。
(当我没有正确的machineKey时,出现解密运行时的错误。)
如果我在这里犯了一个明显的错误,请有人告诉我吗?
此外,由于我是 OWIN 管道的新手;我在新项目中可能缺少一个配置步骤。
谢谢, 大卫 :-)
2016-05-23:代码来自 Startup.Configuration
public class Startup
{
public void Configuration(IAppBuilder app)
{
// Build IoC Container
var container = new Container().Initialize();
// Initialize Logging and grab logger.
MyCustomLogger.Configure();
var logger = container.GetInstance<IMyCustomLogger>();
var userIdProvider = container.GetInstance<IUserIdProvider>();
var azureSignalRInterface = new SignalRInterface();
GlobalHost.DependencyResolver.Register(typeof(ITokenService), container.GetInstance<ITokenService>);
GlobalHost.DependencyResolver.Register(typeof(IMyCustomLogger), () => logger);
GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => userIdProvider);
GlobalHost.DependencyResolver.Register(typeof(IExternalMessageBus), () => azureSignalRInterface);
GlobalHost.DependencyResolver.Register(typeof(ISerializer<>), () => typeof(JsonSerializer<>));
app.Use<ExceptionHandlerMiddleware>(logger, container);
app.Use<StructureMapMiddleware>(container);
// Setup Authentication
var authConfig = container.GetInstance<OwinAuthConfig>();
authConfig.ConfigureAuth(app);
// Load SignalR
app.MapSignalR("/signalR", new HubConfiguration()
{
EnableDetailedErrors = false,
EnableJSONP = true,
EnableJavaScriptProxies = true
});
}
}
Container().Initialize 只是为StructureMap 的依赖注入设置了一些注册表,代码如下:-
public static IContainer Initialize(this IContainer container)
{
container.Configure(x => {
x.AddRegistry<ServiceRegistry>();
x.AddRegistry<AlertsRegistry>();
x.AddRegistry<SignalRRegistry>();
});
return container;
}
此外,我的 Global.asax.cs 文件如下所示:-
protected void Application_Start()
{
//GlobalConfiguration.Configure(WebApiConfig.Register);
GlobalConfiguration.Configure(config =>
{
AuthConfig.Register(config);
WebApiConfig.Register(config);
});
}
AuthConfig class 看起来像这样:-
public static class AuthConfig
{
/// <summary>
/// Registers authorization configuration with global HttpConfiguration.
/// </summary>
/// <param name="config"></param>
public static void Register(HttpConfiguration config)
{
// Forces WebApi/OAuth to handle authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
}
}
其中 OAuthDefaults.AuthenticationType
是字符串常量。
最后我的OwinAuthConfig代码如下:-
public class OwinAuthConfig
{
public static OAuthAuthorizationServerOptions OAuthAuthorizationOptions { get; private set; }
public static OAuthBearerAuthenticationOptions OAuthAuthenticationOptions { get; private set; }
public static string PublicClientId { get; private set; }
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the application for OAuth based flow
PublicClientId = "MyCustom.SignalRMessaging";
OAuthAuthorizationOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Authenticate"), // PathString.FromUriComponent("https://dev.MyCustom-api.com/Authenticate"),
Provider = new MyCustomDbLessAuthorizationProvider(
PublicClientId),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// TODO: change when we go to production.
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthAuthorizationServer(OAuthAuthorizationOptions);
OAuthAuthenticationOptions = new OAuthBearerAuthenticationOptions
{
Provider = new MyCustomDbLessAuthenticationProvider()
};
app.UseOAuthBearerAuthentication(OAuthAuthenticationOptions);
}
public static AuthenticationTicket UnprotectToken(string token)
{
return OAuthAuthenticationOptions.AccessTokenFormat.Unprotect(token);
}
public void ConfigureHttpAuth(HttpConfiguration config)
{
config.Filters.Add(new AuthorizeAttribute());
}
}
2016-05-26:添加了配置文件片段。 所以这是生成令牌的服务器上的配置:-
<system.web>
<machineKey
validationKey="..."
decryptionKey="..." validation="SHA1" decryption="AES" />
<authentication mode="None" />
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<customErrors mode="Off" />
</system.web>
这是尝试使用令牌的 SignalR 服务器上的配置:-
<system.web>
<machineKey
validationKey="..."
decryptionKey="..." validation="SHA1" decryption="AES" />
<authentication mode="None" />
<compilation debug="true" targetFramework="4.5.2" />
<httpRuntime targetFramework="4.5.2" />
<customErrors mode="Off" />
</system.web>
在资源服务器中你应该使用OAuthBearerAuthenticationOptions.AccessTokenFormat
property instead of OAuthAuthorizationServerOptions.AccessTokenFormat
。请参阅文档链接。
对于 AuthenticationTokenReceiveContext
在 IAuthenticationTokenProvider.Receive()
方法中你也可以做 context.DeserializeTicket(context.Token);
.
如您所指,两台服务器中的 MachineKey 应该相同。
希望对您有所帮助。
编辑 (2016-05-24)
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
context.DeserializeTicket(context.Token);
// Now you can access to context.Ticket
...
}
另一种可能是web.config中的machine key在web部署后发生了变化,导致编译后的dll中的machine key与dll中的machine key不匹配。