Refresh Token 机制 C# 完整的示例代码

写这个代码,写了很久。

实际上,项目上很难用到这么复杂的 Token 机制。贴在这里,回向给十方三世的程序员吧。

 

using PortalCore;
using SqlServerModels.舍利弗;
using System;
using System.Threading.Tasks;

namespace TokenCore
{
    /// <summary>
    /// 专家模式:会重新颁发一个会话。
    /// 普通模式:会话永远有效,并不重新颁发。除非失效,或者另行获取,弃用这个。
    /// </summary>
    public class TokenContext_Expert
    {
        const int _会话有效分钟 = 60 * 2; //会话过期,还可以获取颁发
        const int _颁发有效分钟 = 60 * 24 * 7; //颁发过期,就是彻底过期
        const int _重新颁发后赠送分钟 = 1; 

        /// <summary>
        /// 获取会话Token
        /// </summary>
        public static _会话表 GetToken(string accesstoken, string publishtoken)
        {
            try
            {
                return TokenContext_Expert.GetAccessTokenAsync(accesstoken).Result;
            }
            catch (TokenNotFoundException ex)
            {
                throw (ex);
            }
            catch (TokenExpiredException) //如果过期
            {
                try
                {
                    return TokenContext_Expert.PublishTokenAsync(publishtoken).Result;
                }
                catch (TokenNotFoundException ex)
                {
                    throw (ex);
                }
            }
        }

        /// <summary>
        /// 校验token是否有效,鉴权
        /// 如果accesstoken有效,返回token。
        /// 如果accesstoken无效,则抛出异常。
        /// 颁发:本设计会颁发一个新的Token,保留原来的Token一段时间,所以每个Token只能颁发一次。
        /// </summary>
        /// <returns></returns>
        private static async Task<_会话表> GetAccessTokenAsync(string accesstoken)
        {
            _会话表 token = new _会话表();
            try
            {
                token = DB1.SingleByIdAsync<_会话表>(accesstoken).Result;
            }
            catch
            {
                throw new TokenNotFoundException($"找不到 token {accesstoken}");
            }

            if (token.TheLastTime == null) //没有“颁发过一次”,没有最后通牒时间,正常判断
            {
                if (token.PublishTime.AddMinutes(_会话有效分钟) > DateTime.Now) //还没终止
                {
                    return token; //并不自动颁发token
                }
                else
                {
                    throw new TokenExpiredException($"accesstoken {accesstoken} 超时,请使用 publishtoken 颁发新的 token");
                }
            }
            else //颁发过,则受限于赠送的“最后通牒时间”
            {
                if (token.TheLastTime.Value.AddMinutes(_重新颁发后赠送分钟) > DateTime.Now) //已经颁发过,老token还允许用,也就是赠送时间范围内
                {
                    return token;
                }
                else
                {
                    await ClearExpiredTokenAsync();//这里趁机清理
                    throw new TokenNotFoundException($"accesstoken {accesstoken} 已经颁发过且过期,本次调用已执行销毁,请使用你的新 token");
                }
            }
        }

        /// <summary>
        /// 重新颁发一个 Token
        /// </summary>
        /// <returns></returns>
        private static async Task<_会话表> PublishTokenAsync(string publishtoken)
        {
            _会话表 token = new _会话表();
            try
            {
                token = DB1.SingleAsync<_会话表>("select * from _会话表 where publishtoken = @publishtoken", 
                    new { publishtoken }
                    ).Result;
            }
            catch
            {
                throw new TokenNotFoundException($"根据指定的 publishtoken {publishtoken} ,找不到匹配的 token");
            }

            if (token.TheLastTime != null) //这个token已经重新颁发过,此时执行销毁。
            {
                RemoveToken(token.AccessToken);
                throw new TokenNotFoundException($"已经重新颁发过,本次调用已执行销毁 {token.AccessToken}");
            }

            if (token.PublishTime.AddMinutes(_颁发有效分钟) <= DateTime.Now)
            {
                RemoveToken(token.AccessToken);
                throw new TokenNotFoundException($"颁发时间已经过期,本次调用已执行销毁 {token.AccessToken}");
            }

            //如果有值,表示这个token已经失效,赠送了1分钟,用于解决多个请求冲突。1分钟之后再次用旧的token访问,则会拒绝。
            token.TheLastTime = DateTime.Now; 
            await DB1.UpdateAsync(token);

            return await NewTokenAsync(token.UserID, token.UserJson);
        }

        /// <summary>
        /// 生成会话Token
        /// </summary>
        public static async Task<_会话表> NewTokenAsync(string userid, string userjson)
        {
            _会话表 token = new _会话表();
            token.AccessToken = Guid.NewGuid().ToString("N");
            token.PublishToken = Guid.NewGuid().ToString("N");
            token.PublishTime = DateTime.Now;
            token.SigninScene = "web";
            token.UserID = userid;
            token.UserJson = userjson;
            await DB1.InsertAsync<_会话表>(token);
            return token;
        }

        /// <summary>
        /// 删除会话
        /// </summary>
        /// <param name="accesstoken"></param>
        /// <returns></returns>
        public static int RemoveToken(string accesstoken)
        {
            int result = DB1.ExecuteAsync("delete from _会话表 where accesstoken = @accesstoken",new { accesstoken }).Result;
            return result;
        }

        private static async Task ClearExpiredTokenAsync()
        {
            //删除永久淘汰的,和无法颁发的
            await DB1.ExecuteAsync(@"delete from _会话表 
                where (TheLastTime is not null and dateadd(mi,@赠送分钟,TheLastTime) < getdate())
                    or (TheLastTime is null and dateadd(mi,@颁发有效分钟,PublishTime) < getdate())
                ",
                new { 赠送分钟 = _重新颁发后赠送分钟 , 颁发有效分钟 = _颁发有效分钟});
        }
    }

    public class TokenNotFoundException : ApplicationException
    {
        public TokenNotFoundException(string message) : base(message)
        {
        }
    }

    public class TokenExpiredException : ApplicationException
    {
        public TokenExpiredException(string message) : base(message)
        {
        }
    }

}
 

相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页