Authored by 陈轩

save

'use strict';
exports.get = (req, res, next) => {
let data = {
src: 'http://portal.mogujie.com/api/validate/captcha/pc/33gp15as84ob',
code: '9999'
};
res.session.captcha = {
code: '9999'
};
res.json({
src: data.src
});
};
... ...
... ... @@ -15,10 +15,13 @@ var multipartMiddleware = multipart();
const uploadApi = require(cRoot + '/upload.js');
const hotfix = require(`${cRoot}/hotfix`);
const captcha = require(`${cRoot}/captcha`);
// routers
router.post('/api/upload/image', multipartMiddleware, uploadApi.uploadImg);
router.post('/hf/v1', hotfix.v1);
router.get('/api/captcha/get', captcha.get);
module.exports = router;
... ...
... ... @@ -97,6 +97,7 @@ const local = {
});
res.render('login', {
width750: true,
loginIndex: true, // 模板中使用JS的标识
// 返回的URL链接
... ...
... ... @@ -3,11 +3,12 @@
const _ = require('lodash');
const helpers = global.yoho.helpers;
const cookie = global.yoho.cookie;
const EventEmitter = require('events');
const utils = require(global.utils);
const RegService = require('../models/reg-service');
const PhoneService = require('../models/phone-service');
const AuthHelper = require('../models/auth-helper');
const captchaService = require('../models/captcha-service');
const imgcheckAPI = require('../../serverAPI/imgcheck');
// constrant
const CODE_REQUIRED = '请输入校验码';
... ... @@ -42,19 +43,28 @@ const _step1 = (req, res, next) => {
req.session.smsLogin.count = 5;
}
let template = 'sms/login';
let viewData = {
module: 'passport',
page: 'sms-login',
title: '手机短信登录',
isPassportPage: true,
headerText: '手机号码快捷登录',
captchaUrl: helpers.urlFormat('/passport/sms_login/captcha.png', {t: Date.now()}),
areaCode: '+86', // 默认的区号
countrys: RegService.getAreaData() // 地区信息列表
};
imgcheckAPI.gen()
.then(captcha => {
_.set(req.session, 'captcha.code', captcha.data.code);
let template = 'sms/login';
let viewData = {
width750: true,
module: 'passport',
page: 'sms-login',
title: '手机短信登录',
isPassportPage: true,
headerText: '手机号码快捷登录',
captchaUrl: helpers.urlFormat('/passport/sms_login/captcha.png', {t: Date.now()}),
areaCode: '+86', // 默认的区号
countrys: RegService.getAreaData(), // 地区信息列表
captsrc: captcha.data.src
};
res.render(template, viewData);
})
.catch(next);
res.render(template, viewData);
};
// 短信登录 第二步: 输入 校验码
... ... @@ -118,44 +128,59 @@ exports.loginPage = (req, res, next) => {
}
};
exports.tokenBefore = (req, res, next) => {
/**
* step1 的表单提交验证
*/
exports.indexCheck = (req, res, next) => {
_.set(req.session, 'smsLogin.step', 1);
let area = req.query.area = (req.query.area || '').trim();
let mobile = req.query.mobile = (req.query.mobile || '').trim();
let step = _.get(req.session, 'smsLogin.step');
let count = _.get(req.session, 'smsLogin.count');
let interval = _.get(req.session, 'smsLogin.interval');
let captcha1 = _.get(req.session, 'smsLogin.captcha');
let captcha2 = (req.query.captcha || '').trim();
let captcode = (req.body.captcode || '').trim();
let captcodeValid = _.get(req.session, 'captcha.code');
let em = new EventEmitter();
if (!req.xhr) {
return next(404);
}
// 校验 成功
em.on('resolve', () => {
// 1. 将信息放入 session
_.set(req.session, 'smsLogin.area', area);
_.set(req.session, 'smsLogin.mobile', mobile);
_.set(req.session, 'smsLogin.step', 2);
if ([area, mobile].some(val => val === '')) {
return res.json({
code: 401,
message: '手机号 必填'
res.json({
redirect: '/passport/sms_login?step=2'
});
});
// 校验 失败
em.on('reject', error => {
_.set(req.session, 'smsLogin.step', 1);
res.status(400).json(error);
});
// 验证
if ([area, mobile].some(val => val === '')) {
em.emit('reject', {message: '请填写手机号'});
} else if (captcode !== captcodeValid) {
em.emit('reject', {message: '请将图片旋转到正确位置'});
}
delete req.session.smsLogin.captcha; // 图形验证码 一次性
// congratulation~~
em.emit('resolve');
};
// step1 要 校验图形验证码
if (step === 1) {
if (!captcha2) {
return res.json({
code: 400,
message: '请填写验证码'
});
}
exports.tokenBefore = (req, res, next) => {
if (captcha1 !== captcha2) {
return res.json({
code: 400,
message: VERIFY_ERROR
});
}
let step = _.get(req.session, 'smsLogin.step');
let count = _.get(req.session, 'smsLogin.count');
let interval = _.get(req.session, 'smsLogin.interval');
if (!req.xhr || step !== 2) {
return next(404);
}
let now = Date.now();
... ... @@ -186,8 +211,8 @@ exports.tokenBefore = (req, res, next) => {
// AJAX 获取验证码
exports.token = (req, res, next) => {
let area = req.query.area;
let mobile = req.query.mobile;
let area = _.get(req.session, 'smsLogin.area');
let mobile = _.get(req.session, 'smsLogin.mobile');
PhoneService.sendSMS(mobile, area, 1).then(result => {
if (result.code === 200) {
... ... @@ -376,6 +401,7 @@ exports.password = (req, res, next) => {
/**
* 生成 校验码
*/
/*
exports.genCaptcha = (req, res) => {
let captcha = captchaService.generateCaptcha(90, 52, 4);
... ... @@ -386,3 +412,4 @@ exports.genCaptcha = (req, res) => {
.status(200)
.send(captcha.image);
};
*/
... ...
... ... @@ -41,6 +41,7 @@ router.post('/passport/login/auth', login.local.login);
// SMS 短信
router.use('/passport/sms_login', login.common.beforeLogin, smsLogin.beforeIn);
router.get('/passport/sms_login', smsLogin.loginPage);
router.post('/passport/sms_login/step1_check', smsLogin.indexCheck);
router.get('/passport/sms_login/token.json',
smsLogin.tokenBefore,
smsLogin.token); // only ajax;
... ... @@ -48,7 +49,6 @@ router.get('/passport/sms_login/check.json',
smsLogin.checkBefore,
smsLogin.check); // only ajax
router.post('/passport/sms_login/password.json', smsLogin.password);
router.get('/passport/sms_login/captcha.png', smsLogin.genCaptcha);
// 微信登录
router.get('/passport/login/wechat', login.common.beforeLogin, login.wechat.login);
... ...
... ... @@ -7,12 +7,11 @@
<input id="phone-num" class="input phone-num" type="text" placeholder="手机号">
<button class="clear-input" type="button"></button>
</div>
<div class="passport-captcha row">
<div class="passport-captcha-img"><img src="{{captchaUrl}}" alt=""></div>
<div class="passport-captcha-input">
<input id="js-captcha" type="text" placeholder="验证码">
</div>
{{!--图片验证 start--}}
<div id="js-img-check">
<input type="hidden" name="captsrc" value="{{captsrc}}">
</div>
{{!--图片验证 end--}}
<button id="btn-next" class="btn btn-next disable row" disabled>获取短信验证码</button>
</div>
</div>
\ No newline at end of file
... ...
exports.gen = () => {
// TODO
return Promise.resolve({
code: '200',
data: {
src: 'http://portal.mogujie.com/api/validate/captcha/pc/zie3qc7b1tia',
code: '9999'
}
});
};
... ...
'use strict';
const fs = require('fs');
let devHost = '127.0.0.1';
let devHost = '172.16.10.122';
fs.readFile('.devhost', (err, buf)=> {
if (!err) {
... ...
{{!--图片验证--}}
<div class="img-check">
<div class="img-check-header">
<span>请将下列图片点击翻转至正确方向</span>
<a class="img-check-refresh">换一批</a>
</div>
<div class="img-check-main">
<ul class="img-check-pics">
<li class="img-check-pic" data-val="0" style="background-image:url('{{imgSrc}}');"></li>
<li class="img-check-pic" data-val="0" style="background-image:url('{{imgSrc}}');"></li>
<li class="img-check-pic" data-val="0" style="background-image:url('{{imgSrc}}');"></li>
<li class="img-check-pic" data-val="0" style="background-image:url('{{imgSrc}}');"></li>
</ul>
</div>
</div>
\ No newline at end of file
... ...
... ... @@ -17,6 +17,23 @@ tip = require('plugin/tip');
api = require('./api');
checkPoint = require('./smslogin/check-point');
// 图片验证码
let ImgCheck = require('plugin/img-check');
let imgCheck = new ImgCheck('#js-img-check', {
useREM: {
rootFontSize: 40,
picWidth: 150
}
});
imgCheck.init({
imgSrc: $('#js-img-check').find('input').val()
});
page = {
init: function() {
this.domInit();
... ... @@ -28,8 +45,6 @@ page = {
$nextBtn = $('#btn-next');
$phoneNum = $('#phone-num');
$resetBtn = $('.clear-input');
$captcha = $('.passport-captcha input');
$captchaPNG = $('.passport-captcha-img img');
},
bindEvent: function() {
var self = this;
... ... @@ -41,12 +56,6 @@ page = {
self.toggleNextBtn();
});
$captcha.on('input', function() {
self.toggleNextBtn();
});
$captchaPNG.on('click', $.proxy(this.refreshCapatch, this));
$nextBtn.on('click', function() {
self.goNext();
});
... ... @@ -62,7 +71,7 @@ page = {
// 切换$nextBtn disable状态
toggleNextBtn: function() {
var bool = Boolean($.trim($phoneNum.val())) && Boolean($.trim($captcha.val()));
var bool = Boolean($.trim($phoneNum.val()));
$nextBtn
.toggleClass('disable', !bool)
... ... @@ -71,17 +80,11 @@ page = {
$resetBtn.toggle(bool);
},
refreshCapatch: function() {
$captchaPNG.attr('src', '/passport/sms_login/captcha.png?t=' + Date.now());
$captcha.val('');
},
// 提交按钮
goNext: function() {
var self = this;
var areaCode = $countrySelect.val();
var phone = $.trim($phoneNum.val());
var captcha = $.trim($captcha.val());
var captcha = $.trim(imgCheck.getResults());
if ($nextBtn.prop('disabled')) {
return;
... ... @@ -92,11 +95,16 @@ page = {
return;
}
if (captcha === '0000') {
tip.show('请将图片旋转到正确位置');
return;
}
$nextBtn.prop('disabled', true);
$.get('/passport/sms_login/token.json', {
$.post('/passport/sms_login/step1_check', {
area: areaCode.replace('+', ''),
mobile: phone,
captcha: captcha
captcode: captcha
})
.done(function(data) {
if (data.code === 200) {
... ... @@ -104,12 +112,14 @@ page = {
$nextBtn.off();
location.href = data.redirect;
} else {
self.refreshCapatch();
imgCheck.refresh();
tip.show(data.message);
}
})
.fail(function() {
tip.show('出错了, 请重试');
.fail(function(error) {
var message = error && error.message || '';
tip.show(message || '出错了, 请重试');
})
.always(function() {
$nextBtn.prop('disabled', false);
... ...
/**
* Plugin: 图片旋转验证;
* @example
* pc:
* var ImgCheck = require('path/to/img-check')
* var imgCheck = new ImgCheck('#js-img-check', {
* template: require('path/to/hbs')
* }, {
* imgSrc
* })
*
* imgCheck.init({imgSrc})
*
* imgCheck.getResult() // '1230'
*- ----------------------------------------
* @example
* wap:
* var imgCheck = new ImgCheck('#js-img-check', {
* useREM: {
* rootFontSize: 40,
* picWidth: 150
* }
* })
* imgCheck.init({
* imgSrc
* })
*
* imgCheck.getResult() // '1112'
*/
/**
* @param selector|jQuery|Element container
* @param object options 配置选项
* pc:
* {
* template,
* refreshURI
* }
*
* wap:
* {
* useREM: {
* rootFontSize, // 设计稿基准字体大小
* picWidth // 设计稿的验证图片宽度
* },
* template,
* refreshURI
* }
*
* @param object data 初始值
* {
* imgSrc: '图片src'
* }
*
*/
const ImgCheck = function(container, options) {
let optionDefault = {
useREM: null,
template: require('common/img-check.hbs'),
refreshURI: '/api/captcha/get'
};
$.extend(this, optionDefault, options);
this.$container = $(container);
this.$imgCheck = null;
this.$imgPics = null;
this.picWidth = null;
return this;
};
ImgCheck.prototype = {
/**
* method:初始化
* @param object data
* {
* imgSrc
* }
*/
init: function(data) {
if (this.useREM) {
this.picWidth = this.useREM.picWidth / this.useREM.rootFontSize;
}
this.render(data);
this.bindEvents();
},
/**
* method: 绑定事件
*/
bindEvents: function() {
this.$container
.on('click.refresh', '.img-check-refresh', $.proxy(this.refresh, this))
.on('click.rotate', '.img-check-pic', $.proxy(this.rotateImg, this));
},
/**
* method: 渲染 dom
* @param obj data 渲染数据
* {
* imgSrc: 'src' //图片src
* }
*/
render: function(data) {
var self = this;
this.$container.html(this.template(data));
this.$imgPics = this.$container.find('.img-check-pic');
if (!this.useREM && !this.picWidth) {
this.picWidth = this.$imgPics.width();
}
this.$imgPics.each(function(index, elem) {
var $elem = $(elem);
var position = self.calcPosition($elem);
$elem.css('backgroundPosition', position);
});
},
/**
* method: 计算 background-position
* @param num index img-check-pic 的index
* @param num count img-check-pic
*/
calcPosition: function($elem) {
var positionX, positionY;
var index = $elem.index();
var count = parseInt($elem.attr('data-val'), 10);
var unit = this.useREM ? 'rem' : 'px';
count = count % 4;
positionX = -index * this.picWidth + unit;
positionY = -count * this.picWidth + unit;
return [positionX, positionY].join(' ');
},
/**
* Handler method: 刷新验证码
*/
refresh: function() {
this.render({
imgSrc: 'http://portal.mogujie.com/api/validate/captcha/pc/zie3qc7b1tia?' + Date.now()
});
},
/**
* Handler method: 旋转图片
*/
rotateImg: function(event) {
var $pic = $(event.target);
var prevRotateCount = parseInt($pic.attr('data-val'), 10);
$pic.attr('data-val', prevRotateCount + 1);
$pic.css({
backgroundPosition: this.calcPosition($pic)
});
},
/**
* method: 获取结果
* @return string '0123'
*/
getResults: function() {
var result = [];
this.$container.find('.img-check-pic')
.each(function() {
var $elem = $(this);
result.push($elem.attr('data-val'));
});
return result.join('');
}
};
ImgCheck.prototype.construct = ImgCheck;
module.exports = ImgCheck;
... ...
... ... @@ -12,6 +12,7 @@
/* component */
@import "layout/modal";
@import "layout/img-check";
/* module */
... ...
/* ======================= *\
Module: 图片旋转验证
\* ======================= */
.img-check {
margin-top: 45px;
margin-bottom: 30px;
}
.img-check-header {
font-size: 28px;
line-height: 35px;
text-align: left;
margin-bottom: 30px;
color: #B0B0B0;
}
.img-check-refresh {
float: right;
font-size: 22px;
color: #36A74C;
}
.img-check-pics {
display: flex;
justify-content: space-between;
list-style: none;
padding: 0;
margin: 0;
li {
background-size: 600px 600px;
background-repeat: no-repeat;
background-color: #575757;
width: 150px;
height: 150px;
}
}
\ No newline at end of file
... ...