ASP.NET OWIN OAuth:遇到的2个refresh token问题

之前写过2篇关于refresh token的生成与持久化的博文:1)Web API与OAuth:既生access token,何生refresh token;2)ASP.NET OWIN OAuth:refresh token的持久化

之后我们在CNBlogsRefreshTokenProvider中这样实现了refresh token的生成与持久化:

public class CNBlogsRefreshTokenProvider : AuthenticationTokenProvider
{
    private IRefreshTokenService _refreshTokenService;

    public CNBlogsRefreshTokenProvider(IRefreshTokenService refreshTokenService)
    {
        _refreshTokenService = refreshTokenService;
    }

    public override async Task CreateAsync(AuthenticationTokenCreateContext context)
    {
        if (string.IsNullOrEmpty(context.Ticket.Identity.Name)) return;

        var clientId = context.OwinContext.Get<string>("as:client_id");
        if (string.IsNullOrEmpty(clientId)) return;

        var refreshTokenLifeTime = context.OwinContext.Get<string>("as:clientRefreshTokenLifeTime");
        if (string.IsNullOrEmpty(refreshTokenLifeTime)) return;

        //generate access token
        RandomNumberGenerator cryptoRandomDataGenerator = new RNGCryptoServiceProvider();
        byte[] buffer = new byte[60];
        cryptoRandomDataGenerator.GetBytes(buffer);
        var refreshTokenId = Convert.ToBase64String(buffer).TrimEnd('=').Replace('+', '-').Replace('/', '_');

        var refreshToken = new RefreshToken()
        {
            Id = refreshTokenId,
            ClientId = new Guid(clientId),
            UserName = context.Ticket.Identity.Name,
            IssuedUtc = DateTime.UtcNow,
            ExpiresUtc = DateTime.UtcNow.AddSeconds(Convert.ToDouble(refreshTokenLifeTime)),
            ProtectedTicket = context.SerializeTicket(),
            IP = context.Request.GetUserIp()
        };

        context.Ticket.Properties.IssuedUtc = refreshToken.IssuedUtc;
        context.Ticket.Properties.ExpiresUtc = refreshToken.ExpiresUtc;

        if (await _refreshTokenService.Save(refreshToken))
        {
            context.SetToken(refreshTokenId);
        }
    }

    public override async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
    {
        var refreshToken = await _refreshTokenService.Get(context.Token);

        if (refreshToken != null)
        {
            context.DeserializeTicket(refreshToken.ProtectedTicket);
            var result = await _refreshTokenService.Remove(context.Token);
        }
    }
}
CNBlogsRefreshTokenProvider

后来发现一个问题(这是遇到的第1个问题),在用户不登录的情况下,以client credentials grant方式获取access token时,也会生成refresh token并且保存至数据库。而refresh token是为了解决以resource owner password credentials grant方式获取access token时多次输入用户名与密码的麻烦。所以,对于client credentials grant的场景,生成refresh token完全没有必要。

于是,就得想办法避免这种refresh token生不逢时的情况。后来,找到了解决方法,很简单,只需在CreateAsync的重载方法的开头加上如下的代码:

public class CNBlogsRefreshTokenProvider : AuthenticationTokenProvider
{
    public override async Task CreateAsync(AuthenticationTokenCreateContext context)
    {
        if (string.IsNullOrEmpty(context.Ticket.Identity.Name)) return;
        //...
    }
}

遇到的第2个问题是,Client多次以resource owner password credentials grant的方式获取refresh token,会生成多个refresh token,并且会在数据库中保存多条记录。

通常情况的操作是,Client以resource owner password credentials grant的方式获取refresh token,并之将保存。需要更新access token时就用这个refresh token去更新,更新的同时会生成新的refresh token,并且将原先的refresh token删除。对应的实现代码如下:

public override async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
    var refreshToken = await _refreshTokenService.Get(context.Token);

    if (refreshToken != null)
    {
        context.DeserializeTicket(refreshToken.ProtectedTicket);
        var result = await _refreshTokenService.Remove(context.Token);
    }
}

但是当Client多次获取多个refresh token时,只有那个用于刷新access token的refresh token会被删除,其他的refresh token会成为无人问津的垃圾留在数据库中。为了爱护环境,不乱扔垃圾,我们得解决这个问题。

解决的思路是在生成新的refresh token并将之保存至数据库之前,将对应于这个用户(resource owner)及这个client的所有refresh token删除。删除所依据的条件是ClientId与UserId,由于之前持久化refresh token时只保存了UserName,没有保存UserId,所以要给RefreshToken增加UserId属性。然后给Application层的IRefreshTokenService接口增加删除方法:

public interface IRefreshTokenService
{
    //...
    Task<bool> Remove(Guid clientId, Guid userId);
}

(该方法的实现省略)

接着在CNBlogsRefreshTokenProvider中保存refresh token之前,调用这个方法:

public class CNBlogsRefreshTokenProvider : AuthenticationTokenProvider
{
    private IRefreshTokenService _refreshTokenService;

    public override async Task CreateAsync(AuthenticationTokenCreateContext context)
    {
            var refreshToken = new RefreshToken()
        {
            //...
            UserId = (await UCenterService.GetUser(context.Ticket.Identity.Name)).UserID,
            //...
        };            

        await _refreshTokenService.Remove(refreshToken.ClientId, refreshToken.UserId);

        if (await _refreshTokenService.Save(refreshToken))
        {
            context.SetToken(refreshTokenId);
        }
    }
}

这样就解决了第2个问题。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: .Net 实现 RefreshToken 可以使用 ASP.NET Identity 框架以及 JSON Web Token (JWT)来实现。下面是一个简单的实现步骤: 1. 引入 ASP.NET Identity:首先,在 .Net 项目中引入 ASP.NET Identity 框架。可以通过 NuGet 包管理器安装 Microsoft.AspNet.Identity.Core,Microsoft.AspNet.Identity.EntityFramework 和 Microsoft.AspNet.Identity.Owin 包。 2. 创建身份验证逻辑:创建一个名为 ApplicationUser 的自定义用户模型,并使用 ApplicationUser 类继承 IdentityUser 类。然后,创建一个名为 ApplicationSignInManager 的自定义检查登录逻辑的类,使用 SignInManager 类作为基类。 3. 实现 RefreshToken 功能:在应用中添加一个名为 RefreshToken 的实体类,包含必要属性,如 Token、UserId、ExpiryDate 等。然后,创建一个名为 RefreshTokenService 的服务类,用于处理和生成 RefreshToken 相关的逻辑。这个类可以包含一些方法,如 CreateRefreshToken、ValidateRefreshToken、DeleteRefreshToken 等。 4. 生成和验证 JWT:使用 System.IdentityModel.Tokens.Jwt 包生成和验证 JWT。在需要生成 JWT 的地方,可以使用 JwtSecurityTokenHandler 类来创建包含有效负载和签名的 JWT。可以将 RefreshToken 作为有效负载的一部分。 5. 客户端使用 RefreshToken:当用户的 AccessToken 过期时,客户端可以使用保存的 RefreshToken 向应用的 RefreshToken 端点发送请求获取新的 AccessToken。服务器会验证 RefreshToken 的有效性,如果有效则生成新的 AccessToken 并返回给客户端。 以上是一个基本的实现 RefreshToken 的步骤,具体的代码逻辑可以根据实际需求进行调整和优化。 ### 回答2: 在使用.NET实现Refresh Token的过程中,我们可以借助ASP.NET Core中的Identity框架来实现。首先,我们需要使用Entity Framework Core来创建数据库,并创建用于存储用户信息、TokenRefresh Token等相关表格。 在配置文件中设置Token的有效期限。然后,通过在Startup.cs文件中进行配置,将Identity添加到应用程序的服务中。配置身份验证选项,包括设置JWT Token验证参数。 接下来,我们需要创建一个Token生成器类,用于生成TokenRefresh Token。在这个类中,我们可以使用JWT库来生成Token,并使用Refresh Token作为一个单独的字段添加到Token的负载中。 在用户登录时,我们可以使用ASP.NET Core的身份验证机制生成一个标准的JWT Token,并将Refresh Token存储在数据库中并与用户进行关联。当Token失效时,用户使用Refresh Token来获取一个新的Token。 我们可以创建一个Token刷新端点,用于接收Refresh Token,并检查是否与数据库中存储的Refresh Token匹配。如果匹配成功,则使用之前生成的用户信息生成一个新的Token,并将其返回给用户。 在整个过程中,我们需要注意Token的有效期限以及Refresh Token的存储和更新。可以通过定期清理过期的TokenRefresh Token来确保系统的安全性。 总结来说,通过使用ASP.NET Core中的Identity框架和JWT库,我们可以相对轻松地实现Refresh Token的功能,为系统提供更好的安全性和用户体验。 ### 回答3: .Net实现Refresh Token可以通过以下步骤完成: 1. 首先,在身份验证过程中,用户登录并获得访问令牌(Access Token)。这个令牌是用于对受保护资源进行授权的。 2. 为了实现Refresh Token,我们需要在访问令牌的响应中返回一个刷新令牌(Refresh Token)。它通常是一个长期有效的令牌,并且具有更高的权限,可以用于重新请求新的访问令牌。 3. 将刷新令牌存储在可靠的持久性存储中,例如数据库或缓存。确保对其进行适当的保护和加密,以防止未经授权的访问。 4. 当访问令牌过期时,客户端可以使用刷新令牌请求新的访问令牌。在请求中包括刷新令牌,并向身份验证服务器发送请求。 5. 在服务器端,验证刷新令牌的有效性和正确性。如果刷新令牌是有效的,服务器将针对该用户生成一个新的访问令牌,并将其返回给客户端。 6. 客户端接收到新的访问令牌后,可以继续使用该令牌来访问受保护的资源。 7. 重复以上步骤,直到刷新令牌过期或被撤销。 总结来说,Refresh Token的实现过程中涉及到生成、存储和验证刷新令牌,并使用刷新令牌请求新的访问令牌。这样可以使用户在访问令牌过期时无需重新登录,提供更好的用户体验和安全性。在.Net中,可以使用相关的OAuth2.0和ASP.Net身份验证组件来实现Refresh Token功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值