Authored by 梁志锋

Merge remote-tracking branch 'remotes/origin/feature/passport' into release/4.8

... ... @@ -56,7 +56,7 @@ passport.use(new LocalStrategy({
let shoppingKey = cookie.getShoppingKey(req);
AuthHelper.signin(area, username, password, shoppingKey).then((result) => {
AuthHelper.signinAes(area, username, password, shoppingKey).then((result) => {
if (result.code && result.code === 200 && result.data.uid) {
done(null, result.data);
} else {
... ... @@ -104,5 +104,7 @@ passport.use('qq', new QQStrategy({
passport.use('alipay', new AlipayStrategy({
partner: '2088701661478015',
key: 'kcxawi9bb07mzh0aq2wcirsf9znusobw',
callbackURL: `${siteUrl}/passport/login/alipay/callback`
}));
return_url: `${siteUrl}/passport/login/alipay/callback`
}), (profile, done) => {
done(null, profile);
});
... ...
... ... @@ -120,7 +120,7 @@ const setNewPasswordByEmailAPI = (req, res) => {
data: SIGN_IN
};
service.modifyPasswordByEmailAsync(pwd, code)
service.modifyPasswordByEmailAsyncAes(pwd, code)
.then(result => {
if (result.includes('history.back')) {
data.code = 400;
... ... @@ -281,9 +281,8 @@ const setNewPasswordByMobileAPI = (req, res, next) => {
let areaCode = req.body.areaCode || '86';
let newPwd = req.body.password || '';
service.modifyPasswordByMobileAsync(phoneNum, token, newPwd, areaCode)
service.modifyPasswordByMobileAsyncAes(phoneNum, token, newPwd, areaCode)
.then(result => {
console.log(result);
if (result.code === 200) {
res.json({
code: 200,
... ...
... ... @@ -35,7 +35,7 @@ const bind = {
res.render('bind/index', {
bindIndex: true, // js标识
backUrl: helpers.urlFormat('/signin.html'), // 返回的URL链接
backUrl: helpers.urlFormat('passport/login'), // 返回的URL链接
showHeaderImg: true, // 控制显示头部图片
isPassportPage: true, // 模板中模块标识
sourceType: sourceType, // 第三方登录来源
... ... @@ -59,7 +59,7 @@ const bind = {
let phoneNum = req.query.phoneNum;
res.render('bind/code', {
backUrl: helpers.urlFormat('/signin.html'),
backUrl: helpers.urlFormat('passport/login'),
showHeaderImg: true,
isPassportPage: true,
sourceType: sourceType,
... ... @@ -168,12 +168,16 @@ const bind = {
}).then(result => {
let refer = req.cookies.refer;
refer = refer ? decodeURI(refer) : helpers.urlFormat();
refer = refer ? decodeURI(refer) : helpers.urlFormat('/passport/bind/success?type=bind');
if (result && result.code && result.code === 200 && result.data.uid) {
AuthHelper.syncUserSession(result.data.uid, req, res);
result.data.refer = refer;
return AuthHelper.syncUserSession(result.data.uid, req, res).then(() => {
result.data.refer = refer;
return result;
});
} else {
return { code: 400, message: '绑定失败', data: '' };
}
return result;
}).then(result => {
res.json(result);
}).catch(next);
... ... @@ -200,10 +204,14 @@ const bind = {
let refer = helpers.urlFormat('/passport/bind/success', { sourceType: sourceType });
if (result && result.code && result.code === 200 && result.data.uid) {
AuthHelper.syncUserSession(result.data.uid, req, res);
result.data.refer = refer;
return AuthHelper.syncUserSession(result.data.uid, req, res).then(() => {
result.data.refer = refer;
return result;
});
} else {
return { code: 400, message: '关联失败', data: '' };
}
return result;
}).then(result => {
res.json(result);
}).catch(next);
... ... @@ -212,11 +220,38 @@ const bind = {
}
},
passwordPage: (req, res) => {
let openId = req.query.openId;
let sourceType = req.query.sourceType;
let areaCode = req.query.areaCode || '86';
let phoneNum = req.query.phoneNum;
let code = req.query.code;
res.render('bind/password', {
module: 'passport',
page: 'bind-password',
bindPwd: true, // js标识
backUrl: helpers.urlFormat('/passport/login'), // 返回的URL链接
showHeaderImg: true, // 控制显示头部图片
isPassportPage: true, // 模板中模块标识
sourceType: sourceType, // 第三方登录来源
openId: openId, // openId
areaCode: areaCode, // 国别码
phoneNum: phoneNum, // 国别码
code: code // 验证码
});
},
successPage: (req, res) => {
let refer = req.cookies.refer;
let type = req.query.type;
refer = refer ? decodeURI(refer) : helpers.urlFormat('/?go=1');
res.render('bind/success', {
isPassportPage: true,
successTip: '恭喜您,第三方账号关联手机号码成功!',
goUrl: helpers.urlFormat(),
successTip: type === 'bind' ? '恭喜您,第三方账号绑定手机号码成功!' : '恭喜您,第三方账号关联手机号码成功!',
goUrl: refer,
module: 'passport',
page: 'bind-success',
title: '绑定手机号'
... ...
... ... @@ -14,7 +14,7 @@ const log = global.yoho.logger;
const config = global.yoho.config;
const AuthHelper = require('../models/auth-helper');
const loginPage = `${config.siteUrl}/passport/login/index`;
const loginPage = `${config.siteUrl}/passport/login`;
function doPassportCallback(openId, nickname, sourceType, req, res) {
let shoppingKey = cookie.getShoppingKey(req);
... ... @@ -45,6 +45,8 @@ function doPassportCallback(openId, nickname, sourceType, req, res) {
}).then((redirectTo) => {
return res.redirect(redirectTo);
});
} else {
return Promise.reject('missing third party login openId or nickname');
}
}
... ... @@ -55,7 +57,7 @@ const common = {
if (!refer) {
refer = req.get('Referer');
}
refer && !/signin|login/.test(refer) && res.cookie('refer', encodeURI(refer), {
refer && !/signin|login|passport/.test(refer) && res.cookie('refer', encodeURI(refer), {
domain: 'yohobuy.com'
});
next();
... ... @@ -64,19 +66,30 @@ const common = {
const local = {
loginPage: (req, res) => {
// 先清除cookie
res.clearCookie('LE' + md5('_LOGIN_EXPIRE'), {
domain: 'yohobuy.com'
});
// 设置登录有效时间30分钟, 防机器刷,cache不稳定,改为cookie
res.cookie('LE' + md5('_LOGIN_EXPIRE'), (new Date()).getTime() / 1000 + 1800);
// 清除cookie
res.clearCookie('_UID');
res.clearCookie('_TOKEN');
res.clearCookie('_UID', {
domain: 'yohobuy.com'
});
res.clearCookie('_TOKEN', {
domain: 'yohobuy.com'
});
res.render('login', {
loginIndex: true, // 模板中使用JS的标识
backUrl: 'javascript:history.go(-1)', // eslint-disable-line // 返回的URL链接
// 返回的URL链接
backUrl: 'javascript:history.go(-1)', // eslint-disable-line
showHeaderImg: true, // 控制显示头部图片
isPassportPage: true, // 模板中模块标识
registerUrl: '/reg.html', // 注册的URL链接
registerUrl: '/passport/reg/index', // 注册的URL链接
aliLoginUrl: '/passport/login/alipay', // 支付宝快捷登录的URL链接
weiboLoginUrl: '/passport/login/sina', // 微博登录的URL链接
qqLoginUrl: '/passport/login/qq', // 腾讯QQ登录的URL链接
... ... @@ -203,8 +216,9 @@ const qq = {
log.error(`qq authenticate error : ${JSON.stringify(err)}`);
return res.redirect(loginPage);
}
let nickname = user.nickname;
let openId = user.openid;
let openId = user.id;
doPassportCallback(openId, nickname, 'qq', req, res).catch(next);
})(req, res, next);
... ...
... ... @@ -26,7 +26,7 @@ let index = (req, res) => {
res.render('reg/index', {
title: '注册',
backUrl: 'javascript:history.go(-1)', // eslint-disable-line
backUrl: 'javascript:history.go(-1)', // eslint-disable-line
headerText: '注册', // 头部信息
isPassportPage: true, // 模板中模块标识
areaCode: '+86', // 默认的区号
... ... @@ -235,14 +235,13 @@ let setPassword = (req, res, next) => {
let shoppingKey = cookie.getShoppingKey(req);
// 验证注册的标识码是否有效
RegService.regMobile(area, mobile, password, shoppingKey).then((result) => {
RegService.regMobileAes(area, mobile, password, shoppingKey).then((result) => {
if (!result.code || result.code !== 200) {
return Promise.reject(result);
}
if (!result.data || !result.data.uid) {
return Promise.reject(result);
}
return AuthHelper.syncUserSession(result.data.uid, req, res);
}).then(() => {
// 返回跳转到来源页面
... ...
/**
* 登录注册密码加密
* @author: wsl<shuiling.wang@yoho.cn>
* @date: 2016/07/07
*/
'use strict';
const crypto = require('crypto');
const aesPwd = (pwd) => {
let algorithm = 'aes-128-ecb';
let key = 'yoho9646yoho9646';
let clearEncoding = 'utf8';
let cipherEncoding = 'base64';
let iv = '';
let cipher = crypto.createCipheriv(algorithm, key, iv);
let cipherChunks = [];
cipherChunks.push(cipher.update(pwd, clearEncoding, cipherEncoding));
cipherChunks.push(cipher.final(cipherEncoding));
return cipherChunks.join('');
};
module.exports = {
aesPwd
};
... ...
'use strict';
const aes = require('./aes-pwd');
const sign = global.yoho.sign;
const api = global.yoho.API;
... ... @@ -20,6 +20,21 @@ class Auth {
return api.post('', param);
}
static signinAes(area, profile, password, shoppingKey) {
let param = {
method: 'app.passport.signinAES',
area: area,
profile: profile,
password: aes.aesPwd(password)
};
if (shoppingKey) {
param.shopping_key = shoppingKey;
}
return api.post('', param);
}
static signinByOpenID(nickname, openId, sourceType, shoppingKey) {
let param = {
nickname: nickname,
... ...
... ... @@ -6,6 +6,7 @@
const api = global.yoho.API;
const aes = require('./aes-pwd');
const YOHOBUY_URL = 'http://www.yohobuy.com/';
... ... @@ -66,6 +67,20 @@ const modifyPasswordByEmailAsync = (pwd, code) => {
};
/**
* 根据邮箱验证码修改密码(调用新接口 采用AES密码加密)
*
* @param string pwd 新密码
* @param string code 邮箱验证码
*/
const modifyPasswordByEmailAsyncAes = (pwd, code) => {
return api.get('', {
code: code,
newPwd: aes.aesPwd(pwd),
method: 'app.register.resetPwdByCodeAES'
});
};
/**
* 通过手机找回密码
*
* @param string mobile 手机号
... ... @@ -113,12 +128,24 @@ const modifyPasswordByMobileAsync = (mobile, token, newpwd, area) => {
});
};
const modifyPasswordByMobileAsyncAes = (mobile, token, newpwd, area) => {
return api.get('', {
mobile: mobile,
token: token,
newpwd: aes.aesPwd(newpwd),
area: area,
method: 'app.register.changepwdByMobileCodeAES'
});
};
module.exports = {
getAreaDataAsync,
sendCodeToEmailAsync,
modifyPasswordByEmailAsync,
modifyPasswordByEmailAsyncAes,
sendCodeToMobileAsync,
validateMobileCodeAsync,
modifyPasswordByMobileAsync
modifyPasswordByMobileAsync,
modifyPasswordByMobileAsyncAes
};
... ...
... ... @@ -9,7 +9,7 @@
const util = require('util');
const _ = require('lodash');
const md5 = require('md5');
const Strategy = require('passport-strategy');
const passport = require('passport-strategy');
// 支付宝网关地址
const ALIPAY_URL = 'https://mapi.alipay.com/gateway.do';
... ... @@ -30,52 +30,50 @@ function paramsToRaw(params) {
let keys = Object.keys(params);
keys = keys.sort();
let newArgs = {};
let string = '';
keys.forEach((key) => {
newArgs[key] = params[key];
string += '&' + key + '=' + params[key];
});
let string = '';
for (let k of newArgs) {
string += '&' + k + '=' + newArgs[k];
}
string = string.substr(1);
return string;
}
function AlipayStrategy(options) {
Strategy.call(this);
function AlipayStrategy(options, verify) {
if (typeof options === 'function') {
verify = options;
options = {};
}
options = options || {};
passport.Strategy.call(this);
this.name = 'alipay';
this._passReqToCallback = options.passReqToCallback;
this._verify = verify;
this._options = options;
}
util.inherits(AlipayStrategy, Strategy);
util.inherits(AlipayStrategy, passport.Strategy);
AlipayStrategy.prototype.authenticate = (req, options) => {
if (req.query && req.query.is_success && req.query.sign && req.query.sign_type) {
// sign check
AlipayStrategy.prototype.authenticate = function(req, options) {
if (req.query && req.query.is_success && req.query.sign && req.query.sign_type) {
let query = req.query;
let sign = query.sign;
let signType = query.sign_type;
delete query.sign_type;
delete query.sign;
let signString = paramsToRaw(query) + options.key;
let signString = paramsToRaw(query) + this._options.key;
if (signType === 'MD5' && sign !== md5(signString)) {
this.error('alipay callback sign check fail');
this.fail('alipay callback sign check fail');
return;
}
if (req.query.is_success === 'T') {
let user = {
userId: req.query.user_id,
realName: req.query.realName,
realName: req.query.real_name,
email: req.query.email
};
... ... @@ -85,12 +83,14 @@ AlipayStrategy.prototype.authenticate = (req, options) => {
this.fail(req.error_code);
}
} else {
let params = _.extends(defaultOptions, options);
let params = _.assign(defaultOptions, this._options, options);
let signType = params.sign_type;
let key = params.key;
delete params.sign_type;
delete params.sign;
let signString = paramsToRaw(params) + options.key;
delete params.key;
let signString = paramsToRaw(params) + key;
if (signType === 'MD5') {
params.sign = md5(signString);
... ... @@ -101,4 +101,7 @@ AlipayStrategy.prototype.authenticate = (req, options) => {
}
};
module.exports.Strategy = AlipayStrategy;
exports = module.exports = AlipayStrategy;
exports.Strategy = AlipayStrategy;
... ...
... ... @@ -8,6 +8,7 @@
'use strict';
const api = global.yoho.API;
const aes = require('./aes-pwd');
const RegService = {
getAreaData() {
... ... @@ -94,6 +95,20 @@ const RegService = {
}
return api.post('', params);
},
regMobileAes(area, mobile, password, shoppingKey) {
let params = {
method: 'app.passport.registerAES',
area: area,
profile: mobile,
password: aes.aesPwd(password)
};
if (shoppingKey) {
params.shopping_key = shoppingKey;
}
return api.post('', params);
}
};
... ...
... ... @@ -37,6 +37,10 @@ router.get('/login/sina/callback', login.sina.callback);
router.get('/login/qq', login.common.beforeLogin, login.qq.login);
router.get('/login/qq/callback', login.qq.callback);
// 支付宝登录
router.get('/login/alipay', login.common.beforeLogin, login.alipay.login);
router.get('/login/alipay/callback', login.alipay.callback);
// 登录绑定
router.get('/bind/index', bind.indexPage);
router.post('/bind/bindCheck', bind.bindCheck);
... ... @@ -45,6 +49,7 @@ router.post('/bind/sendBindMsg', bind.sendBindMsg);
router.post('/bind/bindMobile', bind.bindMobile);
router.post('/bind/relateMobile', bind.relateMobile);
router.get('/bind/password', bind.passwordPage);
router.get('/bind/success', bind.successPage);
router.post('/bind/changeCheck', bind.changeCheck);
... ...
require('./bind/password');
... ...
... ... @@ -3,7 +3,7 @@
* @author: xuqi<qi.xu@yoho.cn>
* @date: 2015/10/8
*/
var $ = require('jquery');
var $ = require('yoho-jquery');
var $pwd = $('#pwd'),
$btnSure = $('#btn-sure');
... ... @@ -14,8 +14,7 @@ var tip = require('../../plugin/tip');
var trim = $.trim;
var showErrTip = tip.show;
var nickname = $('#nickname').val(),
sourceType = $('#sourceType').val(),
var sourceType = $('#sourceType').val(),
openId = $('#openId').val(),
phoneNum = $('#phone-num').val(),
areaCode = $('#area-code').val().replace('+', ''),
... ... @@ -30,7 +29,6 @@ function startBind(password) {
phoneNum: phoneNum,
openId: openId,
sourceType: sourceType,
nickname: nickname,
password: password,
code: code
},
... ...
... ... @@ -9,7 +9,7 @@
@import "channel/index";
@import "product/index";
@import "activity/index";
/* @import "passport/index"; */
@import "passport/index";
@import "guang/index";
@import "cart/chose-panel";
@import "me/index";
... ...
@import 'suggest';
... ...
.yoho-suggest-page {
width: 100%;
height: auto;
/* 意见反馈头部 */
.suggest-header {
text-align: center;
color: #fff;
font-size: 26px;
line-height: 46px;
overflow: hidden;
padding-bottom: 20px;
background-image: linear-gradient(#383838, #505050);
&:before {
content: '';
display: block;
background: url("/me/suggest/suggest-logo.png");
width: 104px;
height: 35px;
margin: 10px auto 15px;
}
}
/* 意见反馈主体 */
.suggest-content {
border-top: 1px solid #e0e0e0;
}
.suggest-item {
width: 100%;
color: #444;
border-top: 1px solid #e0e0e0;
border-bottom: 30px solid #f0f0f0;
overflow: hidden;
.suggest-item-img {
width: 100%;
overflow: hidden;
> img {
margin: 0 auto;
display: block;
max-width: 100%;
}
}
> h2 {
font-size: 38px;
margin: 30px 0 31px;
padding: 0 35px;
}
> p {
font-size: 26px;
line-height: 48px;
padding: 0 35px;
}
}
.suggest-type {
margin-top: 29px - 11px;
height: 88px;
line-height: 88px;
border-top: 1px solid #e0e0e0;
border-bottom: 1px solid #e0e0e0;
color: #b0b0b0;
font-size: 26px;
display: none;
text-align: center;
> .active {
color: #444;
}
&.show {
display: block;
}
}
.suggest-active {
> div {
width: 50%;
height: 100%;
float: left;
text-align: left;
padding-left: 128px;
box-sizing: border-box;
}
> div:nth-last-of-type(1) {
padding-left: 0;
padding-right: 128px;
text-align: right;
float: right;
> span {
display: inline-block;
height: 100%;
overflow: hidden;
&:nth-of-type(1) {
transform: rotate(180deg);
}
}
}
}
.suggest-bad {
> div {
> span {
display: inline-block;
height: 100%;
overflow: hidden;
&:nth-of-type(1) {
transform: rotate(180deg);
}
}
}
}
/* 发表意见 */
.create-new-suggest {
display: block;
width: 100%;
height: 88px;
line-height: 88px;
text-align: center;
font-size: 30px;
border-top: 30px solid #f0f0f0;
border-bottom: 30px solid #f0f0f0;
position: relative;
.list-item {
padding: 0 35px;
}
.new-right {
float: right;
margin-left: 40px;
color: #e0e0e0;
}
a {
color: #444;
display: inline-block;
}
}
}
/* 提交页面 */
.yoho-suggest-sub-page {
width: 100%;
background: #f0f0f0;
.suggest-sub-form {
background: #fff;
width: 100%;
#suggest-textarea {
box-sizing: border-box;
width: 100%;
max-width: 100%;
min-width: 100%;
height: 255px;
max-height: 255px;
min-height: 255px;
padding: 30px;
font-size: 26px;
line-height: 48px;
color: #000;
display: block;
background: #fff;
border: none;
outline: none;
resize: none;
}
}
.img-form {
padding: 0 30px;
padding-top: 40px;
overflow: hidden;
.upload-img-list {
float: left;
> li {
display: block;
width: 130px;
height: 130px;
float: left;
margin-right: 30px;
background: resolve('common/loading.gif') center center no-repeat;
background-size: 50%;
position: relative;
> img {
display: block;
width: 100%;
height: 100%;
overflow: hidden;
}
> span {
display: block;
background: url("/me/suggest/sub_del.png");
width: 42px;
height: 42px;
position: absolute;
top: -21px;
right: -21px;
}
}
}
.img-add {
display: block;
width: 130px;
height: 130px;
border: 1px dashed #e0e0e0;
position: relative;
text-indent: -1000px;
float: left;
&:after {
content: '';
display: block;
background: url("/me/suggest/suggest-add.png");
width: 72px;
height: 72px;
position: absolute;
top: 50%;
left: 50%;
margin-top: -36px;
margin-left: -36px;
}
input[type="file"] {
position: absolute;
opacity: 0.2;
border: none;
outline: none;
display: block;
width: 130px;
height: 130px;
top: 0;
left: 0;
}
}
}
}
/* dialog */
.dialog-wrapper {
background: hsla(0, 0%, 0%, 0.5);
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: none;
.dialog-box {
width: 540px;
border-radius: 20px;
background: hsla(100, 100%, 100%, 0.8);
position: absolute;
left: 50%;
margin-left: -270px;
font-size: 30px;
text-align: center;
color: #000;
}
.dialog-content {
padding: 60px 30px;
}
.dialog-footer {
border-top: 1px solid #ccc;
height: 88px;
line-height: 88px;
> span {
display: block;
width: 50%;
height: 100%;
float: left;
box-sizing: border-box;
&:nth-last-of-type(1) {
border-left: 1px solid #ccc;
color: #e01;
}
}
> span:active {
background-color: #ccc;
}
}
}
... ...
... ... @@ -79,6 +79,8 @@ body.passport-body {
border-radius: 5PX;
appearance: none;
direction: rtl;
-webkit-appearance: none;
&:focus {
outline: 0;
border: none;
... ...
... ... @@ -2,28 +2,28 @@
color: #fefefe;
.success-icon {
width: 74px;
height: 74px;
margin: 60px auto 30px;
width: 74PX;
height: 74PX;
margin: 60PX auto 30PX;
background: resolve("passport/success.png");
background-size: 100%;
}
.success-tip {
padding: 10px;
font-size: 16px;
padding: 10PX;
font-size: 16PX;
line-height: 1.5;
}
.go {
display: block;
margin: 30px auto;
width: 270px;
height: 40px;
font-size: 14px;
line-height: 40px;
margin: 30PX auto;
width: 270PX;
height: 40PX;
font-size: 14PX;
line-height: 40PX;
color: #fff;
background: rgba(255, 255, 255, 0.4);
border-radius: 5px;
border-radius: 5PX;
}
}
... ...