正确的WebApi和OAuth登录过程
本文关键字:OAuth 登录 过程 WebApi | 更新日期: 2023-09-27 18:16:26
我最近开始深入研究WebApi,作为开始新的c# web项目的基础。我已经看了一下VS2013 Update 4提供的基本模板,特别是MVC(尝试将方法移植到WebApi)以及基本的WebApi模板。
通过我建立这个框架(它基本上应该提供身份验证和授权规则)的努力,我已经习惯了以下来源作为指导:
- http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/
- http://bitoftech.net/2015/02/03/asp-net-identity-2-accounts-confirmation-password-user-policy-configuration/
- http://kaliko.com/blog/aspnet-template-for-data-access-identity/
使用这些源代码,我拼凑了一个基本的WebApi框架,它使用OAuth和JWT作为承载令牌提供者。我还用一个使用Telerik ORM而不是实体框架的库取代了原生的asp.net身份库(因为我们需要映射到使用元数据在结构上定义的自定义表,但现在这并不重要)。
然而,上述来源都没有描述如何使用WebApi进行登录,并且我所做的其他搜索也没有产生结果。
使用使用asp.net Identity的基本MVC模板(带有个人用户帐户和Cookie身份验证),它包含相当强大的登录过程,可以登录用户,可以锁定用户,或者可以检查是否需要帐户验证,这些都是由登录管理器提供的。
基本的WebApi模板(带有个人用户帐户和Cookie身份验证)使用AuthAuthorizationServerOptions,它被设置为检查具有提供的凭据的用户是否存在,生成用户身份以获得ClaimsIdenity,生成身份验证票据,验证票据,然后调用context. request . context. Authentication。登录(参数类型ClaimsIdentity)。这个函数做什么,我不确定,因为它似乎没有应用到它的MVC模板有使用SigninManager相同的规则。
我想知道的是,使用WebApi登录一个人的正确过程是什么?承载令牌授予是否构成登录过程的一部分,或者应该完成登录(使用IAuthenticationManager或SigninManager),并且基于登录的结果,如果成功,则只应该请求令牌?
下面我提供了我的Startup.cs和OAuthProvider的版本。
Startup.cs:
[assembly: OwinStartup(typeof(TelerikAndWebAPI.Startup))]
namespace TelerikAndWebAPI
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
ConfigureOAuthTokenGeneration(app);
ConfigureOAuthTokenConsumption(app);
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
//Uses JWT bearer tokens
public void ConfigureOAuthTokenGeneration(IAppBuilder app)
{
bool debug = false;
if (System.Diagnostics.Debugger.IsAttached) {
debug = true;
}
else{
debug = false;
}
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = debug,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new CustomOAuthProvider(),
AccessTokenFormat = new CustomJwtFormat(HttpContext.Current.Request.Url.AbsoluteUri.Replace(HttpContext.Current.Request.Url.PathAndQuery, "/"))
};
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
//Consume the JWT bearer tokens
private void ConfigureOAuthTokenConsumption(IAppBuilder app)
{
var issuer = HttpContext.Current.Request.Url.AbsoluteUri.Replace(HttpContext.Current.Request.Url.PathAndQuery, "/");
string audienceId = JwtCodeProvider.audienceID;
byte[] audienceSecret = TextEncodings.Base64Url.Decode(JwtCodeProvider.audienceSecret);
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audienceId },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
}
});
}
}
}
OAuthProvder:
public class CustomOAuthProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
using (AccountController accounts = new AccountController())
{
//Should I plug in a signin manager or authentication manager here and then based on whether the
//sign in is successful, then do the bearer token generation?
IdentityUser user = await accounts.FindUser(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await accounts.GenerateUserIdentity(user, "JWT");
var ticket = new AuthenticationTicket(oAuthIdentity, null);
context.Validated(ticket);
}
}
}
为了清晰起见,ClaimsIdentity oAuthIdentity = await accounts.GenerateUserIdentity(user, "JWT");
的源代码:
public async Task<ClaimsIdentity> GenerateUserIdentity(IdentityUser user,string authenticationType)
{
ClaimsIdentity oAuthIdentity = await UserManager.CreateIdentityAsync(user, authenticationType);
return oAuthIdentity;
}
我已经在我认为我应该插入SignInManager方法的oauthrprovider类中包含了注释,但是,我不确定。如果有人能告诉我最好的做法是什么,我将不胜感激。
作为免责声明,我的类中的许多代码与我提供的源代码中相同。一旦我完成了概念证明,我就会改变这个。
VS 2013模板的问题,它们混合了MVC应用程序的身份验证(应用程序在浏览器中呈现并使用传统的身份验证cookie)和依赖于OAuth 2.0承载令牌的Web Api身份验证,而不管令牌格式(JWT或默认格式)。
在REST api世界中,身份验证应该保持简单,因为你有一个广泛的消费者(移动应用程序,JS,服务器端服务,MVC等),它应该是无状态的(对每个请求进行身份验证),所以要实现这一点,你需要在开始时获得一个OAuth访问令牌,通过在服务器上提供用户名/密码验证,然后发出这个令牌并将其返回给消费者。此令牌应该有到期日期/时间(几个小时、几天、几个月)。
一旦您有了访问令牌,您需要使用承载方案将它与授权头中的每个请求一起发送。然后服务器将负责验证它并允许/拒绝调用。JWT访问令牌是自包含的令牌,因此您可以在其中放入声明(关于登录的用户或代表用户的客户端应用程序的信息),并且您可以在每个请求时在服务器上再次读取这些声明,而无需访问DB。
现在SignIn管理器在你的情况下是不需要的,它将是有用的,如果你想创建一个安全的身份验证cookie,将在MVC应用程序中使用。
如果你的应用程序将包含MVC和Web API,那么你需要SignIn管理器
我希望我的回答能澄清你的疑虑。