加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

ASP.NET OAuth授权 – 使用ClientId和Secret以及用户名和密码之

发布时间:2020-12-16 00:14:21 所属栏目:asp.Net 来源:网络整理
导读:我正在尝试在ASP.NET WebAPI 2中实现一个简单的OAuthAuthorizationServerProvider.我的主要目的是学习如何为移动应用程序设置令牌.我希望用户使用用户名密码,然后接收令牌(和刷新令牌,这样一旦令牌过期,他们就不必重新输入凭据).稍后,我希望有机会打开其他应
我正在尝试在ASP.NET WebAPI 2中实现一个简单的OAuthAuthorizationServerProvider.我的主要目的是学习如何为移动应用程序设置令牌.我希望用户使用用户名&密码,然后接收令牌(和刷新令牌,这样一旦令牌过期,他们就不必重新输入凭据).稍后,我希望有机会打开其他应用程序外部使用的API(比如使用Facebook api等……).

以下是我设置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()
});

这是我的SimpleAuthorizationServerProviderOptions实现:

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 vs用户名和密码.我粘贴的代码通过用户名和密码生成令牌,我可以使用该令牌(直到它过期),但是当我尝试获取刷新令牌时,我必须拥有ClientId.

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

解决方法

What I don’t fully understand is the use of ClientId and Secret vs Username and Password. The code I pasted generates a token by username and password and I can work with that token (until it expires),but when I try to get a refresh token,I must have the ClientId.

Also,if a token expires,the correct way is to send the refresh token and get a new token? What if the refresh token gets stolen? isn’t it the same as a username & password getting stolen?

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

刷新令牌只是OAuth2的替代“授权类型”,并且正如您所说的那样,将替换用户的用户名和密码对.此令牌必须被视为机密数据(甚至比访问令牌更加机密),但优于存储用户名和密码.客户端密码:

>如果受到损害,用户可以撤销;
>它的寿命有限(通常是几天或几周);
>它不会公开用户凭据(攻击者只能获取发布刷新令牌的“范围”的访问令牌).

我建议您阅读有关official draft中OAuth 2检查中定义的不同授权类型的更多信息.我还建议您this resource我发现在Web API中首次实现OAuth2时非常有用.

样品申请

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

和刷新令牌授予:

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读