From 845f20b2ee10559e0697016fe63af108d0dd8660 Mon Sep 17 00:00:00 2001 From: p31729568 Date: Wed, 23 Oct 2019 14:13:19 +0800 Subject: [PATCH] limit forbid feature --- app/controllers/accounts_controller.rb | 16 ++++-- app/controllers/application_controller.rb | 10 ++++ app/libs/limit_forbid_control.rb | 2 + app/libs/limit_forbid_control/base.rb | 56 +++++++++++++++++++ .../limit_forbid_control/send_email_code.rb | 25 +++++++++ app/libs/limit_forbid_control/user_login.rb | 25 +++++++++ 6 files changed, 129 insertions(+), 5 deletions(-) create mode 100644 app/libs/limit_forbid_control.rb create mode 100644 app/libs/limit_forbid_control/base.rb create mode 100644 app/libs/limit_forbid_control/send_email_code.rb create mode 100644 app/libs/limit_forbid_control/user_login.rb diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index fed6ec280..f515d386d 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -73,15 +73,21 @@ class AccountsController < ApplicationController def login @user = User.try_to_login(params[:login], params[:password]) - if @user - # user is already in local database - return normal_status(-2, "违反平台使用规范,账号已被锁定") if @user.locked? - return normal_status(-2, "错误的账号或密码") unless @user.check_password?(params[:password].to_s) - else + return normal_status(-2, "错误的账号或密码") if @user.blank? + # user is already in local database + return normal_status(-2, "违反平台使用规范,账号已被锁定") if @user.locked? + + login_control = LimitForbidControl::UserLogin.new(@user) + return normal_status(-2, "登录密码出错已达上限,将锁定密码1小时") if login_control.forbid? + + password_ok = @user.check_password?(params[:password].to_s) + unless password_ok + login_control.increment! return normal_status(-2, "错误的账号或密码") end successful_authentication(@user) + login_control.clear # 重置每日密码错误次数 session[:user_id] = @user.id end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 76ffe23d4..6ad488f48 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -86,8 +86,18 @@ class ApplicationController < ActionController::Base when 8, 3, 5 # 邮箱类型的发送 sigle_para = {email: value} + # 60s内不能重复发送 + send_email_limit_cache_key = "send_email_60_second_limit:#{value}" + tip_exception(-2, '请勿频繁操作') if Rails.cache.exist?(send_email_limit_cache_key) + + # 短时间内不能大量发送 + send_email_control = LimitForbidControl::SendEmailCode.new(value) + tip_exception(-2, '邮件发送太频繁,请稍后再试') if send_email_control.forbid? begin UserMailer.register_email(value, code).deliver_now + + Rails.cache.write(send_email_limit_cache_key, 1, expires_in: 1.minute) + send_email_control.increment! # Mailer.run.email_register(code, value) rescue Exception => e logger_error(e) diff --git a/app/libs/limit_forbid_control.rb b/app/libs/limit_forbid_control.rb new file mode 100644 index 000000000..f45a53130 --- /dev/null +++ b/app/libs/limit_forbid_control.rb @@ -0,0 +1,2 @@ +module LimitForbidControl +end \ No newline at end of file diff --git a/app/libs/limit_forbid_control/base.rb b/app/libs/limit_forbid_control/base.rb new file mode 100644 index 000000000..a437f48b6 --- /dev/null +++ b/app/libs/limit_forbid_control/base.rb @@ -0,0 +1,56 @@ +class LimitForbidControl::Base + def initialize + end + + def cache_key + raise 'Please overwrite method :cache_Key' + end + + def forbid_cache_key + "#{cache_key}:forbid" + end + + def allow_times + 5 + end + + def cumulative_expires + 1.days + end + + def forbid_expires + 1.hours + end + + def forbid? + Rails.cache.read(forbid_cache_key) + end + + def increment! + value = Rails.cache.read(cache_key) + value = value.to_i + 1 + + # 锁定 + if value > allow_times.to_i + Rails.cache.write(forbid_cache_key, true, expires_in: forbid_expires) + Rails.cache.delete(cache_key) + else + Rails.cache.write(cache_key, value, expires_in: cumulative_expires) + end + end + + def clear + Rails.cache.delete(forbid_cache_key) + Rails.cache.delete(cache_key) + end + + private + + def redis_cache? + Rails.cache.is_a?(ActiveSupport::Cache::RedisStore) + end + + def day + Time.current.strftime('%Y%m%d') + end +end \ No newline at end of file diff --git a/app/libs/limit_forbid_control/send_email_code.rb b/app/libs/limit_forbid_control/send_email_code.rb new file mode 100644 index 000000000..729446e7c --- /dev/null +++ b/app/libs/limit_forbid_control/send_email_code.rb @@ -0,0 +1,25 @@ +class LimitForbidControl::SendEmailCode < LimitForbidControl::Base + attr_reader :email + + def initialize(email) + super() + @email = email + end + + def allow_times + EduSetting.get('daily_send_email_code_times').presence || 5 + end + + def forbid_expires + num = EduSetting.get('daily_send_email_code_forbid_time').presence.to_i + num.zero? ? 10.minutes : num.to_i.hours + end + + def cumulative_expires + 1.hours + end + + def cache_key + @_cache_key ||= "limit_forbid_control:#{day}:send_email_code:#{email}" + end +end \ No newline at end of file diff --git a/app/libs/limit_forbid_control/user_login.rb b/app/libs/limit_forbid_control/user_login.rb new file mode 100644 index 000000000..882556eb1 --- /dev/null +++ b/app/libs/limit_forbid_control/user_login.rb @@ -0,0 +1,25 @@ +class LimitForbidControl::UserLogin < LimitForbidControl::Base + attr_reader :user + + def initialize(user) + super() + @user = user + end + + def allow_times + EduSetting.get('daily_error_password_times').presence || 5 + end + + def forbid_expires + num = EduSetting.get('daily_error_password_forbid_time').presence.to_i + num.zero? ? 1.hours : num.to_i.hours + end + + def cumulative_expires + 1.days + end + + def cache_key + @_cache_key ||= "limit_forbid_control:#{day}:user_login:#{user.id}" + end +end \ No newline at end of file