Authored by 陈峰

login captcha

<template>
<div class="captcha-box">
<div class="captcha-box-header">
<span>请将下列图片点击翻转至正确方向</span>
<a href="javascript:;" class="img-check-refresh" @click="captchaRefresh">换一批</a>
</div>
<div class="captcha-box-content">
<div class="item"
v-for="block in blocks"
:key="block.posX"
:style="{
'background-image': `url(${imgSrc})`,
'background-position': `${block.posX}px ${block.posY}px`
}"
@click="captchaClick(block)"></div>
</div>
</div>
</template>
<script>
import _ from 'lodash';
export default {
name: 'captcha-box',
data() {
return {
captchaSrc: '/Api/captcha.jpg',
random: Math.random(),
blocks: []
};
},
created() {
this.blocks = [{
val: 0,
posX: 0,
posY: 0
}, {
val: 0,
posX: -60,
posY: 0
}, {
val: 0,
posX: -120,
posY: 0
}, {
val: 0,
posX: -180,
posY: 0
}];
},
computed: {
imgSrc() {
return `${this.captchaSrc}?r=${this.random}`;
}
},
methods: {
captchaClick(block) {
block.val = (block.val + 1) % 4;
block.posY = (block.posY - 60) % 240;
let vals = _.map(this.blocks, b => {
return b.val;
});
this.$emit('change', vals);
},
captchaRefresh() {
this.random = Math.random();
}
}
};
</script>
<style lang="scss">
.captcha-box {
margin: 0 auto;
width: 270px;
}
.captcha-box-header {
color: #b0b0b0;
a {
color: #ff1901;
float: right;
}
}
.captcha-box-content {
display: flex;
.item {
flex: 1;
cursor: pointer;
margin-right: 10px;
width: 60px;
height: 60px;
overflow: hidden;
border: solid 1px #ccc;
background-size: 240px;
&:last-child {
margin-right: 0;
}
}
}
</style>
... ...
import Captcha from './captcha';
export {
Captcha
};
... ...
... ... @@ -16,6 +16,9 @@
<Icon type="ios-locked-outline" slot="prepend"></Icon>
</Input>
</Form-item>
<Form-item>
<captcha v-if="isCaptcha" @change="captchaChange"></captcha>
</Form-item>
<Form-item class="login-btn">
<Button type="primary" :loading="loading" @click="handleSubmit('formInline')">
登录
... ... @@ -29,47 +32,65 @@
<script>
import Vue from 'vue';
import _ from 'lodash';
import {Captcha} from './components';
export default {
name: 'login',
data() {
return {
loading: false,
isCaptcha: false,
captcha: '',
formInline: {
user: '',
password: ''
},
ruleInline: {
user: [
{ required: true, message: '请填写用户名', trigger: 'blur' }
],
password: [
{ required: true, message: '请填写密码', trigger: 'blur' },
{ type: 'string', min: 6, message: '密码长度不能小于6位', trigger: 'blur' }
]
}
};
},
created() {
this.isCaptcha = this.$cookie.get('_captcha');
},
methods: {
handleSubmit(name) {
this.$refs[name].validate((valid) => {
if (valid) {
this.login(this.formInline.user, this.formInline.password);
if (this.isCaptcha && !this.captcha) {
this.$Message.error('请将图形验证码翻转至正确方向');
return;
}
this.login(this.formInline.user, this.formInline.password, this.captcha);
} else {
this.$Message.error('表单验证失败!');
}
});
},
login(username, password) {
captchaChange(vals) {
this.captcha = _.join(vals, '');
},
login(username, password, captcha) {
this.loading = true;
Vue.passport.local(username, password).then(() => {
Vue.passport.local(username, password, captcha).then(() => {
this.loading = false;
this.$router.push('/');
}, (error) => {
this.isCaptcha = error.captcha;
this.loading = false;
this.$Message.error(error.message);
});
}
},
data() {
return {
loading: false,
formInline: {
user: '',
password: ''
},
ruleInline: {
user: [
{ required: true, message: '请填写用户名', trigger: 'blur' }
],
password: [
{ required: true, message: '请填写密码', trigger: 'blur' },
{ type: 'string', min: 6, message: '密码长度不能小于6位', trigger: 'blur' }
]
}
};
components: {
Captcha
}
};
</script>
... ... @@ -104,10 +125,16 @@ export default {
.login-btn {
text-align: right;
button {
width: 100%;
height: 36px;
font-size: 14px;
}
}
.login-card {
height: 250px;
min-height: 250px;
}
}
</style>
... ...
... ... @@ -88,8 +88,8 @@ export default {
});
Vue.passport = {
local: (username, password) => {
return this.userService.login(username, password).then((res) => {
local: (username, password, captcha) => {
return this.userService.login(username, password, captcha).then((res) => {
if (res.code === 200) {
return Promise.all([
this.initPurview(Vue, res.data),
... ...
... ... @@ -2,8 +2,12 @@ import _ from 'lodash';
import Service from '../service';
class UserService extends Service {
login(username, password) {
return this.post('/login', {username, password});
login(username, password, captcha) {
return this.post('/login', {
username,
password,
captcha
});
}
purviews() {
return this.post('/erp/getPurview', {
... ...
... ... @@ -63,7 +63,7 @@
"request": "^2.81.0",
"request-promise": "^4.2.0",
"serve-favicon": "^2.4.2",
"uuid": "^3.0.1",
"uuid": "^3.1.0",
"vue": "^2.3.4",
"vue-cookie": "^1.1.4",
"vue-html5-editor": "^1.1.1",
... ... @@ -116,7 +116,7 @@
"html-webpack-plugin": "^2.28.0",
"husky": "^0.13.3",
"ignore-file-loader": "^1.0.0",
"node-sass": "^4.5.2",
"node-sass": "^4.5.3",
"nodemon": "^1.11.0",
"optimize-css-assets-webpack-plugin": "^1.3.0",
"ora": "^1.2.0",
... ...
This diff could not be displayed because it is too large.
/**
* 用户controller
* @author: feng.chen<feng.chen@yoho.cn>
* @date: 2017/04/13
*/
const _ = require('lodash');
const uuid = require('uuid');
const request = require('request');
const Context = require('../framework/context');
const captchaData = require('../common/captcha');
class CaptchaController extends Context {
constructor() {
super();
}
captcha(req, res) {
let random = _.random(0, captchaData.length);
let captcha = captchaData[random];
let codeStr = captcha.degrees.reduce((str, rotate) => {
return str.concat((4 - rotate / 90 % 4) % 4);
}, '');
req.session.captcha = codeStr;
req.session.captchaTimeout = new Date().getTime() + 1000 * 60;
req.session.captchaSrc = captcha.verifiedGraphicCode;
return request(`${captcha.verifiedGraphicCode}?imageView2/0/format/jpg/q/70|watermark/2/text/${uuid.v4()}/fontsize/120/dissolve/10`).pipe(res); // eslint-disable-line
}
check(req, res, next) {
let isCaptcha = req.session.isCaptcha;
if (isCaptcha) {
if (req.body.captcha === req.session.captcha) {
if (new Date().getTime() > req.session.captchaTimeout) {
return res.json({
code: 400,
captcha: true,
expired: true,
message: '验证码过期'
});
}
return next();
} else {
return res.json({
code: 400,
captcha: true,
message: '验证码错误'
});
}
}
return next();
}
}
module.exports = CaptchaController;
... ...
... ... @@ -7,20 +7,22 @@
'use strict';
const Express = require('express');
const UserController = require('./user-controller');
const FileController = require('./file-controller');
const ImportController = require('./import-controller');
const middleware = require('../framework/middleware');
const before = require('../middleware/before');
const auth = require('../middleware/auth');
const UserController = require('./user-controller');
const FileController = require('./file-controller');
const ImportController = require('./import-controller');
const CaptchaController = require('./captcha-controller');
let router = Express.Router(); // eslint-disable-line
router.post('/login', middleware(UserController, 'login'));
router.post('/login', middleware(CaptchaController, 'check'), middleware(UserController, 'login'));
router.post('/logout', middleware(UserController, 'logout'));
router.post('/switchShop', before, auth, middleware(UserController, 'switchShop'));
router.post('/upload/image', before, auth, middleware(FileController, 'uploadImage'));
router.post('/import', before, auth, middleware(ImportController, 'import'));
router.post('/config', middleware(UserController, 'config'));
router.get('/captcha.jpg', middleware(CaptchaController, 'captcha'));
module.exports = router;
... ...
... ... @@ -49,7 +49,13 @@ class UserController extends Context {
}
});
}, err => {
return res.json(err);
req.session.isCaptcha = true;
res.cookie('_captcha', true, {
path: '/'
});
return res.json(Object.assign(err, {
captcha: true
}));
}).catch(next);
}
... ... @@ -123,6 +129,8 @@ class UserController extends Context {
}
syncSession(context, user, sess, currentShop) {
delete context.req.session.isCaptcha;
context.res.clearCookie('_captcha');
context.req.session.USER = user;
context.req.session.LOGIN_UID = user.pid; // pid 为用户名
... ...
... ... @@ -4165,7 +4165,7 @@ node-pre-gyp@^0.6.36:
tar "^2.2.1"
tar-pack "^3.4.0"
node-sass@^4.5.2:
node-sass@^4.5.3:
version "4.5.3"
resolved "http://npm.yoho.cn/node-sass/-/node-sass-4.5.3.tgz#d09c9d1179641239d1b97ffc6231fdcec53e1568"
dependencies:
... ... @@ -6343,7 +6343,7 @@ uuid@^2.0.1, uuid@^2.0.2:
version "2.0.3"
resolved "http://npm.yoho.cn/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
uuid@^3.0.0, uuid@^3.0.1:
uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0:
version "3.1.0"
resolved "http://npm.yoho.cn/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04"
... ...