Authored by 姜枫

add third part login

... ... @@ -8,7 +8,10 @@
const _ = require('lodash');
const passport = require('passport');
const WeixinStrategy = require('passport-weixin');
const SinaStrategy = require('passport-sina').Strategy;
const LocalStrategy = require('passport-local').Strategy;
const QQStrategy = require('passport-qq').Strategy;
const AlipayStrategy = require('./models/passport-alipay').Strategy;
const md5 = require('md5');
... ... @@ -18,6 +21,7 @@ const config = global.yoho.config;
const helpers = global.yoho.helpers;
const cookie = global.yoho.cookie;
const logger = global.yoho.logger;
const cache = global.yoho.cache;
let siteUrl = config.siteUrl.indexOf('//') === 0 ? 'http:' + config.siteUrl : config.siteUrl;
... ... @@ -52,16 +56,63 @@ passport.use(new LocalStrategy({
let shoppingKey = cookie.getShoppingKey(req);
AuthHelper.signin(area, username, password, shoppingKey).then((result) => {
let account = req.body.account;
let ip = req.ip;
let errorLoginKey = 'account_errorlogin_' + account;
let accountKey = 'account_signin_' + account;
let ipKey = 'ip_signin_' + ip;
let cacheGet = [cache.get(errorLoginKey), cache.get(accountKey), cache.get(ipKey)];
Promise.all(cacheGet).then(times => {
let errLoginTimes = parseInt(times[0], 0) || 0;
let accountTimes = parseInt(times[1], 0) || 0;
let ipTimes = parseInt(times[2], 0) || 0;
console.log(errLoginTimes);
if (accountTimes >= 10) {
done('您的账号已被暂时锁定,请稍后再试', null);
} else if (ipTimes >= 100) {
done('您尝试的次数过多,账号已被暂时锁定,请稍后再试', null);
} else {
return AuthHelper.signin(area, username, password, shoppingKey).then((result) => {
console.log(result);
if (result.code && result.code === 200 && result.data.uid) {
cache.del(errorLoginKey);
done(null, result.data);
} else {
done('账号或密码不正确', null);
errLoginTimes = errLoginTimes + 1;
accountTimes = accountTimes + 1;
ipTimes = ipTimes + 1;
cache.set(errorLoginKey, errLoginTimes);
cache.set(accountKey, accountTimes, 1800);
cache.set(ipKey, ipTimes, 3600);
//再次校验
if (ipTimes >= 100) {
done('您尝试的次数过多,账号已被暂时锁定,请稍后再试', null);
} else if (accountTimes >= 10) {
done('您的账号已被暂时锁定,请稍后再试', null);
} else if (errLoginTimes >= 3) {
done(`您输入的密码及账户名不匹配,
是否<a href="${helpers.urlFormat('/passport/back/index')}" target="_blank">忘记密码?</a>`,
{ needCaptcha: true });
} else {
done(`您输入的密码及账户名不匹配,
是否<a href="${helpers.urlFormat('/passport/back/index')}" target="_blank">忘记密码?</a>`,
{ needCaptcha: false });
}
}
});
}
}).catch(e => {
logger.error('call the signin service fail,', e);
done('登录失败,请稍后重试', null);
});
}));
/**
... ... @@ -74,6 +125,35 @@ passport.use('wechat', new WeixinStrategy({
callbackURL: `${siteUrl}/passport/login/wechat/callback`,
requireState: true,
scope: 'snsapi_login'
}, function(accessToken, refreshToken, profile, done) {
}, (accessToken, refreshToken, profile, done) => {
done(null, profile);
}));
// sina 登录
passport.use('sina', new SinaStrategy({
clientID: '3739328910',
clientSecret: '9d44cded26d048e23089e5e975c93df1',
callbackURL: `${siteUrl}/passport/login/sina/callback`,
requireState: false
}, (accessToken, refreshToken, profile, done) => {
done(null, profile);
}));
// qq 登录
passport.use('qq', new QQStrategy({
clientID: '100229394',
clientSecret: 'c0af9c29e0900813028c2ccb42021792',
callbackURL: `${siteUrl}/passport/login/qq/callback`,
requireState: false
}, (accessToken, refreshToken, profile, done) => {
done(null, profile);
}));
// alipay 登录
passport.use('alipay', new AlipayStrategy({
partner: '2088701661478015',
key: 'kcxawi9bb07mzh0aq2wcirsf9znusobw',
callbackURL: `${siteUrl}/passport/login/alipay/callback`
}), (profile, done) => {
done(null, profile);
});
\ No newline at end of file
... ...
... ... @@ -21,7 +21,7 @@ const loginPage = `${config.siteUrl}/passport/login/index`;
const SIGNIN_LEFT_BANNER_CODE = 'db350894e01e90eac55cd3a13ad77331';
// 第三方登录回调
function doPassportCallback(req, res, next, user) {
function doPassportCallback(req, res, user) {
let shoppingKey = cookie.getShoppingKey(req);
let refer = req.cookies.refer;
... ... @@ -60,7 +60,7 @@ function doPassportCallback(req, res, next, user) {
} else if (result.code === 200 && result.data.uid) {
return AuthHelper.syncUserSession(result.data.uid, req, res).then(() => {
return refer;
}).catch(next);
});
}
}).then((redirectTo) => {
res.redirect(redirectTo);
... ... @@ -83,26 +83,6 @@ const common = {
domain: 'yohobuy.com'
});
next();
},
ipFilter: (req, res, next) => {
let account = req.body.account;
let ip = req.ip;
// let errorLoginKey = 'account_errorlogin_' + account;
let accountKey = 'account_signin_' + account;
let ipKey = 'ip_signin_' + ip;
// let errLoginTimes = cache.get(errorLoginKey) || 0;
let accountTimes = cache.get(accountKey) || 0;
let ipTimes = cache.get(ipKey) || 0;
if (accountTimes >= 10) {
res.json({ code: 400, message: '您的账号已被暂时锁定,请稍后再试', data: '' });
} else if (ipTimes >= 100) {
res.json({ code: 400, message: '您尝试的次数过多,账号已被暂时锁定,请稍后再试', data: '' });
} else {
return next();
}
}
};
... ... @@ -135,6 +115,7 @@ const local = {
forgetPwd: helpers.urlFormat('/passport/back/index'),
fastReg: helpers.urlFormat('/reg.html'),
weixinLogin: helpers.urlFormat('/passport/autosign/wechat'),
qqLogin: helpers.urlFormat('/passport/autosign/qq'),
weiboLogin: helpers.urlFormat('/passport/autosign/sina'),
alipayLogin: helpers.urlFormat('/passport/autosign/alipay'),
doubanLogin: helpers.urlFormat('/passport/autosign/douban'),
... ... @@ -153,11 +134,20 @@ const local = {
res.json({
code: 400,
message: err,
data: ''
data: user || {}
});
} else {
let isRemember = req.body.isRemember;
let refer = req.cookies.refer;
if (isRemember) {
AuthHelper.rememberAccount({
area: req.body.areaCode || '86',
account: req.body.account,
password: req.body.password
}, req, res);
}
if (refer) {
refer = decodeURI(req.cookies.refer);
} else {
... ... @@ -177,33 +167,142 @@ const local = {
});
}
})(req, res, next);
},
logout: (req, res) => {
req.session = null;
res.clearCookie('_UID', {
domain: 'yohobuy.com'
});
res.clearCookie('_TOKEN', {
domain: 'yohobuy.com'
});
res.clearCookie('_SPK');
res.clearCookie('_g');
res.clearCookie('isRemember');
res.clearCookie('remem');
let refer = req.get('Referer') || config.siteUrl;
res.redirect(refer);
}
};
const wechat = {
login: (req, res, next) => {
req.session = req.session || {};
req.session.authState = uuid.v4();
return passport.authenticate('wechat', {
state: uuid.v4()
state: req.session.authState
})(req, res, next);
},
callback: (req, res, next) => {
if (req.session && req.session.authState && req.session.authState === req.query.state) {
passport.authenticate('wechat', (err, user) => {
if (err) {
log.error(`wechat authenticate error : ${JSON.stringify(err)}`);
return res.redirect(loginPage);
}
doPassportCallback(req, res, next, {
doPassportCallback(req, res, {
openId: user._json.openid,
unionId: user._json.unionid || user.id,
nickname: user._json.nickname || user.displayName,
sourceType: 'wechat',
rawUser: user
});
}).catch(next);
})(req, res, next);
} else {
return next(new Error('Auth State Mismatch'));
}
}
};
const sina = {
login: (req, res, next) => {
req.session = req.session || {};
req.session.authState = uuid.v4();
return passport.authenticate('sina', {
state: req.session.authState
})(req, res, next);
},
callback: (req, res, next) => {
if (req.session && req.session.authState && req.session.authState === req.query.state) {
passport.authenticate('sina', (err, user) => {
if (err) {
log.error(`sina authenticate error : ${JSON.stringify(err)}`);
return res.redirect(loginPage);
}
let nickname = user.screen_name;
let openId = user.id;
doPassportCallback(req, res, {
openId: openId,
nickname: nickname,
sourceType: 'sina'
}).catch(next);
})(req, res, next);
} else {
return next(new Error('Auth State Mismatch'));
}
}
};
const qq = {
login: (req, res, next) => {
req.session = req.session || {};
req.session.authState = uuid.v4();
return passport.authenticate('qq', {
state: req.session.authState
})(req, res, next);
},
callback: (req, res, next) => {
if (req.session && req.session.authState && req.session.authState === req.query.state) {
passport.authenticate('qq', (err, user) => {
if (err) {
log.error(`qq authenticate error : ${JSON.stringify(err)}`);
return res.redirect(loginPage);
}
let nickname = user.nickname;
let openId = user.openid;
doPassportCallback(req, res, {
openId: openId,
nickname: nickname,
sourceType: 'qq'
}).catch(next);
})(req, res, next);
} else {
return next(new Error('Auth State Mismatch'));
}
}
};
const alipay = {
login: (req, res, next) => {
return passport.authenticate('alipay')(req, res, next);
},
callback: (req, res, next) => {
passport.authenticate('alipay', (err, user) => {
if (err) {
log.error(`alipay authenticate error : ${JSON.stringify(err)}`);
return res.redirect(loginPage);
}
let nickname = user.realName;
let openId = user.userId;
doPassportCallback(req, res, {
openId: openId,
nickname: nickname,
sourceType: 'alipay'
}).catch(next);
})(req, res, next);
}
};
exports.common = common;
exports.wechat = wechat;
exports.local = local;
exports.sina = sina;
exports.qq = qq;
exports.alipay = alipay;
... ...
'use strict';
const md5 = require('md5');
const cache = global.yoho.cache;
const sign = global.yoho.sign;
const api = global.yoho.API;
class Auth {
static signin(area, profile, password, shoppingKey) {
const Auth = {
signin(area, profile, password, shoppingKey) {
let param = {
method: 'app.passport.signin',
area: area,
... ... @@ -18,9 +20,8 @@ class Auth {
}
return api.post('', param);
}
static signinByOpenID(nickname, openId, sourceType, shoppingKey) {
},
signinByOpenID(nickname, openId, sourceType, shoppingKey) {
let param = {
nickname: nickname,
openId: openId,
... ... @@ -33,9 +34,8 @@ class Auth {
}
return api.get('', param);
}
static signinByWechat(nickname, openId, unionId, sourceType, shoppingKey) {
},
signinByWechat(nickname, openId, unionId, sourceType, shoppingKey) {
let param = {
nickname: nickname,
openId: openId,
... ... @@ -49,24 +49,22 @@ class Auth {
}
return api.get('', param);
}
static profile(uid) {
},
profile(uid) {
let param = {
uid: uid,
method: 'app.passport.profile'
};
return api.get('', param);
}
static syncUserSession(uid, req, res) {
},
syncUserSession(uid, req, res) {
return Auth.profile(uid).then((userInfo) => {
let token = sign.makeToken(uid);
let data = userInfo.data;
if (data) {
let uidCookie = `${data.profile_name}::${data.uid}::${data.vip_info.title}::${token}`;
let uidCookie = `{data.profile_name}::${data.uid}::${data.vip_info.title}::${token}`;
req.session._TOKEN = token;
req.session._LOGIN_UID = uid;
... ... @@ -80,8 +78,18 @@ class Auth {
res.cookie('_TOKEN', token, {
domain: 'yohobuy.com'
}); // esline-disable-line
});
}).catch(console.log);
},
rememberAccount(accountInfo, req, res) {
let aWeek = (new Date()).getTime() / 1000 + 504000; //504000-一周
let rememKey = md5(md5(accountInfo.account + accountInfo.password + accountInfo.area));
res.cookie('isRemember', true, aWeek);
res.cookie('remem', rememKey, aWeek);
if (!cache.get(rememKey)) {
cache.set(rememKey, accountInfo, aWeek);
}
}
}
};
module.exports = Auth;
... ...
/**
* passport.js 支付宝登录插件
*
* @author JiangFeng<jeff.jiang@yoho.cn>
* @date 2016/06/21
*/
'use strict';
const util = require('util');
const _ = require('lodash');
const md5 = require('md5');
const passport = require('passport-strategy');
// 支付宝网关地址
const ALIPAY_URL = 'https://mapi.alipay.com/gateway.do';
const defaultOptions = {
service: 'alipay.auth.authorize',
_input_charset: 'utf-8',
sign_type: 'MD5',
target_service: 'user.auth.quick.login'
};
/**
* 将参数排序,拼接成 "参数=参数值" 的格式
*
* @param {Object} params
*/
function paramsToRaw(params) {
let keys = Object.keys(params);
keys = keys.sort();
let string = '';
keys.forEach((key) => {
string += '&' + key + '=' + params[key];
});
string = string.substr(1);
return string;
}
function AlipayStrategy(options, verify) {
if (typeof options === 'function') {
verify = options;
}
passport.Strategy.call(this);
this.name = 'alipay';
this._verify = verify;
}
util.inherits(AlipayStrategy, passport.Strategy);
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;
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,
email: req.query.email
};
this.success(user, null);
} else {
this.error('alipay login fail');
this.fail(req.error_code);
}
} else {
let params = _.assign(defaultOptions, options);
let signType = params.sign_type;
delete params.sign_type;
delete params.sign;
let signString = paramsToRaw(params) + options.key;
if (signType === 'MD5') {
params.sign = md5(signString);
params.sign_type = 'MD5';
}
this.redirect(ALIPAY_URL + '?' + paramsToRaw(params));
}
};
exports = module.exports = AlipayStrategy;
exports.Strategy = AlipayStrategy;
... ...
... ... @@ -16,11 +16,27 @@ const reg = require(cRoot + '/reg');
const router = express.Router(); // eslint-disable-line
// 本地登录
router.get('/login', login.common.beforeLogin, login.local.loginPage);
router.post('/login/auth', login.local.login);
router.get('/logout', login.local.logout);
// 微信登录
router.get('/autosign/wechat', login.common.beforeLogin, login.wechat.login); // 微信登录, 兼容 PHP 的路径
router.get('/login/wechat/callback', login.wechat.callback);
// sina登录
router.get('/autosign/sina', login.common.beforeLogin, login.sina.login);
router.get('/login/sina/callback', login.sina.callback);
// qq登录
router.get('/autosign/qq', login.common.beforeLogin, login.qq.login);
router.get('/login/qq/callback', login.qq.callback);
// alipay登录
router.get('/autosign/alipay', login.common.beforeLogin, login.alipay.login);
router.get('/login/alipay/callback', login.alipay.callback);
/**
* 注册页面路由
*/
... ...
... ... @@ -47,6 +47,9 @@
"oneapm": "^1.2.20",
"passport": "^0.3.2",
"passport-local": "^1.0.0",
"passport-qq": "0.0.3",
"passport-sina": "^0.1.0",
"passport-strategy": "1.x.x",
"passport-weixin": "^0.1.0",
"request-promise": "^3.0.0",
"serve-favicon": "^2.3.0",
... ...
... ... @@ -102,6 +102,7 @@ function syncLoginInfo() {
expires: -1
});
}
$loginBox.show();
});
}
... ... @@ -530,4 +531,4 @@ function actionCover() {
}
}
actionCover();
// actionCover();
... ...