在ASP.NET Web API 2中使用Owin OAuth 刷新令牌
2021-03-20 23:24
标签:继承 private san remove get minutes todo direct ras 在上篇文章介绍了Web Api中使用令牌进行授权的后端实现方法,基于WebApi2和OWIN OAuth实现了获取access token,使用token访问需授权的资源信息。本文将介绍在Web Api中启用刷新令牌的后端实现。 本文要用到上篇文章所使用的代码,代码编写环境为VS 2017、.Net Framework 4.7.2,数据库为MS SQL 2008 R2. 上文已经搞了一套Token授权访问,这里有多出来一个刷新令牌(Refresh Token),平白添加程序的复杂度,意义何在呢 刷新令牌(Refresh Token) 是用来从身份认证服务器交换获得新的访问令牌,允许在没有用户交互的情况下请求新的访问令牌。刷新令牌有几个好处: 这个在没有特定的业务场景比较难理解,下面还是一步一步操作一遍,动动手后会有更多收获。本文需要使用进行客户端和刷新令牌的持久化,需要用到EF和数据库客户端。 Client 实体: RefreshToken 实体: 到这里终于完成了Client与RefreshToken两个实体表的管理,这里主要是实现一下Client和RefreshToken这两个实体的一些增删改查操作,在后面会用到。具体实现方式不限于以上代码。 这里有个小插曲,执行数据迁移的时候出现错误 “无法将参数绑定到参数“Path”,因为该参数是空值。”,卡了半个小时也没解决,最后卸载掉当前EntityFramework,换了个低版本的,一切正常了。 现在,我们需要实现负责验证发送给应用程序请求访问令牌或使用刷新令牌的客户端信息的客户端信息的逻辑,因此打开文件“ CustomAuthorizationServerProvider”修改方法ValidateClientAuthentication,代码如下: 上述操作,我们主要执行了一下验证步骤 现在,我们需要修改方法“ GrantResourceOwnerCredentials”以验证资源所有者的用户名/密码是否正确,并将客户端ID绑定到生成的访问令牌,因此打开文件“ CustomAuthorizationServerProvider”并修改GrantResourceOwnerCredentials方法和添加TokenEndpoint实现代码: 通过上面代码,我们完成了如下操作: 现在我们需要生成Refresh Token并实现持久化到数据库中,我们需要添加一个新的实现类。项目中找到Providers文件夹,右键添加类,命名为”CustomRefreshTokenProvider”,该类继承于”IAuthenticationTokenProvider”,实现代码如下: 通过上面代码,我们完成了如下操作: 我们在此方法实现了刷新令牌的设置和生成,并持久化到数据。添加此方法后,在获取access token的过程中,需要将client Id添加到Authorization中,验证通过后,在响应报文中生成了refresh_token 打开CustomRefreshTokenProvider类,添加实现接口方法ReceiveAsync 。代码见上 打开CustomAuthorizationServerProvider类,添加GrantRefreshToken方法的实现,代码如下: 使用PostMan进行模拟测试 在未授权时,访问 http://localhost:56638/api/Orders,提示“已拒绝为此请求授权” 获取授权,访问 http://localhost:56638/oauth/token,获得的报文中包含有refresh_token 使用Refresh token获取新的Access token 使用新的Access Token 附加到Order请求,再次尝试访问: 监视请求上下文中的信息如下,注意newClaim是刷新令牌访问时才设置的: dddd,终于把这两个总结搞完了,回头把之前webapi参数加密的合到一起,代码整理一下放到文末。 本文参照了很多文章以及代码,文章主要架构与下面链接基本一致,其实也没多少原创,但是在整理总结的过程中梳理了一边Access Token 和Refresh Token的知识,当你在组织语言解释代码的时候,才无情的揭露了自己的无知与浅薄,受益匪浅~ Token Based Authentication using ASP.NET Web API 2, Owin, and Identity Enable OAuth Refresh Tokens in AngularJS App using ASP .NET Web API 2, and Owin 其他参考资料是在较多,有的看一点就关掉了。有的作为参考,列举一二 Web API与OAuth:既生access token,何生refresh token 在ASP.NET中基于Owin OAuth使用Client Credentials Grant授权发放Token 使用OAuth打造webapi认证服务供自己的客户端使用 ASP.NET Web API与Owin OAuth:调用与用户相关的Web API C#进阶系列——WebApi 身份认证解决方案:Basic基础认证 最后本文示例代码地址:等我整理完上传~ 在ASP.NET Web API 2中使用Owin OAuth 刷新令牌 标签:继承 private san remove get minutes todo direct ras 原文地址:https://www.cnblogs.com/buyixiaohan/p/11846046.htmlOAuth 刷新令牌
使用刷新令牌
步骤一:设计客户端表、刷新令牌表,启用持久化操作
启用EntityFramework,安装 Nuget包
install-package Entityframework
添加数据实体,项目右键,新建文件夹命名为Entities,然后文件夹右键,新增类命名为OAuthContext,代码如下:
using System.Data.Entity;
namespace OAuthExample.Entities
{
public class OAuthContext : DbContext
{
public OAuthContext() : base("OAuthConnection") { }
public DbSet
添加客户端、刷新令牌实体 ,在文件夹右键,分别新增Client类、RefreshToken类,代码如下:
using System.ComponentModel.DataAnnotations;
namespace OAuthExample.Entities
{
public class Client
{
[Key]
public string Id { get; set; }
[Required]
public string Secret { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
public ApplicationTypes ApplicationType { get; set; }
public bool Active { get; set; }
public int RefreshTokenLifeTime { get; set; }
[MaxLength(100)]
public string AllowedOrigin { get; set; }
}
public enum ApplicationTypes
{
JavaScript = 0,
NativeConfidential = 1
};
}
using System;
using System.ComponentModel.DataAnnotations;
namespace OAuthExample.Entities
{
public class RefreshToken
{
[Key]
public string Id { get; set; }
[Required]
[MaxLength(50)]
public string Subject { get; set; }
[Required]
[MaxLength(50)]
public string ClientId { get; set; }
public DateTime IssuedUtc { get; set; }
public DateTime ExpiresUtc { get; set; }
[Required]
public string ProtectedTicket { get; set; }
}
}
进行数据库迁移 ,在程序包管理器控制台分别运行如下命令:
PM> enable-migrations
PM> add-migration initDatabase
PM> update-database
实现仓储类,在项目中添加文件夹,命名为Infrastructure,然后添加类,命名为 AuthRepository ,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using OAuthExample.Entities;
namespace OAuthExample.Infrastructure
{
public class AuthRepository : IDisposable
{
private OAuthContext context;
public AuthRepository()
{
context = new OAuthContext();
}
public Client FindClient(string clientId)
{
var client = context.Clients.Find(clientId);
return client;
}
public async Taskbool> AddRefreshToken(RefreshToken token)
{
var existingToken = context.RefreshTokens.Where(r => r.Subject == token.Subject && r.ClientId == token.ClientId).SingleOrDefault();
if (existingToken != null)
{
var result = await RemoveRefreshToken(existingToken);
}
context.RefreshTokens.Add(token);
return await context.SaveChangesAsync() > 0;
}
public async Taskbool> RemoveRefreshToken(string refreshTokenId)
{
var refreshToken = await context.RefreshTokens.FindAsync(refreshTokenId);
if (refreshToken != null)
{
context.RefreshTokens.Remove(refreshToken);
return await context.SaveChangesAsync() > 0;
}
return false;
}
public async Taskbool> RemoveRefreshToken(RefreshToken refreshToken)
{
context.RefreshTokens.Remove(refreshToken);
return await context.SaveChangesAsync() > 0;
}
public async Task
步骤二:实现Client验证
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId = string.Empty;
string clientSecret = string.Empty;
Client client = null;
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
if (context.ClientId == null)
{
//Remove the comments from the below line context.SetError, and invalidate context
//if you want to force sending clientId/secrects once obtain access tokens.
context.Validated();
//context.SetError("invalid_clientId", "ClientId should be sent.");
return Task.FromResultobject>(null);
}
using (AuthRepository _repo = new AuthRepository())
{
client = _repo.FindClient(context.ClientId);
}
if (client == null)
{
context.SetError("invalid_clientId", string.Format("Client ‘{0}‘ is not registered in the system.", context.ClientId));
return Task.FromResultobject>(null);
}
if (client.ApplicationType == ApplicationTypes.NativeConfidential)
{
if (string.IsNullOrWhiteSpace(clientSecret))
{
context.SetError("invalid_clientId", "Client secret should be sent.");
return Task.FromResultobject>(null);
}
else
{
if (client.Secret != Helper.GetHash(clientSecret))
{
context.SetError("invalid_clientId", "Client secret is invalid.");
return Task.FromResultobject>(null);
}
}
}
if (!client.Active)
{
context.SetError("invalid_clientId", "Client is inactive.");
return Task.FromResultobject>(null);
}
context.OwinContext.Setstring>("as:clientAllowedOrigin", client.AllowedOrigin);
context.OwinContext.Setstring>("as:clientRefreshTokenLifeTime", client.RefreshTokenLifeTime.ToString());
context.Validated();
return Task.FromResultobject>(null);
}
步骤三:验证资源所有者凭证
///
步骤四:生成Refresh Token,并持久化
using System;
using System.Threading.Tasks;
using log4net;
using Microsoft.Owin.Security.Infrastructure;
using OAuthExample.Entities;
using OAuthExample.Infrastructure;
namespace OAuthExample.Providers
{
public class CustomRefreshTokenProvider : IAuthenticationTokenProvider
{
private ILog logger = LogManager.GetLogger(typeof(CustomRefreshTokenProvider));
public void Create(AuthenticationTokenCreateContext context)
{
throw new NotImplementedException();
}
///
步骤五:使用刷新令牌生成访问令牌
///
代码测试
总结
上一篇:我用过的gitlab api
文章标题:在ASP.NET Web API 2中使用Owin OAuth 刷新令牌
文章链接:http://soscw.com/index.php/essay/66910.html