var express = require('express'); var router = express.Router(); var User = require('./../sql/collection/users'); var sql = require('./../sql'); var utils = require('./../utils') var uuid = require('node-uuid'); var bcrypt = require('bcryptjs'); var jwt = require('jsonwebtoken'); var salt = bcrypt.genSaltSync(10); // 加密級別 var code = require('./../utils/code'); // 快速登錄 router.post('/quicklogin', (req, res, next) => { let { tel } = req.body; sql.find(User, { tel }, { _id: 0 }).then(data => { if (data.length === 0) { res.send({ code: '10086', msg: '該用戶未註冊' }) } else { let userid = data[0].userid let username = data[0].username let token = jwt.sign({ userid }, 'daxunxun', { expiresIn: 60*60*24*7 }) res.send({ code: '10010', message: '登錄成功', token: token, userid, username }) } }) }) // 快速登錄(驗證碼) router.post('/quick', (req, res, next) => { let { tel } = req.body; sql.find(User, { tel }, { _id: 0}).then(data => { if (data.length !== 0) { let str = ''; for (var i=0; i<5; i++) { str += Math.round(Math.random()*9) } let num = Math.round(str) // console.log(num) code.sendCode(tel, num).then(data => { if (data === 1) { // console.log('驗證碼發送成功') res.send({ code: '200', msg: '發送驗證碼成功', data: num }) } }).catch(() => { // console.log('驗證碼發送失敗') res.send({ code: '201', msg: '發送驗證碼失敗' }) }) } else { res.send({ code: '202', msg: '該用戶未註冊' }) } }) }) // 發送手機驗證碼 router.post('/check', (req, res, next) => { // 生成5位隨機驗證碼 let { tel } = req.body; let str = ''; for (var i=0; i<5; i++) { str += Math.round(Math.random()*9) } let num = Math.round(str) console.log(num) code.sendCode(tel, num).then(data => { if (data === 1) { // console.log('驗證碼發送成功') res.send({ code: '200', msg: '發送驗證碼成功', data: num }) } }).catch(() => { // console.log('驗證碼發送失敗') res.send({ code: '201', msg: '發送驗證碼失敗' }) }) }) /* GET users listing. */ router.get('/', function(req, res, next) { res.send('respond with a resource'); }); // 實現註冊接口 -- post提交方式 router.post('/register', (req, res, next) => { let { username, password, tel } = req.body; sql.find(User, { tel }, { _id: 0 }).then(data => { if (data.length === 0) { let userid = 'users_' + uuid.v1(); password = bcrypt.hashSync(password, salt) sql.insert(User, { userid, username, password, tel}).then(() => { res.send(utils.registersuccess) }) } else { res.send(utils.registered) } }) }) // 實現登錄功能 router.post('/login', (req, res, next) => { let { tel, password } = req.body; sql.find(User, { tel }, { _id: 0 }).then(data => { if (data.length === 0) { res.send(utils.unregister) } else { let pwd = data[0].password; var flag = bcrypt.compareSync(password, pwd) if (flag) { let userid = data[0].userid let username = data[0].username let token = jwt.sign({ userid }, 'daxunxun', { expiresIn: 60*60*24*7// 受權時效7天 }) res.send({ code: '10010', message: '登錄成功', token: token, userid, username }) } else { res.send({ code: '10100', message: '密碼錯誤' }) } } }) }) module.exports = router;
帳號密碼登陸:css
import React, { Component } from 'react'; import { Link } from 'react-router-dom'; import { login } from '@/utils/api'; import { Toast } from 'antd-mobile'; import { withRouter } from 'react-router-dom'; import './style.scss'; class Com extends Component { constructor (props) { super (props); this.state = { tel: '', password: '' } } loginBtn () { let tel = this.state.tel; let password = this.state.password; if (tel === '' || password === '') { Toast.fail('請先輸入用戶名和密碼', 2); } else { login(tel, password).then(data => { console.log(data) if (data.code === '10010') { localStorage.setItem('token', data.token) localStorage.setItem('username', data.username) localStorage.setItem('userid', data.userid) localStorage.setItem('isLogin', 1) Toast.success('登錄成功', 1); this.props.history.push('/user') } else if (data.code === '10086') { Toast.offline('該用戶未註冊,請先註冊', 2); } else if (data.code === '10100') { Toast.fail('密碼錯誤', 2); } }) } } // 手機號 loginTel (event) { let val = event.currentTarget.value; this.setState({ tel: val }) } // 密碼 passwordLogin (event) { let val = event.currentTarget.value; this.setState({ password: val }) } render () { return ( <div className="box"> <header className="header loginHeader"> <div className="imgbox"> <img src="//img.58cdn.com.cn/jxedt/logos/logo3.gif" alt="" /> </div> </header> <div className="content loginColor"> <h2 className="title">登錄</h2> <div className="loginFrom"> <p> <i className="iconfont icon-shoujihao"></i> <input type="text" placeholder="請輸入您的手機號" onBlur={ this.loginTel.bind(this) }/> </p> <p> <i className="iconfont icon-mima"></i> <input type="password" placeholder="請輸入您的密碼" onBlur={ this.passwordLogin.bind(this) }/> </p> <div className="loginBtn" onClick={ this.loginBtn.bind(this) }>登錄</div> <Link className="tabQuick" to="/o/quicklogin">切換至快速登錄</Link> </div> <div className="noUser">尚未註冊?,點擊這裏去<Link to="/o/register">註冊</Link></div> </div> </div> ) } } export default withRouter(Com);
樣式前端
@import '@/lib/reset.scss';
.box {
@include rect(100%, 100%);
@include flexbox();
flex-direction: column;
.loginHeader {
@include rect(100%, 0.5rem);
@include background-color(#54B143);
.imgbox {
@include rect(auto, 100%);
padding: 0.12rem 0 0 0.12rem;
box-sizing: border-box;
img {
display: inline-block;
@include rect(1rem, 0.25rem)
}
}
}
.loginColor {
@include flex();
@include background-color(#fff);
}
.content {
@include flex();
width: 100%;
// height: 100%;
.title {
padding-top: 0.2rem;
@include rect(100%, 0.3rem);
text-align: center;
line-height: 0.3rem;
font-size: 16px;
color: #666;
margin-bottom: 0.2rem;
}
.loginFrom {
width: 100%;
padding: 0 0.2rem;
p {
@include rect(100%, 0.6rem);
@include flexbox();
@include align-items();
// padding-left: 0.2rem;
border-bottom: 1px solid #999;
i {
font-size: 26px;
margin-right: 0.07rem;
}
input {
display: inline-block;
padding-left: 0.1rem;
@include rect(60%, 0.4rem);
border: none;
background: #fff;
}
}
}
.loginBtn {
margin: 0.3rem 0 0 0.34rem;
@include rect(80%, 0.4rem);
@include background-color(#54B143);
border-radius: 20px;
color: #fff;
font-size: 16px;
text-align: center;
line-height: 0.4rem;
}
.tabQuick {
@include rect(100%, 0.3rem);
line-height: 0.3rem;
text-align: center;
margin-top: 0.15rem;
color: limegreen;
display: block;
}
}
.noUser {
@include rect(100%, 0.3rem);
line-height: 0.3rem;
text-align: center;
margin-top: 0.15rem;
}
}
驗證碼登陸node
import React, { Component } from 'react'; import { Link } from 'react-router-dom'; import { quick, quickLogin } from '@/utils/api'; import { Toast } from 'antd-mobile'; import cookie from 'react-cookies'; import { withRouter } from 'react-router-dom' import './style.scss'; class Com extends Component { constructor (props) { super (props); this.state = { tel: '', checkNum: '', num: '', flag: false, text: '獲取驗證碼', _dura: 0 } } componentDidMount () { if (cookie.load('code')) { this.sendCode(); } } loginBtn () { } // 手機號 quickTel (event) { let val = event.currentTarget.value; this.setState({ tel: val }) } // 改變驗證碼狀態 check (event) { let val = event.currentTarget.value; console.log(val) this.setState({ num: val }) } // 判斷cookie中是否存在倒計時我 sendCode () { // console.log(111) this.setState({ flag: true }) let _dura = cookie.load('code'); let timer = setInterval(() => { // console.log(this) _dura--; let text = '從新獲取' + '(' + _dura + ')'; this.setState({ _dura, text }) cookie.save('code', _dura, _dura) if (_dura === 0) { text = '點擊獲取驗證碼'; this.setState({ text, flag: false }) clearInterval(timer); timer = null; cookie.remove('code'); } },1000) } // 發送登錄驗證碼 getQuickCheck () { let tel = this.state.tel; if (tel.length === 0) { Toast.fail('請先輸入您的手機號', 1); } else { quick(tel).then(data => { if (data.code === '200') { cookie.save('code', 60, 60) Toast.success('驗證碼發送成功,請注意查收', 2); this.setState({ checkNum: data.data, flag: true }) this.sendCode(); } else if (data.code === '201') { Toast.fail('驗證碼發送失敗,請不要頻繁點擊', 2); } else { Toast.offline('該用戶尚未註冊,請先註冊', 2); } }) } } // 快速登錄 quickLogin () { let tel = this.state.tel; let num = this.state.num; let checkNum = this.state.checkNum; if (tel.length === 0 || num.length === 0) { Toast.fail('請先輸入手機號和驗證碼', 2); } else { num = Math.round(num) console.log(num) console.log(checkNum) if ( num === checkNum ) { quickLogin(tel).then(data => { if (data.code === '10010') { Toast.success('登錄成功', 2); localStorage.setItem('token', data.token); localStorage.setItem('userid', data.userid); localStorage.setItem('username', data.username); localStorage.setItem('isLogin', 1) this.props.history.push('/user') } }) console.log(1111) } else { Toast.fail('驗證碼不正確,請從新輸入', 2); } } } render () { return ( <div className="box"> <header className="header loginHeader"> <div className="imgbox"> <img src="//img.58cdn.com.cn/jxedt/logos/logo3.gif" alt="" /> </div> </header> <div className="content loginColor"> <h2 className="title">快速登錄</h2> <div className="loginFrom"> <p> <i className="iconfont icon-shoujihao"></i> <input type="text" placeholder="請輸入您的手機號" onBlur={ this.quickTel.bind(this) }/> </p> <p> <i className="iconfont icon-yanzhengma"></i> <input type="text" placeholder="請輸入驗證碼" onBlur={ this.check.bind(this) }/> <button className="checkBtn" disabled={ this.state.flag } onClick={ this.getQuickCheck.bind(this) }>{ this.state.text }</button> </p> <div className="loginBtn" onClick={ this.quickLogin.bind(this) }>登錄</div> <Link className="tabLogin" to="/o/login">切換至密碼登錄</Link> </div> <div className="noUser">尚未註冊?,點擊這裏去<Link to="/o/register">註冊</Link></div> </div> </div> ) } } export default withRouter(Com)
style.cssreact
@import '@/lib/reset.scss';
.box {
@include rect(100%, 100%);
@include flexbox();
flex-direction: column;
.loginHeader {
@include rect(100%, 0.5rem);
@include background-color(#54B143);
.imgbox {
@include rect(auto, 100%);
padding: 0.12rem 0 0 0.12rem;
box-sizing: border-box;
img {
display: inline-block;
@include rect(1rem, 0.25rem)
}
}
}
.loginColor {
@include flex();
@include background-color(#fff);
}
.content {
@include flex();
width: 100%;
// height: 100%;
.title {
padding-top: 0.2rem;
@include rect(100%, 0.3rem);
text-align: center;
line-height: 0.3rem;
font-size: 16px;
color: #666;
margin-bottom: 0.2rem;
}
.loginFrom {
width: 100%;
padding: 0 0.2rem;
p {
@include rect(100%, 0.6rem);
@include flexbox();
@include align-items();
// padding-left: 0.2rem;
border-bottom: 1px solid #999;
i {
font-size: 26px;
margin-right: 0.07rem;
}
input {
display: inline-block;
padding-left: 0.1rem;
@include rect(60%, 0.4rem);
border: none;
background: #fff;
}
.checkBtn {
// display: block;
border: none;
@include rect(1.2rem, 0.3rem);
// background: #fff;
border-radius: 5px;
color: #333;
}
}
}
.loginBtn {
margin: 0.3rem 0 0 0.34rem;
@include rect(80%, 0.4rem);
@include background-color(#54B143);
border-radius: 20px;
color: #fff;
font-size: 16px;
text-align: center;
line-height: 0.4rem;
}
.tabLogin {
@include rect(100%, 0.3rem);
line-height: 0.3rem;
text-align: center;
margin-top: 0.15rem;
color: limegreen;
display: block;
}
}
.noUser {
@include rect(100%, 0.3rem);
line-height: 0.3rem;
text-align: center;
margin-top: 0.15rem;
}
}
import React, { Component } from 'react'; import { getCheck, register } from '@/utils/api'; import './style.scss' import { Toast } from 'antd-mobile'; import { Link, withRouter } from 'react-router-dom'; import cookie from 'react-cookies'; class Com extends Component { constructor (props) { super (props); this.state = { username: '', usernameTip: '', tel: '', telTip: '', password: '', passwordTip: '', codeNum: 0, check: '', checkTip: '', _dura: 0, text: '點擊獲取驗證碼', flag: false } } componentDidMount () { if (cookie.load('sendCode')) { this.sendCode(); } } // 判斷cookie中是否存在倒計時 sendCode () { console.log(111) this.setState({ flag: true }) let _dura = cookie.load('sendCode'); let timer = setInterval(() => { // console.log(this) _dura--; let text = '從新獲取' + '(' + _dura + ')'; this.setState({ _dura, text }) cookie.save('sendCode', _dura, _dura) if (_dura === 0) { text = '點擊獲取驗證碼'; this.setState({ text, flag: false }) clearInterval(timer); timer = null; cookie.remove('sendCode'); } },1000) } // 驗證用戶名格式 username (event) { let val = event.currentTarget.value; let tip = ''; tip = val === '' ? '' : val.length < 2 ? '用戶名要爲2位以上的字符哦' : ''; this.setState({ username: val, usernameTip: tip }) } // 驗證手機號格式 tel (event) { let val = event.currentTarget.value; let tip = ''; if ( val.length === 0 ) { tip = '' }else if ( !(/^1[34578]\d{9}$/.test(val)) ) { tip = '請輸入正確的手機號' } else { tip = '' } this.setState({ tel: val, telTip: tip }) } // 驗證密碼 password (event) { let val = event.currentTarget.value; let tip = ''; if ( val.length === 0) { tip = '' } else if (!(/^[a-zA-Z]{1}([a-zA-Z0-9]|[._]){5,15}$/.test(val)) ) { tip = '密碼必須以字母開頭,6-16位數字、字母、下劃線和.' } else { tip = '' } this.setState({ password: val, passwordTip: tip }) } // 獲取手機驗證碼 getCheck () { let tel = this.state.tel // console.log(tel) if (tel.length !== 0) { getCheck(tel).then(data => { // 設置cookie保存時間 cookie.save('sendCode', 60, 60); // console.log(data) this.setState({ codeNum: data.data.data, // 保存隨機驗證碼,後期用來驗證 flag: true }) this.sendCode(); }) } else { Toast.fail('請先輸入手機號', 1); } } // 填寫驗證碼,保存狀態 check (event) { const val = event.currentTarget.value; // val = Math.round(val) this.setState({ check: val }) } // 註冊按鈕,點擊驗證 register () { let usernameTip = this.state.usernameTip; let telTip = this.state.telTip; let passwordTip = this.state.passwordTip; let username = this.state.username; let tel = this.state.tel; let password = this.state.password; let codeNum = this.state.codeNum; // 若是用戶名,手機號,密碼格式都正確 if (usernameTip === '' && telTip === '' && passwordTip === '') { let val = this.state.check; val = Math.round(val) if ( val === codeNum ) { // console.log('success') register(tel, username, password).then(data => { if (data.code === '10000') { // console.log('該用戶已註冊,請直接登錄') Toast.info('該用戶已註冊,請直接登錄', 1); } else { Toast.success('恭喜您註冊成功', 1); } }) } else { // console.log('驗證碼不正確') Toast.fail('驗證碼不正確', 1); } } else { // console.log('請輸入正確格式的用戶名,手機號和密碼') Toast.fail('請輸入正確格式的用戶名,手機號和密碼', 1); } } render() { return ( <div className="box"> <header className="header registerHeader"> <div className="imgbox"> <img src="//img.58cdn.com.cn/jxedt/logos/logo3.gif" alt="" /> </div> </header> <div className="content registerColor"> <h2 className="title">註冊</h2> <div className="registerFrom"> <p> <i className="iconfont icon-yonghu"></i><input type="text" placeholder="請輸入用戶名" onChange={ this.username.bind(this) }/> </p> <div className="tip">{ this.state.usernameTip }</div> <p> <i className="iconfont icon-shoujihao"></i><input type="text" placeholder="請輸入手機號" onChange={ this.tel.bind(this) }/> </p> <div className="tip">{ this.state.telTip }</div> <p> <i className="iconfont icon-mima"></i><input type="password" placeholder="請輸入密碼" onChange={ this.password.bind(this) }/> </p> <div className="tip">{ this.state.passwordTip }</div> <p> <i className="iconfont icon-yanzhengma"></i> <input type="text" placeholder="請輸入驗證碼" onChange={ this.check.bind(this) }/> <button className="checkBtn" disabled={ this.state.flag } onClick={ this.getCheck.bind(this) }>{ this.state.text }</button> </p> <div className="tip">{ this.state.checkTip }</div> <div className="registerBtn" onClick={ this.register.bind(this) }>確認註冊</div> </div> <div className="toLogin">如您已有帳號,請直接<Link to="/o/login">登錄</Link></div> </div> </div> ) } } export default withRouter(Com)
style.cssweb
@import '@/lib/reset.scss';
.box {
@include rect(100%, 100%);
@include flexbox();
flex-direction: column;
.registerHeader {
@include rect(100%, 0.5rem);
@include background-color(#54B143);
.imgbox {
@include rect(auto, 100%);
padding: 0.12rem 0 0 0.12rem;
box-sizing: border-box;
img {
display: inline-block;
@include rect(1rem, 0.25rem)
}
}
}
.registerColor {
@include flex();
@include background-color(#fff);
}
.content {
@include flex();
width: 100%;
// height: 100%;
.title {
padding-top: 0.2rem;
@include rect(100%, 0.3rem);
text-align: center;
line-height: 0.3rem;
font-size: 16px;
color: #666;
margin-bottom: 0.2rem;
}
.registerFrom {
width: 100%;
padding: 0 0.2rem;
.tip {
@include rect(100%, 0.2rem);
text-align: center;
// line-height: 0.2rem;
color: lightsalmon;
}
p {
@include rect(100%, 0.6rem);
@include flexbox();
@include align-items();
// padding-left: 0.2rem;
border-bottom: 1px solid #999;
i {
font-size: 26px;
margin-right: 0.07rem;
}
input {
display: inline-block;
padding-left: 0.1rem;
@include rect(60%, 0.4rem);
border: none;
background: #fff;
}
.checkBtn {
display: inline-block;
border: none;
@include rect(1.2rem, 0.4rem);
color: #333;
border-radius: 8px;
text-align: center;
line-height: 0.4rem;
}
}
}
.registerBtn {
margin: 0.3rem 0 0 0.34rem;
@include rect(80%, 0.4rem);
@include background-color(#54B143);
border-radius: 20px;
color: #fff;
font-size: 16px;
text-align: center;
line-height: 0.4rem;
}
}
.toLogin {
@include rect(100%, 0.3rem);
line-height: 0.3rem;
text-align: center;
margin-top: 0.15rem;
}
}
// 發送短信驗證碼
const Core = require('@alicloud/pop-core');
var client = new Core({
accessKeyId: 'LTAIZQoVVoPuBjU9', // 本身的id
accessKeySecret: 'GfJuI2dLsCQh7Q56TmFxPTniXjkVnB', // 本身的secret
endpoint: 'https://dysmsapi.aliyuncs.com',
apiVersion: '2017-05-25'
});
module.exports = {
sendCode (tel, code) {
var params = {
"RegionId": "cn-hangzhou",
"PhoneNumbers": tel,
"SignName": "吳勳勳", // 本身的簽名
"TemplateCode": "SMS_111785721", // 本身的模板代碼
"TemplateParam": "{code: " + code + "}"
}
var requestOption = {
method: 'POST'
};
return new Promise((resolve, reject) => {
client.request('SendSms', params, requestOption).then((result) => {
console.log(JSON.stringify(result));
resolve(1)
}, (ex) => {
console.log(ex);
reject()
})
})
}
}