-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathproxy_func.lua
More file actions
301 lines (244 loc) · 9.22 KB
/
proxy_func.lua
File metadata and controls
301 lines (244 loc) · 9.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
require "ngx" --ngx库
local config = require "config"
local tools = require "tools"
local ck = require "resty.cookie"
local conn = require "redis_conn"
local cjson = require "cjson"
local checkState = require "check"['checkState']
function dealProxyPass(r, isServerError)
if r then
--关闭redis链接
conn.close(r)
end
--如果服务器异常了,那就需要关闭state
if isServerError then
ngx.log(ngx.INFO, string.format("isServerError is true, deal proxy pass"))
tools.forceCloseSystem()
end
local args = ngx.req.get_uri_args()
local tdcheck = args['_tdcheck']
--如果开启了check
if tdcheck == '1' then
jsonpStr = tools.jsonp('1','')
tools.jsonpSay(jsonpStr)
ngx.exit(ngx.HTTP_OK)
return
end
end
function erroResponse(r)
if r then
--关闭redis链接
conn.close(r)
end
local args = ngx.req.get_uri_args()
local tdcheck = args['_tdcheck']
--如果开启了check
if tdcheck == '1' then
jsonpStr = tools.jsonp('0','')
tools.jsonpSay(jsonpStr)
ngx.exit(ngx.HTTP_OK)
return
end
ngx.exit(400)
end
function deepCheckDeviceId(deviceId, aesKey, remoteIp, remoteAgent, randomSha256)
local trueDeviceContent = tostring(tools.aes128Decrypt(deviceId, aesKey))
ngx.log(ngx.INFO, string.format("deepCheck before, deviceId:%s, trueDeviceContent: %s", deviceId, trueDeviceContent))
if not trueDeviceContent or trueRemoteLastIp == '' then
ngx.log(ngx.ERR, string.format("deepCheckDeviceId aes128Decrypt error, deviceId is %s, remoteIp %s",deviceId, remoteIp))
return false, nil
end
--对加密的deviceid进行解密
local didList = tools.split(trueDeviceContent, ',')
local didIpAgent = didList[1] or ''
local aesEncryptIp = didList[2] or ''
local aesEncryptRandom = didList[3] or ''
ngx.log(ngx.INFO, string.format("deepCheckDeviceId aes128Decrypt, trueDeviceContent:%s | didIpAgent:%s | aesEncryptIp: %s | aesEncryptRandom: %s", trueDeviceContent, didIpAgent, aesEncryptIp, aesEncryptRandom))
--拿到用户加密时用的ip地址
local trueRemoteLastIp = tools.aes128Decrypt(aesEncryptIp, config.globalIpAesKey)
--如果ip解密失败
if not trueRemoteLastIp or trueRemoteLastIp == '' then
ngx.log(ngx.ERR, string.format("deepCheckDeviceId aes128Decrypt trueRemoteLastIp error, trueDeviceContent: %s ", trueDeviceContent))
return false, nil
end
--拿到用户加密时用的随机数
local trueRandom = tools.aes128Decrypt(aesEncryptRandom, config.globalIpAesKey)
--如果随机数解密失败
if not trueRandom or trueRandom == '' then
ngx.log(ngx.ERR, string.format("deepCheckDeviceId aes128Decrypt trueRandom error, trueDeviceContent: %s ", trueDeviceContent))
return false, nil
end
--将随机数转为整数
local trueRandomNum = tonumber(trueRandom)
--如果这个整数不在100万和1000万之间,则报错
if not trueRandomNum or trueRandomNum < 1000000 or trueRandomNum > 10000000 then
ngx.log(ngx.ERR, string.format("deepCheckDeviceId aes128Decrypt trueRandom invalid, trueRandom: %s ", trueRandom))
return false, nil
end
--判断这个随机数是不是和session的randomSha256匹配
local didRandomSha256 = tools.sha256(trueRandomNum..config.md5Gap..config.sessionKey)
if didRandomSha256 ~= randomSha256 then
ngx.log(ngx.ERR, string.format("deepCheckDeviceId didRandomSha256 != randomSha256 trueRandom: %s, sessionRandomSha256: %s ", trueRandom, randomSha256))
return false, nil
end
local expectShaStr = tools.sha256(trueRemoteLastIp..config.md5Gap..remoteAgent)
--检查ip地址是否合法
if didIpAgent ~= expectShaStr then
--记录错误
local lastKeyState = tools.getLastKeyCookie() or 'not found k_st cookie'
ngx.log(ngx.ERR, string.format("deepCheckDeviceId verifyDeviceId IP and agent not valid, remote ip: %s || remote agent: %s", remoteIp,remoteAgent))
ngx.log(ngx.ERR, string.format("deepCheckDeviceId verifyDeviceId IP and agent not valid, client : %s || server : %s || aesClient: %s", didIpAgent, expectShaStr, deviceId))
ngx.log(ngx.ERR, string.format("deepCheckDeviceId verifyDeviceId IP and agent not valid, last get key ip and timestamp: %s", lastKeyState))
return false, nil
end
return true, nil
end
--代理函数
function doProxy()
--检查状态
local gateStateVal, aesKey, aesSecret, remoteAgent, noAgent = checkState()
--如果没有Agent,报错
if noAgent then
return erroResponse()
end
--如果 gateStateVal 为0,表示关闭验证,直接pass
if gateStateVal == '0' then
ngx.log(ngx.ERR, string.format('gateStateVal eq 0, close system or in white ip list'))
return dealProxyPass()
end
--定义变量
local enterTime = tools.getNowTs()
local remoteIp = tools.getRealIp()
local remoteAgent = remoteAgent
--判断sessioncookie是否有效
local isValidCookie, err, randomSha256 = tools.verifySessionCookie()
--出错直接放过
if err then
return dealProxyPass(nil, true)
end
if not isValidCookie then
return erroResponse()
end
--判断deviceId是否有效
local deviceId, err = tools.simpleVerifyDeviceId()
if err then
return dealProxyPass(nil, true)
end
if not deviceId or deviceId == '' then
return erroResponse()
end
--缓存字典对象
local cachDict = ngx.shared.cachDict
--检查deviceId的值是否被篡改
local ok, err = deepCheckDeviceId(deviceId, aesKey, remoteIp, remoteAgent, randomSha256)
--如果deep检查key错误,则要进一步判断是否更改过key
if not ok then
local lastKey = cachDict:get(config.lastGlobalAesKey)
--如果没有lastkey
if lastKey == ngx.null or not lastKey or lastGlobalAesKey == '' then
return erroResponse()
else
local ok, err = deepCheckDeviceId(deviceId, lastKey, remoteIp, remoteAgent, randomSha256)
if not ok then
ngx.log(ngx.ERR, string.format("deepCheckDeviceId twice still error, deviceid: %s, remoteIp:%s", deviceId, remoteIp))
return erroResponse()
end
end
end
--判断此deviceid是否在本地的黑名单中
local blackDict = ngx.shared.blackDict
local isBlack = blackDict:get(deviceId) or nil
if isBlack then
ngx.log(ngx.ERR, string.format("request in local black dict, deviceId %s, remoteIp:%s", deviceId, remoteIp))
return erroResponse()
end
--下面进行redis连接后的检查
local r, err = conn.conn(deviceId)
if err then
ngx.log(ngx.ERR, string.format("doProxy redis connect error %s", err))
--如果连接reids出错
return dealProxyPass(nil, true)
end
--检查此deviceid是否在黑名单中
local blackKey = string.format('black_%s', deviceId)
local isBlack = r:get(blackKey)
--如果在黑名单中
if isBlack ~= ngx.null and isBlack then
ngx.log(ngx.ERR, string.format("request in blackList, deviceId %s, remoteIp:%s", deviceId, remoteIp))
return erroResponse(r)
end
--检查此deviceid是否访问频率过快
local didKey = string.format(config.didKey, deviceId)
local dtsKey = string.format(config.dtsKey, deviceId)
local dipKey = string.format(config.dipKey, remoteIp)
--ngx.log(ngx.ERR,'********************************'..didKey)
--获取上一次请求时间
local didTs = r:get(dtsKey)
--如果没有找到这个deviceid上次请求的时间,则全部新建
if didTs == ngx.null or not didTs then
r:set(dtsKey, enterTime) --设置上次时间戳
r:del(didKey) --删除片的key
r:lpush(didKey,1) --新增片
else
--如果存在上次请求
r:set(dtsKey, enterTime)
--如果上一次请求在10秒钟之内,最新片+1
if enterTime - tonumber(didTs) <= config.freqSec then
local newCount = r:lindex(didKey,0)
--如果list不存在
if newCount == ngx.null or not newCount then
newCount = 0
--将计数+1
local count = newCount + 1
r:lpush(didKey, count)
else
--如果list存在,则把最新片+1
newCount = tonumber(newCount)
--将计数+1
local count = newCount + 1
r:lset(didKey, 0, count)
end
else
--如果上一次请求在10秒钟之外,新建一个片
r:lpush(didKey,1)
r:ltrim(didKey, 0, config.freqShard)
end
end
--进行访问频率判断
--获得最新的6片数据
didCountList, err = r:lrange(didKey, 0, config.freqShard)
if err then
ngx.log(ngx.ERR, string.format("proxy_func r:lrange(didKey, 0, config.freqShard), error: %s", err))
return dealProxyPass(nil, true)
end
tempSum = 0
for i = 1, config.freqShard, 1 do
--记录每个分片的求和
tempSum = tempSum + tonumber(didCountList[i] or 0)
--当满足规则时,表示请求过于频繁
if config.freqRule[i] ~= -1 and tempSum >= config.freqRule[i] then
ngx.log(ngx.ERR, string.format("request too freqency, deviceId %s, rule: %s, remoteIp: %s", deviceId, i, remoteIp))
--访问频繁,本地先做一个黑名单缓存
local succ, err = blackDict:set(deviceId, '1', 60)
if err then
ngx.log(ngx.ERR, string.format("proxy_func blackDict:set, error: %s", err))
end
return erroResponse(r)
end
end
--将此deviceid存入ipkey中,这里要用set的key,保证数组中唯一
r:sadd(dipKey, deviceId)
--更新redis的key的expire过期时间
r:expire(dtsKey, 600)
r:expire(didKey, 600)
r:expire(dipKey, 3600)
--执行proxy
dealProxyPass(r)
--记录时间,进行转发
--如果超过1秒, 记录错误日志
local dealTime = tools.getNowTs() - enterTime
if dealTime > 0.5 then
ngx.log(ngx.ERR, string.format("proxy deal too long : %s", dealTime))
end
end
doProxy()