基于令牌的webapi实现以保护端点

我有一个带有Web服务的Web应用程序,客户端将使用我的Web应用程序注册他们的应用程序。

现在,客户端将拥有SPA或移动应用程序的应用程序 ,他们将从他们的应用程序中使用我的Web服务。

所以我将实现基于令牌的机制来保护对我的端点的访问。

1)但是在这里我很困惑,我应该使用任何框架来生成访问令牌,或者我可以使用任何库,它将生成任何随机字符串,我将在响应中发送。例如这样的事情:

TokenId = Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace("+", "_") 

因此,在注册应用程序时,如果客户端已为其应用程序启用了身份validation,则将validation用户,然后我将返回访问令牌,并使用该用户ID保存在数据库中的accesstoken。

所以我的数据库表如下所示,用于存储经过validation的accesstoken:

 Id(autogenerated) accesstoken userid clientid createdat expiresat 

因此,在用户进行身份validation后,现在如果用户想要访问任何受保护资源,则用户需要在后续调用头中传递此访问令牌。

所以我要做的是,我将从头部获取访问令牌,然后validation对该数据库的accessstoken,然后允许访问我的受保护资源,其他明智的用户将获得授权。

我已经看到很多与此相关的事情,所以基本上这是oauth2,我想实现这个。

我见过Openid connect (这个项目甚至不编译),它位于oauth2之上,用于身份validation,oauth2将用于授权。

但在这里,因为我在我的数据库中存储访问令牌,所以这是我的疑问:

2)现在我需要openconnectid(但是这个项目甚至没有编译)来validation访问令牌,或者因为我在我的数据库中存储访问令牌,我不需要openconnectid?

3)我想实现asp.net身份,但后来我将收到动态数据库连接字符串,因为我看到asp.net身份主要与entity framework一起工作我找不到任何我可以使用ado.net来validation用户名的来源和密码使用SQL查询。 我知道我可以这样做:

创建一个实现IUser的自定义用户类,如此处所述定义实现的自定义用户存储

 public class UserStoreService : IUserStore, IUserPasswordStore 

但我没有这个信息,因为我没有固定连接string.connection字符串再次存储在客户端注册的数据库中。

4)我们已经为用户提供了一个固定的端点,客户端可以通过该端点创建管理员,因此我将使用我的RSA算法进行密码散列,然后将其存储在数据库中。 那么现在我需要使用asp.net身份吗?

5)我已经看到很多以下链接与基于令牌的实现,但我没有得到他们在哪个部分validationaccesstoken但现在因为我有存储在我的数据库中的accesstoken我是否需要使用以下任何实现?

JSON Web Token in ASP.NET Web API 2 using Owin

Token Based Authentication using ASP.NET Web API 2, Owin, and Identity

6)此外,如果对于任何客户端,如果客户端不希望对其各自的应用程序进行身份validation,那么我将做的是我将没有该用户名密码validation,但我将简单地生成accesstoken,然后发送响应,然后在每个后续请求访问令牌将被传递以访问受保护的资源。您认为这有意义吗?

我从未见过访问令牌存储在数据库中的任何示例,并且在数据库中存储访问令牌的问题是我每次都必须调用数据库以validation每个端点的访问令牌。

更新:

我的webservice引擎的用例是:

1)支持多个客户端应用程序。

2)以每个客户端应用程序的令牌管理的forms管理用户会话。 所以这里的大部分文章都是在身份中存储了accessstoken,并且在[Authorize]属性中validation了身份,其中也validation了accesstoken,并且基于该用户被允许访问受保护的资源。这是我迄今为止的理解。

那么如果我还在用户身份和存储用户上下文内部支持多个客户端应用程序的身份是一个好主意?

不,您不需要将access_token存储在数据库中。 您可以解密JWT并读取信息,因为您是使用密钥加密它的人。 (默认情况下,它是机器密钥。)

身份对Oauth有自我支持。 你必须正确配置它。 您可以在Startup.Auth.cs中设置OAuthAuthorizationServerOptions的配置。 示例代码如下。 我试图在代码中的评论中回答您的大部分问题。

 public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; } public static string PublicClientId { get; private set; } public void ConfigureOAuth(IAppBuilder app) { // Configure the application for OAuth based flow PublicClientId = "theDragonIsAlive"; OAuthOptions = new OAuthAuthorizationServerOptions { TokenEndpointPath = new PathString("/Token"), Provider = new YourOwnApplicationOAuthProvider(PublicClientId), //AuthorizeEndpointPath = new PathString("/Access/Account"), AccessTokenExpireTimeSpan = TimeSpan.FromDays(7) //AllowInsecureHttp = true }; // Enable the application to use bearer tokens to authenticate users app.UseOAuthBearerTokens(OAuthOptions); } public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider { private readonly string _publicClientId; public ApplicationOAuthProvider(string publicClientId) { if (publicClientId == null) { throw new ArgumentNullException("publicClientId"); } _publicClientId = publicClientId; } public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) { var userManager = context.OwinContext.GetUserManager(); // This where you are validating the username and password credentials. ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password); if (user == null) { context.SetError("Dragon Fire:", "The user name or password is incorrect. You shall be burnt."); return; } ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, OAuthDefaults.AuthenticationType); AuthenticationProperties properties = CreateProperties(user.UserName); AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties); context.Validated(ticket); context.Request.Context.Authentication.SignIn(oAuthIdentity); } public override Task TokenEndpoint(OAuthTokenEndpointContext context) { foreach (KeyValuePair property in context.Properties.Dictionary) { context.AdditionalResponseParameters.Add(property.Key, property.Value); } return Task.FromResult(null); } // This method is where you will create the client access token. // First you get the client, you can place values from the client record into the tokens claim collection. // You then create a new ClaimsIdentity. // You add some claims, in the example client name is added. // Create an AuthenticationTicket using your claims identity. // Validate the ticket (you do need to do this or the client will be considered unauthenticated) //public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context) //{ // var client = clientService.GetClient(context.ClientId); // var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType); // oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, client.ClientName)); // var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties()); // context.Validated(ticket); // return base.GrantClientCredentials(context); //} // This method has to be implmented when you are maintaining a list of clients which you will allow. // This method is for validating the input, you can used this method to verify the client id and secret are valid. public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { //string clientId; //string clientSecret; //context.TryGetFormCredentials(out clientId, out clientSecret); //if (clientId == "1234" && clientSecret == "12345") //{ // context.Validated(clientId); //} //return base.ValidateClientAuthentication(context); // Resource owner password credentials does not provide a client ID. if (context.ClientId == null) { context.Validated(); } return Task.FromResult(null); } public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context) { if (context.ClientId == _publicClientId) { Uri expectedRootUri = new Uri(context.Request.Uri, "/"); if (expectedRootUri.AbsoluteUri == context.RedirectUri) { context.Validated(); } } return Task.FromResult(null); } public static AuthenticationProperties CreateProperties(string userName) { IDictionary data = new Dictionary { { "userName", userName } }; return new AuthenticationProperties(data); } } 

上面的示例代码没有单独的客户端分类。 它会将所有用户视为单一类型的客户端。 但是我在评论中给出了一些示例代码,它们将指导您开始正确的方向。

免责声明:我不是这方面的专家(还),我的设置不同。 我和Owin有一个现有的MVC应用程序,我必须在它上面构建一个webapi。 这是我的原型代码,它完成了这项工作。 您必须为您的生产代码改进它。 玩得开心,祝你好运。

“7.1。 访问令牌表示“” OAuth和OpenID Connect的完全从头实现者“ 关于调查结果 ”

如何表示访问令牌? 有两种主要方式。

  1. 作为无意义的随机字符串。 与访问令牌相关联的信息存储在授权服务器后面的数据库表中。

  2. 作为一个自包含的字符串,它是通过base64url或类似的东西对访问令牌信息进行编码的结果。

博客中描述了这两种方式的优缺点。

如果访问令牌是随机字符串,则与访问令牌相关联的信息(用户ID,客户端ID,范围,寿命等)存储在由已发布访问令牌的授权服务器管理的数据库中。

每当公开API的资源服务器接受来自客户端应用程序的API调用时,资源服务器必须以某种方式获取有关访问令牌的信息。

如果资源服务器可以访问由授权服务器管理的数据库(换句话说,如果资源服务器和授权服务器共享数据库),则资源服务器可以直接从数据库获取有关访问令牌的信息。

否则,资源服务器必须对授权服务器进行API调用以获取信息。 在这种情况下,可以预期授权服务器公开符合RFC 7662 (OAuth 2.0 Token Introspection)的API。 请注意,某些实现可能提供比RFC 7662更加开发人员友好的API(例如“ 4.Introspection Access Token ” )。

无论如何,如果服务器在内存缓存或其他适当的位置缓存有关访问令牌的信息,则资源服务器不一定每次都必须进行数据库调用(或对授权服务器进行内省API调用)。

顺便说一下,当您想要保护API时,您需要的是访问令牌 。 因此,您的系统不必支持OpenID Connect,这是关于如何请求和颁发ID令牌的规范。 您可能会感到困惑,因为除了ID令牌之外,支持OpenID Connect的服务器也可以发出访问令牌。 请参阅“ 所有OpenID Connect流程图 ”以了解支持OpenID Connect问题的服务器。

最后,身份管理,用户身份validation以及OAuth 2.0和OpenID Connect不一定必须以单片方式实现。 有关详细信息,请参阅“ OAuth 2.0的新架构和OpenID Connect实施 ”