前言:
目前兄弟们对“统计nginx每秒访问次数怎么算”可能比较关心,我们都想要分析一些“统计nginx每秒访问次数怎么算”的相关文章。那么小编在网络上网罗了一些对于“统计nginx每秒访问次数怎么算””的相关文章,希望看官们能喜欢,我们快快来学习一下吧!背景
前几天项目上需要对一个正常登陆接口,以及忘记密码的接口进行防爆破处理,这里我用nginx,redis,以及前端的一些简单的图形拖动来做一个简单的安全机制,可能有不完善的地方,大家可以提出来意见。
技术分析
其实一个接口是无法完全避免接口爆破的,区分人和机器或许可以使用谷歌的图片验证机制,但是我们一般简单项目没必要做那么复杂的,只需要确保不正常的访问频率不会爆破出我们的用户信息,以及让我们机器的处理流量保存在可控范围即可。
实现的效果有下面这几点:
验证码只能60s获取一次 并且3小时内只能获取三次,超过次数提升获取频繁,稍后再试。正常登录1小时内失败6次账号自动锁定,1小时之后自动解锁。获取验证码无论输入的账号存在不存在均显示发送成功,但是实际不存在的账号不会正常发送。
4.登录失败,账号不存在密码错误不再提示账号不存在等等,而是统一显示账号或密码错误。
5.忘记密码前端部分增加滑动校验,60倒计时无法点击发送验证码。前后端共同校验。
6.技术限制系统此接口的访问频率。前端部分
前端部分可以在这个地址看看这几个简单的组件,这次我们就使用最简单的,滑动拖动即可。
<drag-verify ref="dragVerify" :width="width" :height="height" text="请按住滑块拖动" successText="验证通过" :isPassing.sync="isPassing" background="#ccc" completedBg="rgb(105, 231, 251)" handlerIcon="el-icon-d-arrow-right" successIcon="el-icon-circle-check" @passcallback="passcallback" > </drag-verify>
用户滑动之后需要加上60s倒计时,这块我们使用定时器实现即可,以及邮箱和手机号的正确性校验,不正确则弹窗提示。
this.countDown = 60; timer = setInterval(() => { if (this.countDown - 1 >= 0) { this.countDown -= 1; } else { clearInterval(timer); timer = null; } }, 1000);
<el-button disabled type="text" v-show="time > 0"> {{ time > 0 ? `${time}` : "" }} s之后重试</el-button>
验证邮箱手机号可以使用正则校验进行。
mobileReg = /^1\d{10}$/; emailReg = /^([A-Za-z0-9_\-\.\u4e00-\u9fa5])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,8})$/;
前端大体思路就是,进行滑块验证,拖到右边之后,60s之内无法操作,60s到期之后自动复原,
显示倒计时时间。这个只能防止用户在页面上多次点击,造成一个验证的假象,如果直接对后端接口爆破,则无法避免。
后端
这是大概的流程图,图中还有些细节问题下面慢慢讲解。
这块本来我想用java或者kotlin写,但是历史项目用go写的,重写的话还有其他一些改动,所以继续使用golang完成这部分逻辑。
先定义一个结构体,然后我们来分析下需要哪些字段来实现我们的业务。
我们需要一个首次登陆时间,最后一次登陆时间,和总的登录次数足以判断:
验证码间隔登陆失败次数距离第一次登陆失败次数间隔
type CommonLogin struct { CreateTime time.Time LastTime time.Time Times uint8}正常登陆的前置校验
// 登录的前置校验func beforeCommonLoginValid(key string, r *redis.Client, field string) (bool, error) { // redis中是否存在账号 result, err := r.HExists(field, key).Result() if err != nil { fmt.Printf("从redis中获取用户账户失败,账户为: %s", key) return false, err } if result { login := &CommonLogin{} // 存在账号 说明之前登录失败过 且自从上次失败未登录成功过 commonLogin, err := r.HGet(field, key).Result() if err != nil { return false, err } json.Unmarshal([]byte(commonLogin), login) if login.Times < 6 { return true, nil } // 是否在1小时内失败了6次 if login.Times >= 6 { // 否 if time.Now().Sub(login.CreateTime) > time.Hour*1 { // 连续输错6次时长大于1小时 解锁 r.HDel(field, key) return true, nil } else { fmt.Printf("用户%s于1小时之内连续登录失败6次,账号锁定,1小时后重试。", key) return false, nil } } } // redis中不存在重试记录 return true, nil}
在所有的登录判断的出口,调用此方法即可,例如用户名密码错误,acl校验未通过等等。
忘记密码的登录前置校验
其实原理差不多,唯一的区别就是多了一个获取验证码时间间隔校验。
func beforeForgotPasswordValid(key string, r *redis.Client, field string) (bool, error) { // redis中是否存在账号 result, err := r.HExists(field, key).Result() if err != nil { fmt.Printf("从redis中获取用户账户失败,账户为: %s", key) return false, err } login := &CommonLogin{} // 账号存在 if result { commonLogin, err := r.HGet(field, key).Result() if err != nil { return false, err } json.Unmarshal([]byte(commonLogin), login) // 获取验证码间隔时长不能小于60s if time.Now().Sub(login.LastTime) < time.Second*60 { fmt.Printf("用户获取验证码间隔小于60s") return false, nil } if login.Times < 3 { return true, nil } // 是否在1小时内获取了3次 if login.Times >= 3 { // 否 if time.Now().Sub(login.CreateTime) > time.Hour*3 { // 连续输错6次时长大于1小时 解锁 r.HDel(field, key) return true, nil } else { fmt.Printf("用户%s于3小时之内连续获取验证码3次,账号锁定,3小时后重试。", key) return false, nil } } } return true, nil}
忘记密码的后置校验
// 更新获取验证码的时间func afterForgotPasswordValid(key string, r *redis.Client, field string) { login := &CommonLogin{} commonLogin, _ := r.HGet(field, key).Result() json.Unmarshal([]byte(commonLogin), login) // 验证码发送成功 result, _ := r.HExists(field, key).Result() if result { login.Times = login.Times + 1 login.LastTime = time.Now() data, _ := json.Marshal(login) r.HSet(field, key, data) } else { login.Times = 1 login.LastTime = time.Now() login.CreateTime = login.LastTime data, _ := json.Marshal(login) r.HSet(field, key, data) }}使用nginx进行接口访问频率限制
nginx是一个非常强大的中间价,在安全方面,我们可以用它来限制来自于同一机器的访问频率,可以做黑名单功能等等,当然有人会说ip代{过}{滤}理池之类的,我们此次演示的只是简单demo,恶意攻击当然需要专业防护了。
具体google一下,看这两篇官方文档。
具体的配置其实很简单了。
限制远程同ip访问频率。
limit_req_zone $binary_remote_addr zone=perip:10m rate=1r/s;
解释下这段配置的参数:
$binary_remote_addr 表示通过remoteaddr这个标识来做限制,“binary”的目的是缩写内存占用量,是限制同一客户端ip地址
zone=one:10m表示生成一个大小为10M,名字为one的内存区域,用来存储访问的频次信息
rate=1r/s表示允许相同标识的客户端的访问频次,这里限制的是每秒1次,还可以有比如30r/m的
location ^~ /api/xxx { limit_req zone=perip nodelay; limit_req_status 503; proxy_pass http://正确地址; }
上面配置意思就是超过频率返回503,服务不可用。
使用jmeter进行压力测试:1s 10个请求,我们预期只有1个请求成功,其他的返回503.
核心逻辑其实就是上面这些,源码不贴出来了,有不懂的再讨论吧。
标签: #统计nginx每秒访问次数怎么算