Web API与OAuth:既生access token,何生refresh token
在前一篇博文中,我们基于 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(); _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(),
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
Enable OAuth Refresh Tokens in AngularJS App using ASP .NET Web API 2, and Owin
Web API与OAuth:既生access token,何生refresh token的更多相关文章
- Designing a Secure REST (Web) API without OAuth
原文:http://www.thebuzzmedia.com/designing-a-secure-rest-api-without-oauth-authentication/ Situation Y ...
- 在ASP.NET Web API 2中使用Owin OAuth 刷新令牌(示例代码)
在上篇文章介绍了Web Api中使用令牌进行授权的后端实现方法,基于WebApi2和OWIN OAuth实现了获取access token,使用token访问需授权的资源信息.本文将介绍在Web Ap ...
- Web API在OWIN下实现OAuth
OAuth(Open Authorization) 为用户资源的授权提供了一个安全的.开放而又简易的标准.与以往的授权方式不同之处是OAuth的授权不会使第三方触及到用户的帐号信息(如用户名与密码), ...
- [ASP.NET] 结合Web API在OWIN下实现OAuth
OAuth(Open Authorization) 为用户资源的授权提供了一个安全的.开放而又简易的标准.与以往的授权方式不同之处是OAuth的授权不会使第三方触及到用户的帐号信息(如用户名与密码), ...
- web api token验证理解
最近一直在学习web api authentication,以Jwt为例,可以这样理解,token是身份证,用户名和密码是户口本,身份证是有有效期的(jwt 有过期时间),且携带方便(自己带有所有信息 ...
- ASP.NET OWIN OAuth:遇到的2个refresh token问题
之前写过2篇关于refresh token的生成与持久化的博文:1)Web API与OAuth:既生access token,何生refresh token:2)ASP.NET OWIN OAuth: ...
- ASP.NET OWIN OAuth:refresh token的持久化
在前一篇博文中,我们初步地了解了refresh token的用途——它是用于刷新access token的一种token,并且用简单的示例代码体验了一下获取refresh token并且用它刷新acc ...
- web api authentication
最近在学习web api authentication,以Jwt为例, 可以这样理解,token是身份证,用户名和密码是户口本, 身份证是有有效期的(jwt 有过期时间),且携带方便(自己带有所有信息 ...
- IdentityServer4专题之四:Authorization Endpoint、Token Endpoint、scope、Access Token和Refresh Token、授权服务器发生错误
1.Authorization Endpoint 它是与用户交互的端点,用户在此进行为客户端应用授权的操作,即authorization grant 2.Token Endpoint 端点,就是一个w ...
随机推荐
- opencv源码:cascadedetect
级联分类器检测类CascadeClassifier,提供了两个重要的方法: CascadeClassifier cascade_classifier; cascade_classifier.load( ...
- 闰秒导致MySQL服务器的CPU sys过高
今天,有个哥们碰到一个问题,他有一个从库,只要是启动MySQL,CPU使用率就非常高,其中sys占比也比较高,具体可见下图. 注意:他的生产环境是物理机,单个CPU,4个Core. 于是,他抓取了CP ...
- 比Mysqli操作数据库更简便的方式 。PDO
下面来说一下PDO 先画一张图来了解一下 mysqli是针对mysql这个数据库扩展的一个类 PDO是为了能访问更多数据库 如果出现程序需要访问其他数据库的话就可以用PDO来做 PDO数据访问抽象层1 ...
- [C#] C# 知识回顾 - 学会使用异常
学会使用异常 在 C# 中,程序中在运行时出现的错误,会不断在程序中进行传播,这种机制称为“异常”. 异常通常由错误的代码引发,并由能够更正错误的代码进行 catch. 异常可由 .NET 的 CLR ...
- C#文件安全管理解析
在实际的项目开发中,我们经常需要使用到文件的I/O操作,主要包含对文件的增改删查等操作,这些基本的操作我们都是很熟悉,但是较少的人去考虑文件的安全和操作的管理等方面,例如文件的访问权限管理,文件数据的 ...
- 拼图小游戏之计算后样式与CSS动画的冲突
先说结论: 前几天写了几个非常简单的移动端小游戏,其中一个拼图游戏让我郁闷了一段时间.因为要获取每张图片的位置,用`<style>`标签写的样式,直接获取计算后样式再用来交换位置,结果就悲 ...
- 屌丝giser成长记-大学篇
作为一名屌丝giser的我,刚接触gis专业是2007年的大一,好悲催,当时gis这个专业是被调剂的,我压根都不知道gis为何物,那时候gis冷门的一逼,报名这个专业的寥寥无几.记得那时候得知被调剂到 ...
- nexus 社区版3.0.2部署、访问
下载nexus社区办(oss): https://www.sonatype.com/download-oss-sonatype 目前最新版本 nexus-3.0.2-02-win64.zip nex ...
- chattr用法
[root@localhost tmp]# umask 0022 一.chattr用法 1.创建空文件attrtest,然后删除,提示无法删除,因为有隐藏文件 [root@localhost tmp] ...
- 《深入理解Java虚拟机》类文件结构
上节学习回顾 在上一节当中,主要以自己的工作环境简单地介绍了一下自身的一些调优或者说是故障处理经验.所谓百变不离其宗,这个宗就是我们解决问题的思路了. 本节学习重点 在前面几章,我们宏观地了解了虚拟机 ...