Authored by ccbikai

Merge branch 'feature/wechat-auth' into release/sale

... ... @@ -132,8 +132,7 @@ Session.vim
*~
# auto-generated tag files
tags
### VS Code ###
.vscode/
... ...
... ... @@ -39,7 +39,9 @@ app.set('view engine', '.hbs');
app.use(favicon(path.join(__dirname, '/public/favicon.ico')));
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(cookieParser());
app.use(session({
proxy: true,
... ... @@ -56,8 +58,8 @@ app.use(session({
},
store: new MemcachedStore({
hosts: config.memcache.session,
prefix: 'qinsessionsession:', // 兼容 PHP SESSION
key: 'yohobuy_session' // 兼容 PHP SESSION
prefix: 'qinsessionsession:', // 兼容 PHP SESSION
key: 'yohobuy_session' // 兼容 PHP SESSION
})
}));
... ...
/**
* passport 验证策略注册
* @author: jiangfeng<jeff.jiang@yoho.cn>
* @date: 2016/5/31
*/
'use strict';
const passport = require('passport');
const WeixinStrategy = require('passport-weixin-plus');
const config = require('../../config/common');
let siteUrl = config.siteUrl.indexOf('//') === 0 ? 'http:' + config.siteUrl : config.siteUrl;
/**
* wechat登录
*/
passport.use(new WeixinStrategy({
authorizationURL: 'https://open.weixin.qq.com/connect/oauth2/authorize',
tokenURL: 'https://api.weixin.qq.com/sns/oauth2/access_token',
clientID: config.thirdLogin.wechat.appID,
clientSecret: config.thirdLogin.wechat.appSecret,
callbackURL: `${siteUrl}/passport/login/wechat/callback`,
requireState: false,
scope: 'snsapi_userinfo'
}, (accessToken, refreshToken, profile, done) => {
done(null, profile);
}));
... ...
/**
* 登录
* @author: Bi Kai<kai.bi@yoho.cn>
* @date: 2016/05/09
*/
'use strict';
const library = '../../../library';
const passport = require('passport');
const cookie = require(`${library}/cookie`);
const helpers = require(`${library}/helpers`);
const log = require(`${library}/logger`);
const config = require('../../../config/common');
const AuthHelper = require('../models/auth-helper');
const loginPage = `${config.siteUrl}/passport/login/index`;
function doPassportCallback(openId, nickname, sourceType, req, res) {
let shoppingKey = cookie.getShoppingKey(req);
let refer = req.cookies.refer;
if (refer) {
refer = decodeURI(req.cookies.refer);
} else {
refer = `${config.siteUrl}/home`;
}
if (openId && nickname) {
AuthHelper.signinByOpenID(nickname, openId, sourceType, shoppingKey).then((result) => {
if (result.data['is_bind'] && result.data['is_bind'] === 'N') { //eslint-disable-line
return helpers.urlFormat('/passport/bind/index', {
openId: openId,
sourceType: sourceType,
refer: refer
});
} else if (result.code === 200 && result.data.uid) {
return AuthHelper.syncUserSession(result.data.uid, req, res).then(() => {
return refer;
});
}
}).then((redirectTo) => {
console.log('redirectTo=', redirectTo);
return res.redirect(redirectTo);
}).catch((e) => {
log.error('频道页面渲染错误:' + JSON.stringify(e));
return res.send('error');
});
}
}
const wechat = {
beforeLogin: (req, res, next) => {
let refer = req.query.refer;
if (!refer) {
refer = req.get('Referer');
}
refer && res.cookie('refer', encodeURI(refer));
next();
},
login: (req, res, next) => {
return passport.authenticate('weixin')(req, res, next);
},
callback: (req, res, next) => {
passport.authenticate('weixin', (err, user) => {
if (err) {
log.error(`wechat authenticate error : ${JSON.stringify(err)}`);
return res.redirect(loginPage);
}
let nickname = user.displayName || user._json.nickname;
let openId = user.id || user._json.unionid;
doPassportCallback(openId, nickname, 'wechat', req, res);
})(req, res, next);
}
};
exports.wechat = wechat;
... ...
/**
* sub app channel
* @author: Bi Kai<kai.bi@yoho.cn>
* @date: 2016/05/09
*/
'use strict';
var express = require('express'),
path = require('path'),
hbs = require('express-handlebars');
var passport = require('passport');
var app = express();
// set view engin
var doraemon = path.join(__dirname, '../../doraemon/views'); // parent view root
app.on('mount', function(parent) {
delete parent.locals.settings; // 不继承父 App 的设置
Object.assign(app.locals, parent.locals);
});
app.set('views', path.join(__dirname, 'views/action'));
app.engine('.hbs', hbs({
extname: '.hbs',
defaultLayout: 'layout',
layoutsDir: doraemon,
partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`],
helpers: require(`${global.library}/helpers`)
}));
require('./auth');
app.use(passport.initialize());
app.use(passport.session());
// router
app.use(require('./router'));
module.exports = app;
... ...
'use strict';
const library = '../../../library';
const API = require(`${library}/api`).API;
const sign = require(`${library}/sign`);
const api = new API();
class Auth {
static signinByOpenID(nickname, openId, sourceType, shoppingKey) {
let param = {
nickname: nickname,
openId: openId,
source_type: sourceType, // esline-disable-line
method: 'app.passport.signinByOpenID',
shoppingKey: shoppingKey
};
if (shoppingKey) {
param.shopping_key = shoppingKey;
}
return api.get('', sign.apiSign(param));
}
static profile(uid) {
let param = {
uid: uid,
method: 'app.passport.profile'
};
return api.get('', sign.apiSign(param));
}
static 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}`;
res.cookie('_UID', uidCookie, {
domain: 'yohobuy.com'
});
}
req.session._TOKEN = token; // esline-disable-line
req.session._LOGIN_UID = uid; // esline-disable-line
res.cookie('token', token, {
domain: 'yohobuy.com'
}); // esline-disable-line
}).catch(console.log);
}
}
module.exports = Auth;
... ...
/**
* router of sub app channel
* @author: Bi Kai<kai.bi@yoho.cn>
* @date: 2016/05/09
*/
'use strict';
const express = require('express');
const cRoot = './controllers';
const login = require(cRoot + '/login');
const router = express.Router(); // eslint-disable-line
router.get('/login/wechat', login.wechat.beforeLogin, login.wechat.login); // 登录
router.get('/login/wechat/callback', login.wechat.callback);
module.exports = router;
... ...
... ... @@ -4,30 +4,30 @@
* @date: 2016/05/06
*/
var express = require('express'),
path = require('path'),
hbs = require('express-handlebars');
var express = require('express'),
path = require('path'),
hbs = require('express-handlebars');
var app = express();
var app = express();
// set view engin
var doraemon = path.join(__dirname, '../../doraemon/views'); // parent view root
// set view engin
var doraemon = path.join(__dirname, '../../doraemon/views'); // parent view root
app.on('mount', function(parent) {
delete parent.locals.settings; // 不继承父 App 的设置
Object.assign(app.locals, parent.locals);
});
app.on('mount', function(parent) {
delete parent.locals.settings; // 不继承父 App 的设置
Object.assign(app.locals, parent.locals);
});
app.set('views', path.join(__dirname, 'views/action'));
app.engine('.hbs', hbs({
extname: '.hbs',
defaultLayout: 'layout',
layoutsDir: doraemon,
partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`],
helpers: require(`${global.library}/helpers`)
}));
app.set('views', path.join(__dirname, 'views/action'));
app.engine('.hbs', hbs({
extname: '.hbs',
defaultLayout: 'layout',
layoutsDir: doraemon,
partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`],
helpers: require(`${global.library}/helpers`)
}));
// router
app.use(require('./router'));
// router
app.use(require('./router'));
module.exports = app;
module.exports = app;
... ...
... ... @@ -47,6 +47,12 @@ module.exports = {
colorize: 'all',
prettyPrint: true
}
},
thirdLogin: {
wechat: {
appID: 'wx75e5a7c0c88e45c2',
appSecret: 'ce21ae4a3f93852279175a167e54509b'
}
}
};
... ...
... ... @@ -13,4 +13,5 @@ module.exports = app => {
// 业务模块
app.use('/product', require('./apps/product'));
app.use('/passport', require('./apps/passport'));
};
... ...
... ... @@ -3,6 +3,8 @@
* @param {[object]} req
* @return {[string]}
*/
'use strict';
exports.getUid = (req) => {
const cookie = req.cookies._UID;
let _uid = 0;
... ... @@ -21,3 +23,7 @@ exports.getUid = (req) => {
return _uid;
};
exports.getShoppingKey = (req) => {
return req.cookies['_SPK'] ? req.cookies['_SPK'] : ''; // eslint-disable-line
};
... ...
... ... @@ -11,12 +11,12 @@ const FileTransport = require('winston-daily-rotate-file');
require('influxdb-winston');
const logger = new (winston.Logger)({
const logger = new(winston.Logger)({
transports: [
new (FileTransport)(config.loggers.infoFile),
new (FileTransport)(config.loggers.errorFile),
new (winston.transports.UdpTransport)(config.loggers.udp),
new (winston.transports.Console)(config.loggers.console)
new(FileTransport)(config.loggers.infoFile),
new(FileTransport)(config.loggers.errorFile),
new(winston.transports.UdpTransport)(config.loggers.udp),
new(winston.transports.Console)(config.loggers.console)
],
exitOnError: false
});
... ...
... ... @@ -72,7 +72,7 @@ exports.apiSign = (params) => {
// 检查签名,APP 访问 H5 页面的时候需要检查
exports.checkSign = (params) => {
const // eslint-disable-line camelcase
clientSecret = params.client_secret;
clientSecret = params.client_secret;
let sortedParams;
... ... @@ -94,3 +94,7 @@ exports.webSign = (params) => {
return params.key === md5(md5(webPrivateKey) + params.uid);
};
exports.makeToken = (string) => {
return md5(md5(string + '#@!@#'));
};
... ...
... ... @@ -43,6 +43,8 @@
"moment": "^2.13.0",
"morgan": "^1.7.0",
"oneapm": "^1.2.20",
"passport": "^0.3.2",
"passport-weixin-plus": "0.0.4",
"request-promise": "^3.0.0",
"serve-favicon": "^2.3.0",
"uuid": "^2.0.2",
... ...