ASP.NET身份电话号码令牌寿命和短信限制

本文关键字:NET 身份 电话号码 令牌 ASP | 更新日期: 2023-09-27 18:11:32

我正在使用ASP构建2因子注册API。. NET Identity 2.0.
我想让用户能够按需确认他们的电话号码,所以即使他们在注册时没有确认他们的电话号码,他们也总是可以请求新的令牌(向我的API发出请求),这将通过SMS发送并在页面上输入(也向我的API发出请求)。在负责发送令牌的方法中,我生成令牌并发送它,如下所示:

var token = await UserManager.GeneratePhoneConfirmationTokenAsync(user.Id);
var message = new SmsMessage
{
    Id = token,
    Recipient = user.PhoneNumber,
    Body = string.Format("Your token: {0}", token)
};
await UserManager.SmsService.SendAsync(message);

和UserManager内部:

public virtual async Task<string> GeneratePhoneConfirmationTokenAsync(TKey userId)
{
    var number = await GetPhoneNumberAsync(userId);
    return await GenerateChangePhoneNumberTokenAsync(userId, number);
}

每次我调用我的方法,我得到包含令牌的短信,问题是用户可以无限次调用该方法,很容易产生成本-每条短信=成本。

我想限制用户对该方法的请求数量为每X分钟一次。

我也注意到,当我做多个请求我得到相同的令牌,我已经测试了我的方法,它看起来这个令牌是有效的3分钟,所以如果我做请求在那分钟的时间窗口,我将得到相同的令牌。

理想情况下,我希望有一个参数,允许我指定请求和电话确认令牌寿命之间的时间间隔。

我尝试在UserManager类中设置令牌寿命:

appUserManager.UserTokenProvider = new DataProtectorTokenProvider<User,int>(dataProtectionProvider.Create("ASP.NET Identity"))
{
    TokenLifespan = new TimeSpan(0,2,0)//2 minutes 
};

但这只影响电子邮件确认链接中的令牌。

我需要添加额外的字段到我的用户表,将保持令牌有效期日期,并检查它每次我想生成和发送新的令牌或有更简单的方法?

如何指定ASP. xml的时间间隔?. NET Identity会产生相同的电话号码确认令牌吗?

ASP.NET身份电话号码令牌寿命和短信限制

我不是专家,但我有同样的问题,并在谷歌的一点帮助下找到了这两个线程。

https://forums.asp.net/t/2001843.aspx?Identity + 2 + 0 +两个+因素+认证+使用+ + +和+短信+邮件超时

https://github.com/aspnet/Identity/issues/465

我假设你是正确的,默认的时间限制是3分钟基于asp.net Identity github的讨论。

希望链接的讨论包含您需要配置新的时间限制的答案。

关于速率限制,我使用以下代码,这是松散地基于这个讨论我如何在ASP中实现速率限制。. NET MVC站点?

class RateLimitCacheEntry
{
    public int RequestsLeft;
    public DateTime ExpirationDate;
}
/// <summary>
/// Partially based on
/// https://stackoverflow.com/questions/3082084/how-do-i-implement-rate-limiting-in-an-asp-net-mvc-site
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class RateLimitAttribute : ActionFilterAttribute
{
    private static Logger Log = LogManager.GetCurrentClassLogger();
    /// <summary>
    /// Window to monitor <see cref="RequestCount"/>
    /// </summary>
    public int Seconds { get; set; }
    /// <summary>
    /// Maximum amount of requests to allow within the given window of <see cref="Seconds"/>
    /// </summary>
    public int RequestCount { get; set; }
    /// <summary>
    /// ctor
    /// </summary>
    public RateLimitAttribute(int s, int r)
    {
        Seconds = s;
        RequestCount = r;
    }
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        try
        {
            var clientIP = RequestHelper.GetClientIp(actionContext.Request);
            // Using the IP Address here as part of the key but you could modify
            // and use the username if you are going to limit only authenticated users
            // filterContext.HttpContext.User.Identity.Name
            var key = string.Format("{0}-{1}-{2}",
                actionContext.ActionDescriptor.ControllerDescriptor.ControllerName,
                actionContext.ActionDescriptor.ActionName,
                clientIP
            );
            var allowExecute = false;
            var cacheEntry = (RateLimitCacheEntry)HttpRuntime.Cache[key];
            if (cacheEntry == null)
            {
                var expirationDate = DateTime.Now.AddSeconds(Seconds);
                HttpRuntime.Cache.Add(key,
                    new RateLimitCacheEntry
                    {
                        ExpirationDate = expirationDate,
                        RequestsLeft = RequestCount,
                    },
                    null,
                    expirationDate,
                    Cache.NoSlidingExpiration,
                    CacheItemPriority.Low,
                    null);
                allowExecute = true;
            }
            else
            {
                // Allow and decrement
                if (cacheEntry.RequestsLeft > 0)
                {
                    HttpRuntime.Cache.Insert(key,
                        new RateLimitCacheEntry
                        {
                            ExpirationDate = cacheEntry.ExpirationDate,
                            RequestsLeft = cacheEntry.RequestsLeft - 1,
                        },
                        null,
                        cacheEntry.ExpirationDate,
                        Cache.NoSlidingExpiration,
                        CacheItemPriority.Low,
                        null);
                    allowExecute = true;
                }
            }
            if (!allowExecute)
            {
                Log.Error("RateLimited request from " + clientIP + " to " + actionContext.Request.RequestUri);
                actionContext.Response
                    = actionContext.Request.CreateResponse(
                        (HttpStatusCode)429,
                        string.Format("You can call this {0} time[s] every {1} seconds", RequestCount, Seconds)
                    );
            }
        }
        catch(Exception ex)
        {
            Log.Error(ex, "Error in filter attribute");
            throw;
        }
    }
}
public static class RequestHelper
{
    /// <summary>
    /// Retrieves the client ip address from request
    /// </summary>
    public static string GetClientIp(HttpRequestMessage request)
    {
        if (request.Properties.ContainsKey("MS_HttpContext"))
        {
            return ((HttpContextWrapper)request.Properties["MS_HttpContext"]).Request.UserHostAddress;
        }
        if (request.Properties.ContainsKey(RemoteEndpointMessageProperty.Name))
        {
            RemoteEndpointMessageProperty prop;
            prop = (RemoteEndpointMessageProperty)request.Properties[RemoteEndpointMessageProperty.Name];
            return prop.Address;
        }
        return null;
    }
}

我也看到这个库被推荐过几次:https://github.com/stefanprodan/WebApiThrottle