当前位置: 首页 > 知识库问答 >
问题:

webapi中基于令牌的实现以保护endpoint

姬念
2023-03-14

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

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

因此,我将实施基于令牌的机制来保护对我的endpoint的访问。

1)但在这里我感到困惑,我应该使用任何框架来生成访问令牌,或者我可以使用任何库来生成任何随机字符串,我将在response.for实例中发送如下内容:

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

所以,在注册应用程序时,如果客户端已经为其应用程序启用了身份验证,那么用户将被验证,然后我将返回访问令牌,并将访问令牌与该用户id一起保存在数据库中。

所以我的数据库表如下所示,用于存储经过验证的访问令牌:

Id(autogenerated)   accesstoken    userid   clientid    createdat    expiresat

所以在用户经过身份验证之后,现在如果用户想要访问任何受保护的资源,那么用户需要在随后的调用头中传递这个访问令牌。

所以我要做的是从header中获取访问令牌,然后根据数据库验证该访问令牌,然后允许其他用户访问我的受保护资源。

我见过很多与此相关的东西,所以基本上这是oauth2,我想实现它。

我已经看到了Openid connect(这个项目甚至没有编译),它位于oauth2之上,用于身份验证,oauth2将用于授权。

但是在这里,由于我将访问令牌存储在我的数据库中,因此我对此表示怀疑:

2) 现在,我需要openconnected(但这个项目甚至没有编译)来验证访问令牌,还是因为我在数据库中存储了访问令牌,所以我不需要openconNEcted?

3)我想实现ASP.NET身份,但然后我会收到动态数据库连接字符串,正如我所看到的ASP.NET身份大多与实体框架,我找不到任何来源,我可以使用ado.net验证用户名和密码使用SQL查询。我知道我可以做这样的事情:

创建一个实现Iuser的自定义用户类,如下所述定义一个实现的自定义用户存储

public class UserStoreService 
     : IUserStore<CustomUser>, IUserPasswordStore<CustomUser>

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

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

5)我看到了很多基于令牌的实现的以下链接,但我不知道他们在哪个部分验证accesstoken,但现在我的数据库中存储了accesstoken,我需要使用以下任何实现吗?

http://bitoftech.net/2014/10/27/json-web-token-asp-net-web-api-2-jwt-owin-authorization-server/

http://bitoftech . net/2014/06/01/token-based-authentic ation-ASP-net-we b-API-2-owin-ASP-net-identity/

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

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

更新:

我的网络服务引擎的用例是:

1)支持多客户端应用。

2) 以令牌管理的形式管理每个客户端应用程序的用户会话。因此,本文的大部分内容都是将accesstoken存储在身份中,并且该身份在[Authorize]属性中进行验证,在该属性中,accesstoke也被验证,并基于该用户被允许访问受保护的资源。这是我迄今为止的理解。

因此,如果我还使用用户身份并将用户上下文存储在支持多个客户端应用程序的身份中是一个好主意吗?

共有2个答案

阚砚文
2023-03-14

否,不需要将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<ApplicationUserManager>();
        // 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<string, string> property in context.Properties.Dictionary)
        {
            context.AdditionalResponseParameters.Add(property.Key, property.Value);
        }

        return Task.FromResult<object>(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<object>(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<object>(null);
    }

    public static AuthenticationProperties CreateProperties(string userName)
    {
        IDictionary<string, string> data = new Dictionary<string, string>
        {
            { "userName", userName }
        };
        return new AuthenticationProperties(data);
    }
}

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

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

王嘉木
2023-03-14

从“OAuth和OpenID Connect的完全抓取实现者讨论发现”中的“7.1.访问令牌表示”:

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

> < li>

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

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

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

如果访问令牌是随机字符串,则与访问令牌关联的信息片段(用户 ID、客户端 ID、作用域、生存期等)存储在由颁发访问令牌的授权服务器管理的数据库中。

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

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

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

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

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

最后,身份管理、用户认证和OAuth 2.0

 类似资料:
  • 我的问题是,对每个表单使用一个令牌,而不更新每个表单提交的令牌,安全吗?这能防止CSRF吗?

  • 我想做基于令牌的机制,我将有要么温泉或移动应用程序支持多个客户端。 我的web服务引擎和应用程序的用例: 我的web应用程序:客户将注册他们的应用程序,无论是SPA还是移动应用程序。他们将在注册时获得客户id。在SPA或移动应用程序的情况下,只有作为密钥的客户端id会被泄露,因此我只提供客户端id。 Web服务引擎:支持多个客户端,登录客户端各自的应用程序后管理每个用户的会话。 因此,假设有 2

  • 问题内容: 在这个问题中,Erik需要在Node.js中生成一个安全的随机令牌。有一种生成随机缓冲区的方法。但是,node中的base64编码不是网址安全的,它包含和而不是和。因此,我发现生成这种令牌的最简单方法是 有没有更优雅的方式? 问题答案: 尝试crypto.randomBytes(): “十六进制”编码在节点v0.6.x或更高版本中有效。

  • 我很少使用jersey实现创建rest服务。出于安全考虑,服务可以由任何一个调用。所以我决定使用基于令牌的认证系统。我在spring security中编写了一个过滤器,它在每个请求到达服务器之前处理它。 创建了一个登录服务,这样用户就可以通过传递有效凭据的用户名和密码来调用该服务,它将生成访问令牌和到期日期,并将其保存在Hashmap和DB中,并作为响应返回给用户。 对于剩余的服务,用户必须传递

  • 我正在AngularJS SPA中使用资源所有者密码凭证OAuth 2.0流。有几篇文章(这里,这里…)这个问题的答案解释了我们不应该将刷新令牌存储在(web)客户端(LocalStorage)上,而是将它们加密存储在HttpOnly Cookie中,并使用代理API实现对refreh令牌的解密,从而将其转发给安全令牌服务。 大多数文章都暗示我们应该使用一种常见的保护机制来关注CSRF。我想知道单

  • 我正在使用JWTs为我的应用程序验证用户身份。当用户登录时,他们将获得一个访问令牌和一个刷新令牌。为了保证刷新令牌的安全,我不将其存储在客户端,而是将其与他们的帐户一起保存在后端,这样就不容易访问了。虽然我对刷新令牌的安全性感到困惑,但当我阅读关于如何使用刷新令牌的在线资源时,以下是我理解的逻辑: 身份验证 将访问令牌+刷新令牌存储在某个位置(在我的示例中,访问令牌位于前端,刷新令牌位于后端) 执