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

ASP.NET OAuth 授权 - 使用 ClientId 和密钥以及用户名和密码之间的区别

刘博文
2023-03-14

我正在尝试在 WebAPI 2 中实现一个简单的 OAuthAuthorizationServerProvider ASP.NET。我的主要目的是学习如何为移动应用程序提供令牌。我希望用户使用用户名登录

以下是我如何设置我的AuthorizationServer:

app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions()
{
    AllowInsecureHttp = true,
    TokenEndpointPath = new PathString("/token"),
    AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(5),
    Provider = new SimpleAuthorizationServerProvider(new SimpleAuthorizationServerProviderOptions()
    {
        ValidateUserCredentialsFunction = ValidateUser
    }),
    RefreshTokenProvider = new SimpleRefreshTokenProvider()
});

这是我的简单授权服务器提供选项实现:

public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
    public delegate Task<bool> ClientCredentialsValidationFunction(string clientid, string secret);
    public delegate Task<IEnumerable<Claim>> UserCredentialValidationFunction(string username, string password);
    public SimpleAuthorizationServerProviderOptions Options { get; private set; }

    public SimpleAuthorizationServerProvider(SimpleAuthorizationServerProviderOptions options)
    {
        if (options.ValidateUserCredentialsFunction == null)
        {
            throw new NullReferenceException("ValidateUserCredentialsFunction cannot be null");
        }
        Options = options;
    }

    public SimpleAuthorizationServerProvider(UserCredentialValidationFunction userCredentialValidationFunction)
    {
        Options = new SimpleAuthorizationServerProviderOptions()
        {
            ValidateUserCredentialsFunction = userCredentialValidationFunction
        };
    }

    public SimpleAuthorizationServerProvider(UserCredentialValidationFunction userCredentialValidationFunction, ClientCredentialsValidationFunction clientCredentialsValidationFunction)
    {
        Options = new SimpleAuthorizationServerProviderOptions()
        {
            ValidateUserCredentialsFunction = userCredentialValidationFunction,
            ValidateClientCredentialsFunction = clientCredentialsValidationFunction
        };
    }

    public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        if (Options.ValidateClientCredentialsFunction != null)
        {
            string clientId, clientSecret;

            if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
            {
                context.TryGetFormCredentials(out clientId, out clientSecret);
            }

            var clientValidated = await Options.ValidateClientCredentialsFunction(clientId, clientSecret);
            if (!clientValidated)
            {
                context.Rejected();
                return;
            }
        }

        context.Validated();
    }

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        if (Options.ValidateUserCredentialsFunction == null)
        {
            throw new NullReferenceException("ValidateUserCredentialsFunction cannot be null");
        }

        var claims = await Options.ValidateUserCredentialsFunction(context.UserName, context.Password);
        if (claims == null)
        {
            context.Rejected();
            return;
        }

        // create identity
        var identity = new ClaimsIdentity(claims, context.Options.AuthenticationType);

        // create metadata to pass to refresh token provider
        var props = new AuthenticationProperties(new Dictionary<string, string>()
        {
            { "as:client_id", context.UserName }
        });

        var ticket = new AuthenticationTicket(identity, props);
        context.Validated(ticket);
    }

    public override async Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
    {
        var originalClient = context.Ticket.Properties.Dictionary["as:client_id"];
        var currentClient = context.ClientId;

        // enforce client binding of refresh token
        if (originalClient != currentClient)
        {
            context.Rejected();
            return;
        }

        // chance to change authentication ticket for refresh token requests
        var newIdentity = new ClaimsIdentity(context.Ticket.Identity);
        newIdentity.AddClaim(new Claim("newClaim", "refreshToken"));

        var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties);
        context.Validated(newTicket);
    }
}

还有我的SimpleRefreshTokenProvider实现:

public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider
{
    private static ConcurrentDictionary<string, AuthenticationTicket> _refreshTokens =
        new ConcurrentDictionary<string, AuthenticationTicket>(); 

    public void Create(AuthenticationTokenCreateContext context)
    {

    }

    public async Task CreateAsync(AuthenticationTokenCreateContext context)
    {
        var guid = Guid.NewGuid().ToString();

        var refreshTokenProperties = new AuthenticationProperties(context.Ticket.Properties.Dictionary)
        {
            IssuedUtc = context.Ticket.Properties.IssuedUtc,
            ExpiresUtc = DateTime.UtcNow.AddYears(1)
        };
        var refreshTokenTicket = new AuthenticationTicket(context.Ticket.Identity, refreshTokenProperties);

        _refreshTokens.TryAdd(guid, refreshTokenTicket);
        context.SetToken(guid);
    }

    public void Receive(AuthenticationTokenReceiveContext context)
    {

    }

    public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
    {
        AuthenticationTicket ticket;
        if (_refreshTokens.TryRemove(context.Token, out ticket))
        {
            context.SetTicket(ticket);
        }
    }
}

我不完全理解的是ClientId和Secret用户名和密码的使用。我粘贴的代码通过用户名和密码生成令牌,我可以使用该令牌(直到它过期),但当我尝试获取刷新令牌时,我必须具有ClientId。

此外,如果令牌过期,正确的方法是发送刷新令牌并获取新令牌?如果刷新令牌被盗怎么办?它和用户名不一样吗

共有1个答案

呼延源
2023-03-14

我不完全理解的是ClientId和Secret用户名和密码的使用。我粘贴的代码通过用户名和密码生成令牌,我可以使用该令牌(直到它过期),但当我尝试获取刷新令牌时,我必须具有ClientId。

此外,如果令牌过期,正确的方法是发送刷新令牌并获取新令牌?如果刷新令牌被盗怎么办?它和用户名不一样吗

在OAuth2中,在协议定义的任何授权流程中对用户和客户端进行身份验证是必不可少的。客户端身份验证(正如您可能猜到的那样)强制仅由已知客户端使用您的API。序列化访问令牌一旦生成,就不会直接绑定到特定客户端。请注意,Client秘密必须被视为机密信息,并且只能由可以以某种安全方式存储此信息的客户端(例如外部服务客户端,但不能是javascript客户端)使用。

刷新令牌只是OAuth2的另一种“授权类型”,正如您正确指出的那样,它将代替用户名和密码对。这个令牌必须被视为机密数据(甚至比访问令牌更机密),但它比存储用户名更有优势

    < li >如果泄露,用户可以撤销它; < li >它具有有限的寿命(通常为几天或几周); < li >它不会暴露用户凭据(攻击者只能获得刷新令牌所发布的“范围”的访问令牌)。

我建议您阅读更多关于OAuth 2中定义的不同授权类型的信息,查看官方草案。我还向您推荐我自己在Web API中首次实现OAuth2时发现非常有用的资源。

样品请求

下面是两个使用fiddler的请求示例,用于资源所有者密码凭据授予

对于刷新令牌授予

 类似资料:
  • 数据供应商为使用其SFTP服务器请求了公共SSH密钥。他们向我们提供了: 用户名 密码 主机名 端口 我正在尝试使用私钥和用户名/密码在php中使用流包装器。我的代码如下: 有人有什么想法吗? 我对使用ssh2函数的想法持开放态度,但我强烈希望使用文件系统和流函数。

  • 我可以看到一些贝宝付款已经完成使用贝宝clientId, ClientSecret而其他类型的付款通常nvp付款是使用用户名,密码和签名付款。使用客户端ID和秘密付款与使用用户名、密码和签名付款有什么区别。请对此作出解释。

  • Using a delegation key The collaborator can now push to the repository using Docker Content Trust. Docker will automatically choose and pick the right key for the targets/release role. Edit the file o

  • 本文向大家介绍超级密钥和候选密钥之间的区别,包括了超级密钥和候选密钥之间的区别的使用技巧和注意事项,需要的朋友参考一下 超级键和候选键都用于从表中获取记录。这些键还用于创建表之间的关系。超级键和候选键都用于唯一标识表中的记录。两个键都可以具有空值。 以下是超级键和候选键之间的重要区别。 序号 键 超级钥匙 候选键 1 定义 超级键用于标识关系中的所有记录。 候选键是超级键的子集。 2 使用 所有超

  • 问题内容: 我需要了解超级密钥和复合密钥之间的区别。我发现的例子更加令人困惑。您能简单说明一下有什么区别吗?谢谢 问题答案: 超级键唯一地标识一行。它可以由一列或多列组成。复合键是由多个列组成的键。 如果超级键由多列组成,则它也是一个复合键。 如果复合键唯一地标识一行,则它也是超级键。 我看不到“超级密钥”这个名称使用过多:通常只称其为“唯一密钥”。

  • 我想让我的用户注册一个唯一的用户名和密码(而不是电子邮件密码)。我的一切都在工作,我: 报名啦 > 用户 Q Z q F zW xp d Mfc 0 x my Iq 5 IDAr 1 bz r 2 凭据 电子邮件:myemail@gmail.com 用户名:“MyUsername” 凭据 电子邮件:another@gmail.com 用户名:"另一个 登录 查询数据库以获取与用户名关联的用户,并使

  • 我希望能够对客户机进行身份验证/授权,以产生/消费某些主题的消息。它们将是我们vpn的一部分(包括aws)。根据我对可用文档的理解,唯一的选择是颁发客户端证书并基于客户端DNS设置ACL?不幸的是,我不能使用我的私人CA(我在linux笔记本电脑上创建的)来创建客户端证书。于是出现了以下问题: null 请开导我:) 提前谢谢marcel

  • 本文向大家介绍分组密码和流密码之间的区别,包括了分组密码和流密码之间的区别的使用技巧和注意事项,需要的朋友参考一下 分组和流密码都是加密方法,主要用于将纯文本直接转换为密文,并且属于对称键密码家族。 以下是块密码和流密码之间的重要区别。 序号 键 分组密码 流密码 1 定义 块密码是一种加密类型,通过一次获取其块来执行纯文本转换。 另一方面,“流密码”是一种加密类型,其中,通过一次获取一个字节的纯