login.js 12.2 KB
/* eslint no-unused-vars: ["error", { "args": "none" }] */
/**
 * 登录
 * @author: Bi Kai<kai.bi@yoho.cn>
 * @date: 2016/05/09
 */
'use strict';
const _ = require('lodash');
const passport = require('passport');

// const md5 = require('md5');
const uuid = require('uuid');
const cookie = global.yoho.cookie;
const helpers = global.yoho.helpers;
const log = global.yoho.logger;
const config = global.yoho.config;
const utils = require(global.utils);
const RegService = require('../models/reg-service');
const AuthHelper = require('../models/auth-helper');

const loginPage = `${config.siteUrl}/passport/login`;

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 (/signin|login/.test(refer)) {
        refer = `${config.siteUrl}/home`;
    }

    refer = utils.refererLimit(refer);

    if (openId && nickname) {
        return AuthHelper.signinByOpenID(nickname, openId, sourceType, shoppingKey).then((result) => {
            if (result.code !== 200) {
                return Promise.reject(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, result.data.session_key).then(() => {
                    return refer;
                });
            }
        }).then((redirectTo) => {
            return res.redirect(redirectTo);
        });
    } else {
        return Promise.reject('missing third party login openId or nickname');
    }
}

const common = {
    beforeLogin: (req, res, next) => {
        let refer = req.query.refer;

        if (!refer) {
            refer = req.get('Referer');
        }

        refer = utils.refererLimit(refer);


        refer && !/signin|login|passport/.test(refer) && res.cookie('refer', encodeURI(refer), {
            domain: 'yohobuy.com'
        });
        next();
    }
};


const local = {
    loginPage: (req, res) => {
        if (req.session.captchaValidCount == null) { // eslint-disable-line
            req.session.captchaValidCount = 5;
        }

        // 先清除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', {
            domain: 'yohobuy.com'
        });
        res.clearCookie('_TOKEN', {
            domain: 'yohobuy.com'
        });

        res.render('login', {
            width750: true,
            loginIndex: true, // 模板中使用JS的标识
            captchaShow: _.get(req.session, 'login.errorCount') <= 0,

            // 返回的URL链接
            backUrl: 'javascript:history.go(-1)', // eslint-disable-line
            showHeaderImg: true, // 控制显示头部图片
            isPassportPage: true, // 模板中模块标识
            smsLoginUrl: '/passport/sms_login',
            registerUrl: '/passport/reg/index', // 注册的URL链接
            aliLoginUrl: '/passport/login/alipay', // 支付宝快捷登录的URL链接
            weiboLoginUrl: '/passport/login/sina', // 微博登录的URL链接
            qqLoginUrl: '/passport/login/qq', // 腾讯QQ登录的URL链接
            wechatLoginUrl: '/passport/login/wechat', // 微信登录的URL链接
            internationalUrl: '/passport/international', // 国际号登录的URL链接
            phoneRetriveUrl: '/passport/back/mobile', // 通过手机号找回密码的URL链接
            emailRetriveUrl: '/passport/back/email', // 通过邮箱找回密码的URL链接
            module: 'passport',
            page: 'login',
            title: '登录'
        });
    },
    international: (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', {
            domain: 'yohobuy.com'
        });
        res.clearCookie('_TOKEN', {
            domain: 'yohobuy.com'
        });

        res.render('international', {
            width750: true,

            // 返回的URL链接
            backUrl: 'javascript:history.go(-1)', // eslint-disable-line
            loginInternational: true, // 模板中使用JS的标识
            captchaShow: _.get(req.session, 'login.errorCount') <= 0,
            isPassportPage: true, // 模板中模块标识
            headerText: '登录',
            areaCode: '+86', // 默认区号
            countrys: RegService.getAreaData(), // 地区信息列表
            module: 'passport',
            page: 'international',
            title: '国际账号登录'
        });
    },
    login: (req, res, next) => {
        let count = _.get(req.session, 'login.errorCount');

        if (count == null) { // eslint-disable-line
            _.set(req.session, 'login.errorCount', 3);
        }

        if (count <= 0) {
            let captchaInput = req.body.captcha;
            let captchaCode = _.get(req.session, 'captcha');
            let testCode = req.body.yohobuy;

            let errorCount = _.get(req.session, 'captchaValidCount'); // 初始1次 + 后续4次, 同一个验证码 共5次

            let jsonData = {
                code: 400,
                message: '请将图片旋转到正确方向',
                captchaShow: true
            };

            --req.session.captchaValidCount;

            if (!errorCount) {
                _.set(req.session, 'captchaValidCount', 5);

                // delete req.session.captcha; // 验证码 用过就扔
                req.session.captcha = null;
                jsonData.changeCaptcha = true;
            }

            if (
                !(
                    (captchaInput && captchaCode && captchaInput === captchaCode) ||
                    (testCode === config.testCode)
                )
            ) {
                res.json(jsonData);

                return;
            }
        }

        passport.authenticate('local', (err, user) => {
            let loginSession = req.session.login;

            if (err) {
                let obj = {
                    code: 400,
                    message: err,
                    data: ''
                };

                --loginSession.errorCount;

                if (loginSession.errorCount <= 0) {
                    obj.captchaShow = true;
                }

                res.json(obj);
            } else {
                let refer = req.cookies.refer;

                if (refer) {
                    refer = decodeURI(req.cookies.refer);
                } else {
                    refer = `${config.siteUrl}/home`;
                }

                if (/sign|login/.test(refer)) {
                    refer = `${config.siteUrl}/home`;
                }

                refer = utils.refererLimit(refer);

                user.session = refer;
                user.href = refer;
                AuthHelper.syncUserSession(user.uid, req, res, user.session_key).then(() => {
                    res.json({
                        code: 200,
                        data: user
                    });
                });
            }
        })(req, res, next);
    },
    logout: (req, res) => {
        if (req.session && req.session.destroy) {
            req.session.destroy();
        }
        if (req.session2 && req.session2.reset) {
            req.session2.reset();
        }

        res.clearCookie('_UID', {
            domain: 'yohobuy.com'
        });
        res.clearCookie('_TOKEN', {
            domain: 'yohobuy.com'
        });
        res.clearCookie('_SPK');
        let refer = req.get('Referer') || config.siteUrl;

        refer = utils.refererLimit(refer);

        res.redirect(refer);
    }
};

const wechat = {
    login: (req, res, next) => {
        req.session.authState = uuid.v4();
        return passport.authenticate('weixin', {
            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('weixin', (err, user) => {
                if (err) {
                    log.error(`wechat authenticate error : ${JSON.stringify(err)}`);
                    return res.redirect(loginPage);
                }
                let nickname = user._json.nickname || user.displayName;
                let openId = user._json.unionid || user.id;

                doPassportCallback(openId, nickname, 'wechat', req, res).catch(next);
            })(req, res, next);
        } else {
            log.error('Auth State Mismatch:' + req.originalUrl);
            return res.redirect(loginPage);
        }
    }
};

const sina = {
    login: (req, res, next) => {
        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(openId, nickname, 'sina', req, res).catch(next);
            })(req, res, next);
        } else {
            log.error('Auth State Mismatch:' + req.originalUrl);
            return res.redirect(loginPage);
        }
    }
};

const qq = {
    login: (req, res, next) => {
        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.id;

                doPassportCallback(openId, nickname, 'qq', req, res).catch(next);
            })(req, res, next);
        } else {
            log.error('Auth State Mismatch:' + req.originalUrl);
            return res.redirect(loginPage);
        }
    }
};

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(openId, nickname, 'alipay', req, res).catch(next);
        })(req, res, next);
    }
};

exports.user = function(req, res, next) {
    let result = {
        code: 403,
        message: '未登录',
        data: ''
    }
    if (req.user.uid) {
        result.code = 200;
        result.message = '已登录';
        result.data = req.user.uid;
    }
    res.jsonp(result);
}

exports.common = common;
exports.local = local;
exports.wechat = wechat;
exports.sina = sina;
exports.qq = qq;
exports.alipay = alipay;