Authored by 姜枫

微信登录整合

@@ -133,6 +133,9 @@ Session.vim @@ -133,6 +133,9 @@ Session.vim
133 # auto-generated tag files 133 # auto-generated tag files
134 tags 134 tags
135 135
  136 +### VS Code ###
  137 +.vscode/
  138 +
136 ### YOHO ### 139 ### YOHO ###
137 dist 140 dist
138 public/css/* 141 public/css/*
@@ -39,7 +39,9 @@ app.set('view engine', '.hbs'); @@ -39,7 +39,9 @@ app.set('view engine', '.hbs');
39 app.use(favicon(path.join(__dirname, '/public/favicon.ico'))); 39 app.use(favicon(path.join(__dirname, '/public/favicon.ico')));
40 app.use(express.static(path.join(__dirname, 'public'))); 40 app.use(express.static(path.join(__dirname, 'public')));
41 app.use(bodyParser.json()); 41 app.use(bodyParser.json());
42 -app.use(bodyParser.urlencoded({extended: false})); 42 +app.use(bodyParser.urlencoded({
  43 + extended: false
  44 +}));
43 app.use(cookieParser()); 45 app.use(cookieParser());
44 app.use(session({ 46 app.use(session({
45 proxy: true, 47 proxy: true,
  1 +/**
  2 + * passport 验证策略注册
  3 + * @author: jiangfeng<jeff.jiang@yoho.cn>
  4 + * @date: 2016/5/31
  5 + */
  6 +
  7 +'use strict';
  8 +const passport = require('passport');
  9 +const WeixinStrategy = require('passport-weixin-plus');
  10 +
  11 +const config = require('../../config/common');
  12 +
  13 +/**
  14 + * wechat登录
  15 + */
  16 +passport.use(new WeixinStrategy({
  17 + clientID: config.wechat.appID,
  18 + clientSecret: config.wechat.appSecret,
  19 + callbackURL: `${config.siteUrl}/passport/login/wechat/callback`,
  20 + requireState: false,
  21 + scope: 'snsapi_userinfo'
  22 +}, (accessToken, refreshToken, profile, done) => {
  23 + done(null, profile);
  24 +}));
@@ -5,19 +5,65 @@ @@ -5,19 +5,65 @@
5 */ 5 */
6 'use strict'; 6 'use strict';
7 7
8 -// const library = '../../../library';  
9 -// const _ = require('lodash');  
10 -// const channelModel = require('../models/channel');  
11 -// const helpers = require(`${library}/helpers`);  
12 -// const log = require(`${library}/logger`); 8 +const library = '../../../library';
  9 +const passport = require('passport');
  10 +const cookie = require(`${library}/cookie`);
  11 +const helpers = require(`${library}/helpers`);
  12 +const log = require(`${library}/logger`);
  13 +const config = require('../../../config/common');
  14 +const AuthHelper = require('../models/auth-helper');
13 15
14 16
15 -/**  
16 - * 频道页底部 bannel  
17 - * @param {[object]} req  
18 - * @param {[object]} res  
19 - * @return {[type]}  
20 - */  
21 -exports.index = (req, res) => {  
22 - res.send('login'); 17 +function doPassportCallback(openId, nickname, req, res) {
  18 + let shoppingKey = cookie.getShoppingKey(req);
  19 + let refer = req.cookies.refer;
  20 +
  21 + if (refer) {
  22 + refer = decodeURI(req.cookies.refer);
  23 + } else {
  24 + refer = `${config.siteUrl}/?go=1`;
  25 + }
  26 +
  27 + if (openId && nickname) {
  28 + AuthHelper.signinByOpenID(nickname, openId, 'wechat', shoppingKey).then((result) => {
  29 + if (result.data['is_bind'] && result.data['is_bind'] === 'N') { //eslint-disable-line
  30 + res.redirect(helpers.urlFormat('/passport/bind/index', {
  31 + openId: openId,
  32 + sourceType: 'wechat'
  33 + }));
  34 + } else if (result.code === 200 && result.data.uid) {
  35 + return AuthHelper.syncUserSession(result.data.uid, res);
  36 + }
  37 + }).then(() => {
  38 + res.redirect(refer);
  39 + }).catch((e) => {
  40 + log.error('频道页面渲染错误:' + JSON.stringify(e));
  41 + res.send('error');
  42 + });
  43 + }
  44 +}
  45 +
  46 +const wechat = {
  47 + beforeLogin: (req, res, next) => {
  48 + let refer = req.query.refer;
  49 +
  50 + res.cookie('refer', encodeURI(refer));
  51 + next();
  52 + },
  53 + login: (req, res, next) => {
  54 + return passport.authenticate('weixin')(req, res, next);
  55 + },
  56 + callback: (req, res, next) => {
  57 + passport.authenticate('weixin', (err, user) => {
  58 + if (err) {
  59 + return next(err);
  60 + }
  61 + let nickname = user.nickname;
  62 + let openId = user.unionid || user.openid;
  63 +
  64 + doPassportCallback(openId, nickname, req, res);
  65 + })(req, res, next);
  66 + }
23 }; 67 };
  68 +
  69 +exports.wechat = wechat;
@@ -3,11 +3,13 @@ @@ -3,11 +3,13 @@
3 * @author: Bi Kai<kai.bi@yoho.cn> 3 * @author: Bi Kai<kai.bi@yoho.cn>
4 * @date: 2016/05/09 4 * @date: 2016/05/09
5 */ 5 */
6 - 6 +'use strict';
7 var express = require('express'), 7 var express = require('express'),
8 path = require('path'), 8 path = require('path'),
9 hbs = require('express-handlebars'); 9 hbs = require('express-handlebars');
10 10
  11 +var passport = require('passport');
  12 +
11 var app = express(); 13 var app = express();
12 14
13 // set view engin 15 // set view engin
@@ -27,6 +29,11 @@ app.engine('.hbs', hbs({ @@ -27,6 +29,11 @@ app.engine('.hbs', hbs({
27 helpers: require(`${global.library}/helpers`) 29 helpers: require(`${global.library}/helpers`)
28 })); 30 }));
29 31
  32 +
  33 +require('./auth');
  34 +app.use(passport.initialize());
  35 +app.use(passport.session());
  36 +
30 // router 37 // router
31 app.use(require('./router')); 38 app.use(require('./router'));
32 39
  1 +'use strict';
  2 +
  3 +const library = '../../../library';
  4 +const API = require(`${library}/api`).API;
  5 +const sign = require(`${library}/sign`);
  6 +const api = new API();
  7 +
  8 +class Auth {
  9 +
  10 + static signinByOpenID(nickname, openId, sourceType, shoppingKey) {
  11 + let param = {
  12 + nickname: nickname,
  13 + openId: openId,
  14 + source_type: sourceType, // esline-disable-line
  15 + method: 'app.passport.signinByOpenID'
  16 + };
  17 +
  18 + return api.get('', sign.apiSign(param));
  19 + }
  20 +
  21 + static profile(uid) {
  22 + let param = {
  23 + uid: uid,
  24 + method: 'app.passport.profile'
  25 + };
  26 +
  27 + return api.get('', sign.apiSign(param));
  28 + }
  29 +
  30 + static syncUserSession(uid, req, res) {
  31 + return Auth.profile(uid).then((userInfo) => {
  32 + let token = sign.makeToken(uid);
  33 + let data = userInfo.data;
  34 +
  35 + if (data) {
  36 + let uidCookie = `${data.profile_name}::${data.uid}::${data.vip_info.title}::${token}`;
  37 +
  38 + res.cookies._UID = uidCookie;
  39 + }
  40 + req.session._TOKEN = token; // esline-disable-line
  41 + req.session._LOGIN_UID = uid; // esline-disable-line
  42 + res.cookies._TOKEN = token; // esline-disable-line
  43 + });
  44 + }
  45 +}
  46 +
  47 +module.exports = Auth;
@@ -12,6 +12,7 @@ const login = require(cRoot + '/login'); @@ -12,6 +12,7 @@ const login = require(cRoot + '/login');
12 12
13 const router = express.Router(); // eslint-disable-line 13 const router = express.Router(); // eslint-disable-line
14 14
15 -router.get('/', login.index); // 登录 15 +router.get('/login/wechat', login.wechat.beforeLogin, login.wechat.login); // 登录
  16 +router.get('/login/wechat/callback', login.wechat.callback);
16 17
17 module.exports = router; 18 module.exports = router;
@@ -4,30 +4,30 @@ @@ -4,30 +4,30 @@
4 * @date: 2016/05/06 4 * @date: 2016/05/06
5 */ 5 */
6 6
7 - var express = require('express'), 7 +var express = require('express'),
8 path = require('path'), 8 path = require('path'),
9 hbs = require('express-handlebars'); 9 hbs = require('express-handlebars');
10 10
11 - var app = express(); 11 +var app = express();
12 12
13 - // set view engin  
14 - var doraemon = path.join(__dirname, '../../doraemon/views'); // parent view root 13 +// set view engin
  14 +var doraemon = path.join(__dirname, '../../doraemon/views'); // parent view root
15 15
16 - app.on('mount', function(parent) { 16 +app.on('mount', function(parent) {
17 delete parent.locals.settings; // 不继承父 App 的设置 17 delete parent.locals.settings; // 不继承父 App 的设置
18 Object.assign(app.locals, parent.locals); 18 Object.assign(app.locals, parent.locals);
19 - }); 19 +});
20 20
21 - app.set('views', path.join(__dirname, 'views/action'));  
22 - app.engine('.hbs', hbs({ 21 +app.set('views', path.join(__dirname, 'views/action'));
  22 +app.engine('.hbs', hbs({
23 extname: '.hbs', 23 extname: '.hbs',
24 defaultLayout: 'layout', 24 defaultLayout: 'layout',
25 layoutsDir: doraemon, 25 layoutsDir: doraemon,
26 partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`], 26 partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`],
27 helpers: require(`${global.library}/helpers`) 27 helpers: require(`${global.library}/helpers`)
28 - })); 28 +}));
29 29
30 - // router  
31 - app.use(require('./router')); 30 +// router
  31 +app.use(require('./router'));
32 32
33 - module.exports = app; 33 +module.exports = app;
@@ -40,13 +40,17 @@ module.exports = { @@ -40,13 +40,17 @@ module.exports = {
40 udp: { // send by udp 40 udp: { // send by udp
41 level: 'debug', // logger level 41 level: 'debug', // logger level
42 host: '192.168.102.162', // influxdb host 42 host: '192.168.102.162', // influxdb host
43 - port: '4444'// influxdb port 43 + port: '4444' // influxdb port
44 }, 44 },
45 console: { 45 console: {
46 level: 'debug', 46 level: 'debug',
47 colorize: 'all', 47 colorize: 'all',
48 prettyPrint: true 48 prettyPrint: true
49 } 49 }
  50 + },
  51 + wechat: {
  52 + appID: 'wx3ae21dcbb82ad672',
  53 + appSecret: 'e78afb2321e6a19085767e1a0f0d52c1'
50 } 54 }
51 }; 55 };
52 56
@@ -3,6 +3,8 @@ @@ -3,6 +3,8 @@
3 * @param {[object]} req 3 * @param {[object]} req
4 * @return {[string]} 4 * @return {[string]}
5 */ 5 */
  6 +'use strict';
  7 +
6 exports.getUid = (req) => { 8 exports.getUid = (req) => {
7 const cookie = req.cookies._UID; 9 const cookie = req.cookies._UID;
8 let _uid = 0; 10 let _uid = 0;
@@ -21,3 +23,7 @@ exports.getUid = (req) => { @@ -21,3 +23,7 @@ exports.getUid = (req) => {
21 23
22 return _uid; 24 return _uid;
23 }; 25 };
  26 +
  27 +exports.getShoppingKey = (req) => {
  28 + return req.cookies['_SPK'] ? req.cookies['_SPK'] : ''; // eslint-disable-line
  29 +};
@@ -11,14 +11,14 @@ const FileTransport = require('winston-daily-rotate-file'); @@ -11,14 +11,14 @@ const FileTransport = require('winston-daily-rotate-file');
11 11
12 require('influxdb-winston'); 12 require('influxdb-winston');
13 13
14 -const logger = new (winston.Logger)({  
15 - transports: [  
16 - new (FileTransport)(config.loggers.infoFile),  
17 - new (FileTransport)(config.loggers.errorFile),  
18 - new (winston.transports.UdpTransport)(config.loggers.udp),  
19 - new (winston.transports.Console)(config.loggers.console)  
20 - ],  
21 - exitOnError: false 14 +const logger = new(winston.Logger)({
  15 + // transports: [
  16 + // new(FileTransport)(config.loggers.infoFile),
  17 + // new(FileTransport)(config.loggers.errorFile),
  18 + // new(winston.transports.UdpTransport)(config.loggers.udp),
  19 + // new(winston.transports.Console)(config.loggers.console)
  20 + // ],
  21 + // exitOnError: false
22 }); 22 });
23 23
24 module.exports = logger; 24 module.exports = logger;
@@ -94,3 +94,7 @@ exports.webSign = (params) => { @@ -94,3 +94,7 @@ exports.webSign = (params) => {
94 94
95 return params.key === md5(md5(webPrivateKey) + params.uid); 95 return params.key === md5(md5(webPrivateKey) + params.uid);
96 }; 96 };
  97 +
  98 +exports.makeToken = (string) => {
  99 + return md5(md5(string + '#@!@#'));
  100 +};
@@ -43,6 +43,8 @@ @@ -43,6 +43,8 @@
43 "moment": "^2.13.0", 43 "moment": "^2.13.0",
44 "morgan": "^1.7.0", 44 "morgan": "^1.7.0",
45 "oneapm": "^1.2.20", 45 "oneapm": "^1.2.20",
  46 + "passport": "^0.3.2",
  47 + "passport-weixin-plus": "0.0.4",
46 "request-promise": "^3.0.0", 48 "request-promise": "^3.0.0",
47 "serve-favicon": "^2.3.0", 49 "serve-favicon": "^2.3.0",
48 "uuid": "^2.0.2", 50 "uuid": "^2.0.2",