? access数据库之Web API与OAuth:既生access token,何生 亚博足球娱乐场注册,亚博娱乐全天彩,亚博官网赢钱不给
VB.net 2010 视频亚博足球娱乐场注册--任意三数字加yabo.com直达官网 VB.net 2010 视频亚博足球娱乐场注册--任意三数字加yabo.com直达官网 VB.net 2010 视频亚博足球娱乐场注册--任意三数字加yabo.com直达官网
SQL Server 2008 视频亚博足球娱乐场注册--任意三数字加yabo.com直达官网 c#入门经典亚博足球娱乐场注册--任意三数字加yabo.com直达官网 Visual Basic从门到精通视频亚博足球娱乐场注册--任意三数字加yabo.com直达官网
当前位置:
首页 > 数据库 > Access数据库 >
  • access数据库之Web API与OAuth:既生access token,何生

  • 2019-07-02 21:39 来源:未知

在前一篇博文中,我们基于 ASP.NET Web API 与 OWIN OAuth 以?Resource Owner Password Credentials Grant 的授权方式( grant_type=password )获取到了 access token,并以这个 token 成功调用了与当前用户(resource owner)关联的 Web API。

亚博官网赢钱不给亚博娱乐全天彩亚博足球娱乐场注册 本以为搞定了 access token 就搞定了 Web API 的验证与授权问题,可是发现 OAuth 中还有一种 token,叫 refresh token。开始的时候很是纳闷,access token 已经能解决问题,为什么要搞定两套 token,refresh token 有啥用?在纳闷之下,发出了这样的感慨:既生 access token,何生 refresh token?

后来看了一些资料,有点明白了。refresh token 是专用于刷新 access token 的 token。

为什么要刷新 access token 呢?一是因为 access token 是有过期时间的,到了过期时间这个 access token 就失效,需要刷新;二是因为一个 access token 会关联一定的用户权限,如果用户授权更改了,这个 access token 需要被刷新以关联新的权限。

为什么要专门用一个 token 去更新 access token 呢?如果没有 refresh token,也可以刷新 access token,但每次刷新都要用户输入登录用户名与密码,多麻烦。有了 refresh token,可以减少这个麻烦,客户端直接用 refresh token 去更新 access token,无需用户进行额外的操作。

两个为什么也许没有解释清楚 refresh token 的用途,下面我们用示例代码在?ASP.NET Web API 与 OWIN OAuth 中实际体验一下,或许有更直观的认识。

(一)Refresh token 的生成、发放、保存

实现一个 RefreshTokenProvider ,比如 CNBlogsRefreshTokenProvider。

需要重载 Microsoft.Owin.Security.Infrastructure.AuthenticationTokenProvider 中的 Create() 与?Receive() 方法(或者直接实现?IAuthenticationTokenProvider 接口),示例代码如下:

复制代码
public class CNBlogsRefreshTokenProvider : AuthenticationTokenProvider
{
    private static ConcurrentDictionary<string, string> _refreshTokens = new ConcurrentDictionary<string, string>();

    public override void Create(AuthenticationTokenCreateContext context)
    {
        string tokenValue = Guid.NewGuid().ToString("n");

        context.Ticket.Properties.IssuedUtc = DateTime.UtcNow;        
        context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddDays(60);

        _refreshTokens[tokenValue] = context.SerializeTicket();

        context.SetToken(tokenValue);
    }

    public override void Receive(AuthenticationTokenReceiveContext context)
    {
        string value;
        if (_refreshTokens.TryRemove(context.Token, out value))
        {
            context.DeserializeTicket(value);
        }
    }
}
复制代码

(注:后来采用的是重载CreateAsync()方法)

然后应用这个 CNBlogsRefreshTokenProvider:

复制代码
public void ConfigureAuth(IAppBuilder app)
{
    OAuthOptions = new OAuthAuthorizationServerOptions
    {
        TokenEndpointPath = new PathString("/token"),
        Provider = new CNBlogsAuthorizationServerProvider(),
        AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
        AllowInsecureHttp = true,
        RefreshTokenProvider = new CNBlogsRefreshTokenProvider()
    };

    app.UseOAuthBearerTokens(OAuthOptions);
}
复制代码

(二)验证持有?refresh token 的客户端

重载?OAuthAuthorizationServerProvider.GrantRefreshToken() 方法,示例代码如下:

复制代码
using Microsoft.Owin.Security.OAuth;

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

            if (originalClient != currentClient)
            {
                context.Rejected();
                return;
            }

            var newId = new ClaimsIdentity(context.Ticket.Identity);
            newId.AddClaim(new Claim("newClaim", "refreshToken"));

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

            await base.GrantRefreshToken(context);
        }
    }
}
复制代码

为了验证client_id,需要在?GrantClientCredentials() 重载方法中保存client_id至context.Ticket:

复制代码
namespace OpenAPI.Providers
{
    public class CNBlogsAuthorizationServerProvider : OAuthAuthorizationServerProvider
    {
        public override async Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
        {
            var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);

            var props = new AuthenticationProperties(new Dictionary<string, string>
                {
                    { "as:client_id", context.ClientId }
                });
            var ticket = new AuthenticationTicket(oAuthIdentity, props);

            context.Validated(ticket);
        }    
    }
}
复制代码

只需实现上面这些代码,其他的都由?Microsoft.Owin.Security.OAuth 帮你代劳了。

(三)测试客户端获取 refresh token

客户端获取 access token 与 refresh token 是一起的,示例代码如下:

复制代码
[Fact]
public async Task GetAccessTokenTest()
{
    var clientId = "[clientId]";
    var clientSecret = "[clientSecret]";

    var parameters = new Dictionary<string, string>();
    parameters.Add("grant_type", "password");            
    parameters.Add("username", "[username]");
    parameters.Add("password", "[password]");

    _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
        "Basic",
        Convert.ToBase64String(Encoding.ASCII.GetBytes(clientId + ":" + clientSecret)));

    var response = await _httpClient.PostAsync("/token", new FormUrlEncodedContent(parameters));
    var responseValue = await response.Content.ReadAsStringAsync();

    Console.WriteLine(responseValue);
}
复制代码

运行结果:

复制代码
{ 
    "access_token": "D3VjxsFvr...",
    "token_type": "bearer",
    "expires_in": 86399,
    "refresh_token": "7f7edd15cba043c29d487235c2276eb1"
}
复制代码

成功拿到了 access token。

(四)测试客户端用 refresh token 刷新 access token

客户端测试代码如下:

复制代码
public async Task GetAccessTokenByRefreshTokenTest()
{
    var clientId = "[clientId]";
    var clientSecret = "[clientSecret]";

    var parameters = new Dictionary<string, string>();
    parameters.Add("grant_type", "refresh_token");
    parameters.Add("refresh_token", "7f7edd15cba043c29d487235c2276eb1");

    _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
        "Basic",
        Convert.ToBase64String(Encoding.ASCII.GetBytes(clientId + ":" + clientSecret)));

    var response = await _httpClient.PostAsync("/token", new FormUrlEncodedContent(parameters));
    var responseValue = await response.Content.ReadAsStringAsync();

    Console.WriteLine(responseValue);
}
复制代码

注:这段客户端代码与前一步中客户端代码的主要区别是少了下面传递?resource owner 用户名与密码的代码,这就是 refresh token 的用途所在 —— 不需要用户名与密码就可以刷新 access token。

parameters.Add("username", "[username]");
parameters.Add("password", "[password]");

运行结果:

复制代码
{
    "access_token": "[new access token]",
    "token_type": "bearer",
    "expires_in": 86399,
    "refresh_token": "[new refresh token]"
}
复制代码

搞定!

看起来挺简单,却折腾了一天。 希望在你折腾OAuth的时候,这篇博文能够帮你减少折腾的时间。

【参考资料】

Adding Refresh Tokens to a Web API v2 Authorization Server

EmbeddedResourceOwnerFlowWithRefreshTokens

Katana source code

Enable OAuth Refresh Tokens in AngularJS App using ASP .NET Web API 2, and Owin

相关亚博足球娱乐场注册--任意三数字加yabo.com直达官网