最近在接受学校实验室的培训,要用 rails 框架实现个博客功能,涉及到登录有个要求,登录次数三次失败要锁定十分钟,然后才能尝试登录。我纯手写的登录功能,没有用到 devise 之类,这个该如何实现?有人可以给个思路吗?用到第三方东西越少越好,或者 memcached 能实现也可。
Rails 的精髓就是:能多用第三方组件的,绝对不要自己写。 回到主题。用 memcache 不是挺简单么,登录失败的时候检查 memcache 对应的值,如果没有就加上次数,如果有就更新次数。把键设置成 10 分钟过期就好了,10 分钟以后键自己消失了也就能再次登录了。
正确的做法是漏水桶模型 https://en.wikipedia.org/wiki/Leaky_bucket
nginx 的 http_limit_req 模块就是这么做的 http://nginx.org/en/docs/http/ngx_http_limit_req_module.html 但是不好配置成针对单用户 id 的
简单的做法可以用 https://github.com/bendiken/rack-throttle 然后设置 client_identifier 不过就不是 leaky bucket 了
自己实现可以更灵活更核心一点
每个用户保存两个字段
bucket:float
bucket_updated_at:time
每次请求的时候
user.bucket = user.bucket - (Time.now - user.bucket_updated_at) * leaking_rate + 1
user.bucket = 0 if user.bucket < 0
user.bucket_updated_at = Time.now
如果 bucket 满 (大于一个设定值) 就拒绝请求
如果只是不管时间间隔,三次就锁定,在用户表中加个连续失败计数字段就可以,并且在成功登录时清空
def show
if Rails.cache.increment("login/ip/#{request.ip}", 0, expires_in: 10.minutes).to_i > 3
render :ip_limit
else
# login form
end
end
def create
if Rails.cache.increment("login/ip/#{request.ip}", 1, expires_in: 10.minutes) > 3
render :ip_limit
else
if success
# login
Rails.cache.delete("login/ip/#{request.ip}")
else
# render :show
end
end
end
补充:
#6 楼 @rei 这样写有点问题,默认的FileStore 的increment在 key 不存在的情况下是不会加 1 的。
文档:Increments an already existing integer value that is stored in the cache. If the key is not found nothing is done.
@jasonpu 推荐你使用 redis 来实现限制用户登录频率 假设当前用户 id 为 2 如何记录用户登录的次数的方法可以看这篇文章来实现。这里就不过多介绍。 http://tise.c2qu.com/2015/07/26/rate-limiting-and-redis-realize
当用户登录的次数超过 10 次时,设置 user:2:sign
key,且过期时间为 600 (10 分钟)
检查 user:2:sign
是否存在,如果存在则给予警告。