Authored by ccbikai(👎🏻🍜)

Merge branch 'feature/geetest' into gray

... ... @@ -13,7 +13,7 @@ const config = global.yoho.config;
const service = require('../models/back-service');
const captchaService = require('../models/captcha-service');
const SIGN_IN = helpers.urlFormat('/passport/login');
const SIGN_IN = helpers.urlFormat('/signin.html');
const logger = global.yoho.logger;
/**
... ... @@ -211,7 +211,7 @@ const sendCodeToMobileAPI = (req, res, next) => {
if (verifyCode) {
let captcha = _.get(req, 'session.captcha');
logger.info(`验证码校验结果${verifyCode.toString() === captcha},user:${verifyCode};server:${captcha}`);
logger.info(`captcha validate result${verifyCode.toString() === captcha},user:${verifyCode};server:${captcha}`);
if ((captcha && verifyCode.toString() === captcha) || (testCode === config.testCode)) {
req.session.backupCaptch.verifyResult = true;
} else {
... ...
... ... @@ -45,7 +45,7 @@ const bind = {
res.render('bind/index', {
bindIndex: true, // js标识
backUrl: helpers.urlFormat('/passport/login'), // 返回的URL链接
backUrl: helpers.urlFormat('/signin.html'), // 返回的URL链接
showHeaderImg: true, // 控制显示头部图片
isPassportPage: true, // 模板中模块标识
sourceType: sourceType, // 第三方登录来源
... ... @@ -69,7 +69,7 @@ const bind = {
let phoneNum = req.query.phoneNum;
res.render('bind/code', {
backUrl: helpers.urlFormat('/passport/login'),
backUrl: helpers.urlFormat('/signin.html'),
showHeaderImg: true,
isPassportPage: true,
sourceType: sourceType,
... ... @@ -97,7 +97,7 @@ const bind = {
if (verify) {
let captcha = _.get(req, 'session.captcha');
logger.info(`验证码校验结果${verify.toString() === captcha},user:${verify};server:${captcha}`);
logger.info(`captcha validate result${verify.toString() === captcha},user:${verify};server:${captcha}`);
if ((captcha && verify.toString() === captcha) || (testCode === config.testCode)) {
BindService.bindCheck(phoneNum, openId, sourceType, areaCode).then(result => {
let data = {
... ... @@ -315,7 +315,7 @@ const bind = {
module: 'passport',
page: 'bind-password',
bindPwd: true, // js标识
backUrl: helpers.urlFormat('/passport/login'), // 返回的URL链接
backUrl: helpers.urlFormat('/signin.html'), // 返回的URL链接
showHeaderImg: true, // 控制显示头部图片
isPassportPage: true, // 模板中模块标识
sourceType: sourceType, // 第三方登录来源
... ...
'use strict';
const _ = require('lodash');
const Geetest = require('geetest');
const captcha = new Geetest({
geetest_id: 'bce95d796bc3058615fdf2ec2c0aef29',
geetest_key: '124c41d3a22aa48f36ae3cd609c51db5'
});
const geetest = {
register(req, res, next) {
captcha.register().then(function(data) {
// data 为一个对象,包含 gt, challenge, success, new_captcha 字段
// success 为 1 表示正常模式,为 0 表示宕机模式(failback, fallback)
// var body = {
// gt: data.geetest_id,
// challenge: data.challenge,
// success: data.success
// };
res.send(data);
}).catch(next);
},
validate(req, res, next) {
let challenge = req.body.geetest_challenge,
validate = req.body.geetest_validate,
seccode = req.body.geetest_seccode;
let errRes = {
code: 400,
message: '验证码错误',
captchaShow: true,
changeCaptcha: true
};
// 使用极验证
let useGeetest = _.get(req.app.locals.wap, 'geetest.validation', true);
if (!useGeetest) {
return next();
}
if (!challenge || !validate || !seccode) {
return res.send(errRes);
}
captcha.validate({
challenge,
validate,
seccode
}).then(function(success) {
if (success) {
return next();
}
return res.send(errRes);
});
}
};
module.exports = geetest;
... ...
... ... @@ -19,7 +19,7 @@ const RegService = require('../models/reg-service');
const AuthHelper = require('../models/auth-helper');
const logger = global.yoho.logger;
const loginPage = `${config.siteUrl}/passport/login`;
const loginPage = `${config.siteUrl}/signin.html`;
function doPassportCallback(openId, nickname, sourceType, req, res) {
let shoppingKey = cookie.getShoppingKey(req);
... ... @@ -112,11 +112,8 @@ const local = {
res.render('login', {
width750: true,
loginIndex: true, // 模板中使用JS的标识
// captchaShow: _.get(req.session, 'login.errorCount') <= 0,
useGeetest: _.get(req.app.locals.wap, 'geetest.validation', true), // 使用极验证
captchaShow: true, // 170306 因为暴力破解密码问题,要求每次都展示验证码
// 返回的URL链接
backUrl: 'javascript:history.go(-1)', // eslint-disable-line
showHeaderImg: true, // 控制显示头部图片
isPassportPage: true, // 模板中模块标识
... ... @@ -132,7 +129,12 @@ const local = {
module: 'passport',
page: 'login',
title: '登录',
reg: true
reg: true,
loadJs: [
{
src: global.yoho.config.geetestJs
}
]
});
},
international: (req, res) => {
... ... @@ -161,12 +163,9 @@ const local = {
res.render('international', {
width750: true,
// 返回的URL链接
backUrl: 'javascript:history.go(-1)', // eslint-disable-line
loginInternational: true, // 模板中使用JS的标识
// captchaShow: _.get(req.session, 'login.errorCount') <= 0,
useGeetest: _.get(req.app.locals.wap, 'geetest.validation', true), // 使用极验证
captchaShow: true, // 170306 因为暴力破解密码问题,要求每次都展示验证码
isPassportPage: true, // 模板中模块标识
headerText: '登录',
... ... @@ -174,21 +173,21 @@ const local = {
countrys: RegService.getAreaData(), // 地区信息列表
module: 'passport',
page: 'international',
title: '国际账号登录'
title: '国际账号登录',
loadJs: [
{
src: global.yoho.config.geetestJs
}
]
});
},
login: (req, res, next) => {
// 170306 因为暴力破解密码问题,要求每次都校验验证码
_.set(req.session, 'login.errorCount', 0);
let count = _.get(req.session, 'login.errorCount');
// 使用极验证
let useGeetest = _.get(req.app.locals.wap, 'geetest.validation', true);
if (count == null) { // eslint-disable-line
_.set(req.session, 'login.errorCount', 3);
}
if (count <= 0) {
// 关闭极验证的时候使用有货自有验证码
if (!useGeetest) {
let captchaInput = req.body.captcha;
let captchaCode = _.get(req.session, 'captcha');
let testCode = req.body.yohobuy;
... ... @@ -201,7 +200,7 @@ const local = {
captchaShow: true
};
logger.info(`验证码校验结果${captchaInput.toString() === captchaCode},user:${captchaInput};server:${captchaCode}`);
logger.info(`captcha validate result${captchaInput.toString() === captchaCode},user:${captchaInput};server:${captchaCode}`);
--req.session.captchaValidCount;
... ... @@ -226,8 +225,6 @@ const local = {
}
passport.authenticate('local', (err, user) => {
let loginSession = req.session.login;
if (err || !user) {
let obj = {
code: 400,
... ... @@ -235,12 +232,6 @@ const local = {
data: ''
};
--loginSession.errorCount;
if (loginSession.errorCount <= 0) {
obj.captchaShow = true;
}
res.json(obj);
} else {
let refer = req.cookies.refer;
... ...
... ... @@ -142,7 +142,7 @@ let verifyMobile = (req, res, next) => {
req.session.captcha = null;
data.changeCaptcha = true;
}
logger.info(`验证码校验结果${captcha.toString() === diffCaptcha},user:${captcha};server:${diffCaptcha}`);
logger.info(`captcha validate result${captcha.toString() === diffCaptcha},user:${captcha};server:${diffCaptcha}`);
// error case: 验证码不匹配
if (!(captcha === diffCaptcha || testCode === config.testCode)) {
... ...
... ... @@ -46,7 +46,6 @@ const _step1 = (req, res, next) => {
let template = 'sms/login';
// 登陆切换到短信登陆
// let viewData = {
// width750: true,
// module: 'passport',
... ... @@ -58,6 +57,30 @@ const _step1 = (req, res, next) => {
// areaCode: '+86', // 默认的区号
// countrys: RegService.getAreaData() // 地区信息列表
// };
// 登陆切换到短信登陆
// let viewData = {
// width750: true,
// module: 'passport',
// page: 'sms-login',
// backUrl: 'javascript:history.go(-1)', // eslint-disable-line
// showHeaderImg: true, // 控制显示头部图片
// title: '手机短信登录',
// isPassportPage: true,
// captchaUrl: helpers.urlFormat('/passport/sms_login/captcha.png', {t: Date.now()}),
// smsLoginUrl: '/passport/sms_login',
// registerUrl: '/passport/reg/index', // 注册的URL链接
// aliLoginUrl: '/passport/login/alipay', // 支付宝快捷登录的URL链接
// weiboLoginUrl: '/passport/login/sina', // 微博登录的URL链接
// qqLoginUrl: '/passport/login/qq', // 腾讯QQ登录的URL链接
// wechatLoginUrl: '/passport/login/wechat', // 微信登录的URL链接
// internationalUrl: '/passport/international', // 国际号登录的URL链接
// phoneRetriveUrl: '/passport/back/mobile', // 通过手机号找回密码的URL链接
// emailRetriveUrl: '/passport/back/email', // 通过邮箱找回密码的URL链接
// areaCode: '+86', // 默认的区号
// countrys: RegService.getAreaData(), // 地区信息列表
// reg: true
// };
let viewData = {
width750: true,
module: 'passport',
... ... @@ -206,7 +229,7 @@ exports.indexCheck = (req, res, next) => {
}
logger.info(`验证码校验结果${captcode.toString() === captcodeValid},user:${captcode};server:${captcodeValid}`);
logger.info(`captcha validate result${captcode.toString() === captcodeValid},user:${captcode};server:${captcodeValid}`);
// 验证
if ([area, mobile].some(val => val === '')) {
return em.emit('reject', Object.assign(errorData, { message: '请填写手机号'}));
... ...
... ... @@ -19,11 +19,16 @@ const smsLogin = require(cRoot + '/sms');
const update = require(cRoot + '/update');
const agreement = require(cRoot + '/app-agreement');
const geetest = require(cRoot + '/geetest');
const router = express.Router(); // eslint-disable-line
router.get('/passport/geetest/register', geetest.register);
// 兼容老的路由
// router.get('/signin.html', login.common.beforeLogin, login.local.loginPage);
router.get('/signin.html', login.common.beforeLogin, smsLogin.loginPage);
router.get('/signin.html', login.common.beforeLogin, login.local.loginPage);
// router.get('/signin.html', login.common.beforeLogin, smsLogin.loginPage);
router.get('/reg.html', disableBFCahce, reg.index);
router.get('/login.html', login.common.beforeLogin, login.local.international);
router.get('/phoneback.html', back.indexMobilePage);
... ... @@ -33,11 +38,13 @@ router.get('/emailback.html', back.indexEmailPage);
router.get('/passport/signout/index', login.local.logout);
// 登录页面
// router.get('/passport/login', login.local.loginPage);
// router.get('/passport/international', login.common.beforeLogin, login.local.international);
router.get('/passport/login', login.common.beforeLogin, login.local.loginPage);
router.get('/passport/international', login.common.beforeLogin, login.local.international);
// 本地登录
// router.post('/passport/login/auth', login.local.login);
router.post('/passport/login/auth', geetest.validate, login.local.login);
// SMS 短信
router.use('/passport/sms_login', login.common.beforeLogin, smsLogin.beforeIn);
... ...
... ... @@ -9,8 +9,8 @@
<div class="input-container row has-eye">
<input id="pwd" class="pwd input" type="password" placeholder="密码">
</div>
<div id="js-img-check" {{#captchaShow }}data-init{{/captchaShow}}></div>
<div data-geetest="{{useGeetest}}" id="js-img-check" {{#captchaShow }}data-init{{/captchaShow}}></div>
<span id="btn-login" class="btn btn-login disble row">登录</span>
</div>
</div>
\ No newline at end of file
</div>
... ...
... ... @@ -8,7 +8,7 @@
<div class="input-container row has-eye">
<input id="pwd" class="pwd input" type="password" placeholder="密码">
</div>
<div id="js-img-check" {{#captchaShow }}data-init{{/captchaShow}}></div>
<div data-geetest="{{useGeetest}}" id="js-img-check" {{#captchaShow }}data-init{{/captchaShow}}></div>
<span id="btn-login" class="btn btn-login disable">登录</span>
<p class="op-container">
<a class="op-item internat" href={{internationalUrl}}>海外手机</a>
... ...
... ... @@ -13,36 +13,5 @@
</div>
{{!--图片验证 end--}}
<button id="btn-next" class="btn btn-next disable row" disabled>获取短信验证码</button>
<p class="op-container">
{{!--<a class="op-item internat" href={{accountUrl}}>账号密码登录</a>--}}
{{!--<a class="op-item internat" href={{internationalUrl}}>海外手机</a>--}}
{{!--<a class="op-item sms-login" href={{smsLoginUrl}}>快速登录</a>--}}
<span id="forget-pwd" class="op-item forget-pwd">忘记密码</span>
</p>
<div class="third-party-login">
<div class="tp-link">
<a class="qq" href={{qqLoginUrl}}></a>
<a class="wechat" href={{wechatLoginUrl}}></a>
<a class="weibo" href={{weiboLoginUrl}}></a>
<a class="alipay" href={{aliLoginUrl}}></a>
</div>
</div>
<div class="login-tip">
<div class="info-icon"></div>
Yoho!Family账号可登录Yoho!Buy有货
</div>
<div id="retrive-pwd-mask" class="mask"></div>
<ul id="retrive-pwd-ways" class="retrive-pwd-ways">
<li>
<a href={{phoneRetriveUrl}}>通过手机找回密码</a>
</li>
<li>
<a href={{emailRetriveUrl}}>通过邮箱找回密码</a>
</li>
<li id="cancel-retrive">
取消
</li>
</ul>
</div>
</div>
... ...
... ... @@ -10,8 +10,8 @@ const isProduction = process.env.NODE_ENV === 'production';
const isTest = process.env.NODE_ENV === 'test';
const domains = {
api: 'http://api-test3.yohops.com:9999/',
service: 'http://service-test3.yohops.com:9999/',
api: 'http://api.yoho.cn/',
service: 'http://service.yoho.cn/',
liveApi: 'http://testapi.live.yohops.com:9999/',
singleApi: 'http://api-test3.yohops.com:9999/',
... ... @@ -101,7 +101,8 @@ module.exports = {
appSecret: 'ce21ae4a3f93852279175a167e54509b',
notifyUrl: domains.service + 'payment/weixin_notify',
},
maxQps: 1200
maxQps: 1200,
geetestJs: '//static.geetest.com/static/tools/gt.js'
};
if (isProduction) {
... ...
... ... @@ -111,5 +111,8 @@
{{#unless devEnv}}
{{> analysis}}
{{/unless}}
{{#loadJs}}
<script type="text/javascript" src="{{src}}"></script>
{{/loadJs}}
</body>
</html>
... ...
... ... @@ -31,6 +31,7 @@
"cookie-parser": "^1.4.3",
"express": "^4.14.1",
"feed": "^0.3.1",
"geetest": "^4.1.1",
"lodash": "^4.17.4",
"md5": "^2.1.0",
"memory-cache": "^0.1.6",
... ...
... ... @@ -4,7 +4,7 @@
* @date: 2015/9/30
*/
var $ = require('yoho-jquery');
var ImgCheck = require('plugin/img-check');
var Validate = require('plugin/validata');
var $account = $('#account'),
$pwd = $('#pwd'),
... ... @@ -25,7 +25,7 @@ var trim = $.trim;
var showErrTip = tip.show;
var imgCheck = new ImgCheck($captcha, {
var validate = new Validate($captcha, {
useREM: {
rootFontSize: 40,
picWidth: 150
... ... @@ -33,7 +33,7 @@ var imgCheck = new ImgCheck($captcha, {
});
if ($captcha.data('init') != null) { //eslint-disable-line
imgCheck.init();
validate.init();
}
// 登录按钮状态切换
... ... @@ -89,86 +89,86 @@ $pwd.bind('input', function() {
// Login
$loginBtn.on('touchstart', function() {
var acc = trim($account.val()),
pwd = trim($pwd.val()),
captcha = null;
if ($loginBtn.hasClass('disable')) {
return;
}
// if (imgCheck.atWorking) {
captcha = imgCheck.getResults();
// if (captcha === '0000') {
// return tip.show(' 请将图片旋转到正确方向');
// }
// }
$loginBtn.text('正在登录...').addClass('disable');
let acc = trim($account.val()),
pwd = trim($pwd.val());
// 验证账号(数字或者邮箱)和密码合理性
if ((/^[0-9]+$/.test(acc) || api.emailRegx.test(acc)) && api.pwdValidate(pwd)) {
let data = {
account: acc,
password: pwd,
yohobuy: $('#yohobuy').val()
};
if (imgCheck.atWorking) {
$.extend(data, {captcha});
let validResult = validate.getResults();
if (typeof validResult === 'string') {
if (validResult === '0000') {
return tip.show(' 请将图片旋转到正确方向');
}
validResult = Promise.resolve({captcha: validResult});
}
validResult.then((captcha) => {
$loginBtn.text('正在登录...').addClass('disable');
$.ajax({
type: 'POST',
url: '/passport/login/auth',
data,
success: function(data) { //eslint-disable-line
var res,
LOGI_TYPE;
if (acc.indexOf('@') > 0) {
LOGI_TYPE = 8;
} else {
LOGI_TYPE = 5;
}
let data = {
account: acc,
password: pwd,
yohobuy: $('#yohobuy').val()
};
if (window._yas && window._yas.sendCustomInfo) {
window._yas.sendCustomInfo({
op: 'YB_MY_LOGIN_C',
param: JSON.stringify({
C_ID: window._ChannelVary[window.cookie('_Channel')],
LOGI_TYPE: LOGI_TYPE
})
}, true);
}
if (validate.atWorking) {
$.extend(data, captcha);
}
if (data.code === 200) {
res = data.data;
$.ajax({
type: 'POST',
url: '/passport/login/auth',
data,
success: function(data) { //eslint-disable-line
var res,
LOGI_TYPE;
if (acc.indexOf('@') > 0) {
LOGI_TYPE = 8;
} else {
LOGI_TYPE = 5;
}
showErrTip('登录成功');
location.href = res.href;
$loginBtn.text('登录成功').off();
} else {
if (data.captchaShow) {
imgCheck.atWorking ? (data.changeCaptcha && imgCheck.refresh()) : imgCheck.init();
if (window._yas && window._yas.sendCustomInfo) {
window._yas.sendCustomInfo({
op: 'YB_MY_LOGIN_C',
param: JSON.stringify({
C_ID: window._ChannelVary[window.cookie('_Channel')],
LOGI_TYPE: LOGI_TYPE
})
}, true);
}
showErrTip(data.message);
resetForm();
}
if (data.code === 200) {
res = data.data;
return data;
},
error: function() {
showErrTip('网络断开连接啦~');
showErrTip('登录成功');
location.href = res.href;
$loginBtn.text('登录成功').off();
} else {
if (data.captchaShow) {
validate.atWorking ? ((data.changeCaptcha || validate.type === 2) && validate.refresh()) : validate.init();
}
imgCheck.atWorking && imgCheck.refresh();
},
complete: function() {
$loginBtn.text('登录').removeClass('disable');
}
});
showErrTip(data.message);
resetForm();
}
return data;
},
error: function() {
showErrTip('网络断开连接啦~');
validate.atWorking && validate.refresh();
},
complete: function() {
$loginBtn.text('登录').removeClass('disable');
}
});
}, () => {});
} else {
showErrTip('账号或密码有错误,请重新输入');
$loginBtn.text('登录').removeClass('disable');
... ...
... ... @@ -8,9 +8,7 @@ var $countrySelect,
$resetBtn,
$captcha,
$captchaPNG,
$phoneNum,
$mask = $('#retrive-pwd-mask'),
$ways = $('#retrive-pwd-ways');
$phoneNum;
var page;
... ... @@ -34,31 +32,7 @@ imgCheck.init();
require('./login/qr-check');
// 显示找回密码面板
function showRetrivePanel() {
$mask.show();
$ways.show();
}
// 隐藏找回密码面板
function hideRetrivePanel() {
$mask.hide();
$ways.hide();
}
$('#forget-pwd').on('touchstart', function() {
showRetrivePanel();
});
$mask.on('touchstart', function() {
hideRetrivePanel();
});
$('#cancel-retrive').on('touchstart', function(e) {
e.preventDefault();
hideRetrivePanel();
});
page = {
init: function() {
this.domInit();
... ... @@ -155,4 +129,4 @@ page = {
$(function() {
page.init();
});
});
\ No newline at end of file
... ...
/**
* 验证码插件
* @author: feng.chen<feng.chen@yoho.cn>
* @date: 2017/03/13
*/
let $ = require('yoho-jquery'),
ImgCheck = require('plugin/img-check');
const validType = {
IMG_CHECK: 1,
GEETEST: 2
};
class Validate {
constructor(container, options) {
this.$container = container;
this.options = options;
this.type = container.data('geetest') ? validType.GEETEST : validType.IMG_CHECK;
if (this.type === validType.IMG_CHECK) {
this.imgCheck = new ImgCheck(this.$container, this.options);
} else {
$.ajax({
url: '/passport/geetest/register?t=' + (new Date()).getTime(), // 加随机数防止缓存(IE下的问题)
type: 'get',
dataType: 'json',
success: data => {
window.initGeetest && window.initGeetest({
gt: data.gt,
challenge: data.challenge,
offline: !data.success
}, (captchaObj) => {
this.captchaObj = captchaObj;
captchaObj.appendTo($('#js-img-check'));
this._atWorking = true;
$('#js-img-check').addClass('hide').addClass('popup');
});
}
});
$('.yoho-page').on('touchstart', '#js-img-check', (e) => {
if ($(e.target).attr('id') !== 'js-img-check') {
return;
}
$('#js-img-check').addClass('hide');
});
}
}
get atWorking() {
return this._atWorking;
}
refresh() {
if (this.type === validType.IMG_CHECK) {
this.imgCheck.refresh();
} else {
this.captchaObj.refresh();
}
}
init() {
if (this.type === validType.IMG_CHECK) {
if (this.$container.data('init') != null) { //eslint-disable-line
this.imgCheck.init();
this._atWorking = this.imgCheck.atWorking;
}
}
}
getResults() {
if (this.type === validType.IMG_CHECK) {
return this.imgCheck.getResults();
} else {
return new Promise((resolve) => {
$('#js-img-check').removeClass('hide');
this.captchaObj.onSuccess(() => {
resolve(this.captchaObj.getValidate());
$('#js-img-check').addClass('hide');
});
});
}
}
}
module.exports = Validate;
... ...
... ... @@ -34,4 +34,24 @@
width: 150px;
height: 150px;
}
}
#js-img-check {
&.hide {
display: none !important;
}
&.popup {
display: flex;
align-items: center;
justify-content: center;
position: fixed;
width: 100%;
height: 100%;
left: 0;
right: 0;
bottom: 0;
top: 0;
z-index: 99;
background-color: rgba(0, 0, 0, 0.3);
}
}
\ No newline at end of file
... ...