Showing
6 changed files
with
149 additions
and
2 deletions
@@ -134,6 +134,7 @@ const logger = global.yoho.logger; | @@ -134,6 +134,7 @@ const logger = global.yoho.logger; | ||
134 | // dispatcher | 134 | // dispatcher |
135 | try { | 135 | try { |
136 | const setYohoData = require('./doraemon/middleware/set-yoho-data'); | 136 | const setYohoData = require('./doraemon/middleware/set-yoho-data'); |
137 | + const riskManagement = require('./doraemon/middleware/risk-management'); | ||
137 | const htaccess = require('./doraemon/middleware/htaccess'); | 138 | const htaccess = require('./doraemon/middleware/htaccess'); |
138 | const subDomain = require('./doraemon/middleware/sub-domain'); | 139 | const subDomain = require('./doraemon/middleware/sub-domain'); |
139 | const mobileRefer = require('./doraemon/middleware/mobile-refer'); | 140 | const mobileRefer = require('./doraemon/middleware/mobile-refer'); |
@@ -149,6 +150,7 @@ try { | @@ -149,6 +150,7 @@ try { | ||
149 | 150 | ||
150 | // YOHO 前置中间件 | 151 | // YOHO 前置中间件 |
151 | app.use(setYohoData()); | 152 | app.use(setYohoData()); |
153 | + app.use(riskManagement()); | ||
152 | app.use(htaccess()); | 154 | app.use(htaccess()); |
153 | app.use(subDomain()); | 155 | app.use(subDomain()); |
154 | app.use(mobileRefer()); | 156 | app.use(mobileRefer()); |
@@ -31,7 +31,7 @@ const isHuman = (req, res) => { | @@ -31,7 +31,7 @@ const isHuman = (req, res) => { | ||
31 | delete req.session.apiLimitValidate; | 31 | delete req.session.apiLimitValidate; |
32 | 32 | ||
33 | logger.warn('isHuman', remoteIp); | 33 | logger.warn('isHuman', remoteIp); |
34 | - return robotCheckService.removeBlack(remoteIp, apiLimitValidate).then(() => { | 34 | + return robotCheckService.removeBlack(remoteIp, apiLimitValidate, req.headers.referer).then(() => { |
35 | return res.json({ | 35 | return res.json({ |
36 | code: 200 | 36 | code: 200 |
37 | }); | 37 | }); |
1 | 'use strict'; | 1 | 'use strict'; |
2 | 2 | ||
3 | +const url = require('url'); | ||
3 | const cache = global.yoho.cache.master; | 4 | const cache = global.yoho.cache.master; |
4 | const Promise = require('bluebird'); | 5 | const Promise = require('bluebird'); |
5 | const co = Promise.coroutine; | 6 | const co = Promise.coroutine; |
@@ -17,9 +18,15 @@ const index = co(function* (channel) { | @@ -17,9 +18,15 @@ const index = co(function* (channel) { | ||
17 | }; | 18 | }; |
18 | }); | 19 | }); |
19 | 20 | ||
20 | -const removeBlack = (remoteIp, apiLimitValidate) => { | 21 | +const removeBlack = (remoteIp, apiLimitValidate, referer) => { |
21 | let operations = []; | 22 | let operations = []; |
22 | 23 | ||
24 | + if (referer) { | ||
25 | + let pid = _.get(url.parse(referer, true), 'query.pid'); | ||
26 | + | ||
27 | + pid && operations.push(cache.delAsync(`${pid}:${remoteIp}`)); | ||
28 | + } | ||
29 | + | ||
23 | operations.push(cache.delAsync(`${config.app}:limiter:${remoteIp}`)); | 30 | operations.push(cache.delAsync(`${config.app}:limiter:${remoteIp}`)); |
24 | 31 | ||
25 | // 验证码之后一小时之内不再限制qps | 32 | // 验证码之后一小时之内不再限制qps |
doraemon/middleware/risk-management.js
0 → 100644
1 | +/** | ||
2 | + * 控制路由请求次数 | ||
3 | + * @date: 2018/03/05 | ||
4 | + */ | ||
5 | +'use strict'; | ||
6 | + | ||
7 | +const _ = require('lodash'); | ||
8 | +const cache = global.yoho.cache.master; | ||
9 | +const helpers = global.yoho.helpers; | ||
10 | +const pathToRegexp = require('path-to-regexp'); | ||
11 | +const logger = global.yoho.logger; | ||
12 | + | ||
13 | +const statusCode = { | ||
14 | + code: 4403, | ||
15 | + data: {}, | ||
16 | + message: '亲,您的访问次数过多,请稍后再试哦...' | ||
17 | +}; | ||
18 | + | ||
19 | +const INVALIDTIME = 3600 * 24; // 24h | ||
20 | +const IP_WHITE_LIST = [ | ||
21 | + '106.38.38.146', | ||
22 | + '106.38.38.147', | ||
23 | + '106.39.86.227', | ||
24 | + '218.94.75.58', | ||
25 | + '218.94.75.50', | ||
26 | + '218.94.77.166' | ||
27 | +]; | ||
28 | + | ||
29 | +const _jumpUrl = (req, res, next, result) => { | ||
30 | + if (result.code === 4403) { | ||
31 | + if (req.xhr) { | ||
32 | + res.set({ | ||
33 | + 'Cache-Control': 'no-cache', | ||
34 | + Pragma: 'no-cache', | ||
35 | + Expires: (new Date(1900, 0, 1, 0, 0, 0, 0)).toUTCString() | ||
36 | + }); | ||
37 | + return res.status(403).json(result); | ||
38 | + } | ||
39 | + return res.redirect(`${result.data.url}&refer=${req.originalUrl}`); | ||
40 | + } | ||
41 | + | ||
42 | + return next(); | ||
43 | +}; | ||
44 | + | ||
45 | +module.exports = () => { | ||
46 | + return (req, res, next) => { | ||
47 | + // default open | ||
48 | + if (_.get(req.app.locals.pc, 'close.risk', false)) { | ||
49 | + return next(); | ||
50 | + } | ||
51 | + | ||
52 | + let ip = _.get(req.yoho, 'clientIp', ''); | ||
53 | + let path = req.path || ''; | ||
54 | + let risks = _.get(req.app.locals.pc, 'json.risk', []); | ||
55 | + let router = {}; | ||
56 | + | ||
57 | + risks = [{ | ||
58 | + route: '/kids-brands/', | ||
59 | + requests: 5 | ||
60 | + }]; | ||
61 | + | ||
62 | + logger.debug(`risk => risks: ${JSON.stringify(risks)}, path: ${path}, ip: ${ip}`); // eslint-disable-line | ||
63 | + if (_.isEmpty(path) || _.isEmpty(risks) || IP_WHITE_LIST.indexOf(ip) > -1) { | ||
64 | + return next(); | ||
65 | + } | ||
66 | + | ||
67 | + _.isArray(risks) && risks.some(item => { | ||
68 | + if (item.state === 'off') { | ||
69 | + return false; | ||
70 | + } | ||
71 | + | ||
72 | + if (!item.regRoute) { | ||
73 | + item.regRoute = pathToRegexp(item.route); | ||
74 | + item.interval = parseInt(item.interval, 10); | ||
75 | + item.requests = parseInt(item.requests, 10); | ||
76 | + } | ||
77 | + | ||
78 | + if (item.regRoute.test(path)) { | ||
79 | + router = item; | ||
80 | + return true; | ||
81 | + } | ||
82 | + | ||
83 | + return false; | ||
84 | + }); | ||
85 | + | ||
86 | + logger.debug(`risk => router: ${JSON.stringify(router)}, path: ${path}`); // eslint-disable-line | ||
87 | + if (_.isEmpty(router)) { | ||
88 | + return next(); | ||
89 | + } | ||
90 | + | ||
91 | + let keyPath = `${_.trim(path, '/').replace(/\//g, ':')}`; | ||
92 | + let limitKey = `pc:risk:limit:${keyPath}:${ip}`; | ||
93 | + let configKey = `pc:risk:${keyPath}:${ip}`; | ||
94 | + let checkUrl = helpers.urlFormat('/3party/check', { | ||
95 | + pid: `pc:risk:limit:${keyPath}` | ||
96 | + }); | ||
97 | + | ||
98 | + return Promise.all([ | ||
99 | + cache.getAsync(limitKey), | ||
100 | + cache.getAsync(configKey), | ||
101 | + ]).then(inters => { | ||
102 | + logger.debug(`risk => getCache: ${JSON.stringify(inters)}, path: ${path}`); // eslint-disable-line | ||
103 | + if (inters[0]) { | ||
104 | + return Object.assign({}, statusCode, {data: {url: checkUrl}}); | ||
105 | + } | ||
106 | + | ||
107 | + if (typeof inters[1] === 'undefined') { | ||
108 | + cache.setAsync(configKey, 1, router.interval || 300); | ||
109 | + return {code: 200}; | ||
110 | + } | ||
111 | + | ||
112 | + inters[1] = parseInt(`0${inters[1]}`, 10); | ||
113 | + if (inters[1] <= router.requests) { | ||
114 | + router = []; | ||
115 | + cache.incrAsync(configKey, 1); | ||
116 | + return {code: 200}; | ||
117 | + } | ||
118 | + | ||
119 | + return Promise.all([ | ||
120 | + cache.setAsync(limitKey, 1, INVALIDTIME), | ||
121 | + cache.delAsync(configKey) | ||
122 | + ]).then(() => { | ||
123 | + return Object.assign({}, statusCode, {data: {url: checkUrl}}); | ||
124 | + }); | ||
125 | + }).then(result => { | ||
126 | + logger.debug(`risk => result: ${JSON.stringify(result)}, path: ${path}`); // eslint-disable-line | ||
127 | + return _jumpUrl(req, res, next, result); | ||
128 | + }).catch(e => { | ||
129 | + console.log(`risk => path: ${path}, err: ${e.message}`); | ||
130 | + return next(); | ||
131 | + }); | ||
132 | + }; | ||
133 | +}; |
@@ -48,6 +48,7 @@ | @@ -48,6 +48,7 @@ | ||
48 | "passport-sina": "^0.1.0", | 48 | "passport-sina": "^0.1.0", |
49 | "passport-strategy": "1.x.x", | 49 | "passport-strategy": "1.x.x", |
50 | "passport-weixin": "^0.1.0", | 50 | "passport-weixin": "^0.1.0", |
51 | + "path-to-regexp": "^2.2.0", | ||
51 | "redis": "^2.7.1", | 52 | "redis": "^2.7.1", |
52 | "request": "^2.81.0", | 53 | "request": "^2.81.0", |
53 | "request-ip": "^1.2.2", | 54 | "request-ip": "^1.2.2", |
@@ -5475,6 +5475,10 @@ path-to-regexp@0.1.7: | @@ -5475,6 +5475,10 @@ path-to-regexp@0.1.7: | ||
5475 | version "0.1.7" | 5475 | version "0.1.7" |
5476 | resolved "http://npm.yohops.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" | 5476 | resolved "http://npm.yohops.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" |
5477 | 5477 | ||
5478 | +path-to-regexp@^2.2.0: | ||
5479 | + version "2.2.0" | ||
5480 | + resolved "http://npm.yohops.com/path-to-regexp/-/path-to-regexp-2.2.0.tgz#80f0ff45c1e0e641da74df313644eaf115050972" | ||
5481 | + | ||
5478 | path-type@^1.0.0: | 5482 | path-type@^1.0.0: |
5479 | version "1.1.0" | 5483 | version "1.1.0" |
5480 | resolved "http://npm.yohops.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" | 5484 | resolved "http://npm.yohops.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" |
-
Please register or login to post a comment