react中登录注册 使用验证码验证
后端接口
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;
前端渲染
账号密码登录:
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; } }
验证码登录
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.css
@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.css
@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() }) }) } }
- 上一篇 »Python实战项目-7发送验证码/短信登录/短信注册接口
- 下一篇 »php编写验证码