使用 Katana 和 MembershipReboot 实现简单的 Oauth 服务器
介绍
在我之前关于 .NET 的文章中,我介绍了如何使用 ASP.NET MVC 中的 MembershipReboot 保护和管理用户帐户。在这篇文章中,我将引导您在 ASP.NET Web API 中实现资源所有者密码凭据授予类型。
如果您正在阅读本文,我会假设您对 ASP.NET 中的 Owin(开放 Web 接口)中间件有相当多的了解,并且您非常了解 OAuth2 和 Katana。如果您需要复习上述任何内容,请看这里。
我不会介绍这些主题,但我会提供您可以添加到 ASP.NET 项目的代码。这样,您将能够使用此授权类型专门保护对您服务的访问。
资源所有者密码凭证授权类型适用于资源所有者(拥有特定资源的应用程序用户)与我们要保护的 Web 服务的客户端(代表资源所有者请求访问资源的应用程序)具有信任关系的情况。有关授权类型重要性的更多信息,请查看OAuth2 页面。
首先,让我们安装以下 nuget 包。
安装 Nuget 包
- 首先安装依赖注入容器SimpleInjector
安装包 SimpleInjector.Integration.WebApi
- 然后安装 MembershipReboot 的软件包。
安装包 BrockAllen.MembershipReboot.Owin
安装包 BrockAllen.MembershipReboot.Ef
配置
配置 MembershipReboot 和 DI 容器并注册用于在运行时解析依赖项的容器
接下来是配置我们之前添加的框架。我将首先在web.config文件中向存储用户凭据的数据库添加连接字符串。
<add name="MembershipReboot" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=MembershipRebootWebAPI;Integrated Security=True" providerName="System.Data.SqlClient" />
在代码中配置 MembershipReboot
让我们配置 MembershipReboot。我通常喜欢在代码中执行此操作,并将其放在App_Start文件夹中的文件中。
在App_Start文件夹中添加一个名为MembershipRebootConfig的文件,内容如下:
using BrockAllen.MembershipReboot;
public class MembershipRebootConfig
{
public static MembershipRebootConfiguration Create()
{
var config = new MembershipRebootConfiguration
{
MultiTenant = true,
RequireAccountVerification = true,
EmailIsUsername = true,
AllowLoginAfterAccountCreation = true
};
return config;
}
}
上面,我启用了多个租户,因为用户帐户将存储在一个租户上,而 API 客户端凭据将存储在另一个租户上。文件中的其他设置可以根据需要进行修改。添加此文件是可选的,因为您可以在 web.config 文件中添加相同的设置。
配置 SimpleInjector
之后,配置 SimpleInjector 以在运行时解析 MembershipReboot 的类。在App_Start文件夹中添加一个名为的新类,其中包含以下内容
public static class SimpleInjectorWebApiInitializer
{
/// <summary>Initialize the container and register it as Web API Dependency Resolver.</summary>
public static void Initialize(IAppBuilder app)
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle();
InitializeContainer(container);
container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
container.Verify();
GlobalConfiguration.Configuration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container);
//allow scoped instances to be resolved during an OWIN request
app.Use(async (context, next) =>
{
using (container.BeginExecutionContextScope())
{
context.Environment.SetUserAccountService(() => container.GetInstance<UserAccountService>());
await next();
}
});
}
private static void InitializeContainer(Container container)
{
System.Data.Entity.Database.SetInitializer(new System.Data.Entity.MigrateDatabaseToLatestVersion<DefaultMembershipRebootDatabase, BrockAllen.MembershipReboot.Ef.Migrations.Configuration>());
container.RegisterSingleton<MembershipRebootConfiguration>(MembershipRebootConfig.Create);
container.Register<DefaultMembershipRebootDatabase>(() => new DefaultMembershipRebootDatabase(), Lifestyle.Scoped);
var defaultAccountRepositoryRegistration =
Lifestyle.Scoped.CreateRegistration<DefaultUserAccountRepository>(container);
container.AddRegistration(typeof(IUserAccountQuery), defaultAccountRepositoryRegistration);
container.AddRegistration(typeof(IUserAccountRepository), defaultAccountRepositoryRegistration);
container.Register<UserAccountService>(() => new UserAccountService(container.GetInstance<MembershipRebootConfiguration>(), container.GetInstance<IUserAccountRepository>()), Lifestyle.Scoped);
}
}
上面,我已经注册了 MembershipReboot 的依赖项,并且还添加了代码以使 SimpleInjector 在 Owin 请求期间解析依赖项。
打开 Owin 启动类并调用Initialize:
//Startup.cs
public void Configuration(IAppBuilder app)
{
SimpleInjectorWebApiInitializer.Initialize(app);
}
OAuth2 令牌端点设置
接下来,我们需要使用Microsoft.Owin.Security.OAuth库附带的OAuthAuthorizationServerMiddleware设置 OAuth 2.0 令牌端点以支持资源所有者密码凭据授予类型。这需要一个授权服务器,然后将其连接到 Katana 管道。
使用 Katana 编写授权服务器围绕从OAuthAuthorizationServerProvider派生的类,该类包含允许我们操作 OAuth2 协议的方法。
为此,我将添加一个名为MyOAuthAuthorizationServerProvider的类,它派生自OAuthAuthorizationServerProvider并重写方法 ValidateClientAuthentication 和 GrantResourceOwnerCredentials。
public class MyOAuthAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override System.Threading.Tasks.Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string cid, csecret;
if (context.TryGetBasicCredentials(out cid, out csecret))
{
var svc = context.OwinContext.Environment.GetUserAccountService<UserAccount>();
if (svc.Authenticate("clients", cid, csecret))
{
context.Validated();
}
}
return Task.FromResult<object>(null);
}
public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var svc = context.OwinContext.Environment.GetUserAccountService<UserAccount>();
UserAccount user;
if (svc.Authenticate("users", context.UserName, context.Password, out user))
{
var claims = user.GetAllClaims();
var id = new System.Security.Claims.ClaimsIdentity(claims, "MembershipReboot");
context.Validated(id);
}
return base.GrantResourceOwnerCredentials(context);
}
}
在 ValidateClientAuthentication 方法中,我编写了代码来验证客户端凭据,如果凭据与数据库中的记录匹配,则调用OAuthValidateClientAuthenticationContext.Validated()方法。如果客户端通过验证,则继续验证用户或资源所有者凭据(用户名和密码),并在将传递给 GrantResourceOwnerCredentials 方法的OAuthGrantResourceOwnerCredentialsContext实例上调用 Validated() 方法。
现在我们需要将授权服务器中间件连接到 Owin 管道。IAppBuilder 上有一个简写扩展方法可以使用此中间件,即 UseOAuthAuthorizationServer。我们将使用此扩展方法在 Startup 类中配置 OAuth 2.0 端点:
public void Configuration(IAppBuilder app)
{
SimpleInjectorWebApiInitializer.Initialize(app);
app.UseOAuthAuthorizationServer(new Microsoft.Owin.Security.OAuth.OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,//should be disabled in production
Provider = new MyOAuthAuthorizationServerProvider(),
TokenEndpointPath = new PathString("/token")
});
}
此外,我们将添加用于使用令牌的承载令牌身份验证中间件:
public void Configuration(IAppBuilder app)
{
SimpleInjectorWebApiInitializer.Initialize(app);
app.UseOAuthAuthorizationServer(new Microsoft.Owin.Security.OAuth.OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
Provider = new MyOAuthAuthorizationServerProvider(),
TokenEndpointPath = new PathString("/token")
});
// token consumption
var oauthConfig = new Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationOptions
{
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
AuthenticationType = "Bearer"
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerAuthentication(oauthConfig);
}
要获取令牌,我们需要向 localhost/token 发出 post 请求并传入必要的参数。例如:
POST http://localhost:19923/token
Content-Type: Application/x-www-form-urlencoded
username=jbloggs&password=pass1234&grant_type=password
此时,我们将获得如下结果:
{
"access_token":
"XoHocsL0wOJBVVrjvSj6GpdGD-VrPD2ainZGyeZ8ji0aTq33epyHw72POhB8evYn41fzaSnjx7eo0iclADQBWTMgnghgZdXzNoLo6hwf4Y3SiB0aPTPgZi6PJwoGQK_aMWW62770jo6PBznPrSO0AOZUIrpxjUSZze90-HJjsM9ZgATIWdIvuiICqVjW7n5Z-o0GNSoDTIm-4k2zee0-c_lifHuLmW97IbsQ3I4gMz1SCBReSJtXJc8noPHvgwhFB_qZ2R1-TxR64nUVgxYYtBAoy9n6WKTgNAqrnUwsa0jfMk6wselrLwMGq-R-6_AX4bkh16OZBTGa5hXVWoLPIHl2JTKCkO2DsX2jqvp3J7PObRkZWMyUOyzwhnQWu_XTpn4ogwtcJvLulfiA6W01s8qiUQO--Xefm38ngu5HTM4",
"token_type": "bearer",
"expires_in": 1199
}
这就是我们开始所需要的一切。如您所见,令牌具有类型和到期日期。干净又安全!我在 GitHub 上有一个示例项目。
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~