zoukankan      html  css  js  c++  java
  • 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();
         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 {
           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();
         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 {
           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();
         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 {
           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()
          })
        })
      }
    }
  • 相关阅读:
    es6异步编程 Promise 讲解 --------各个优点缺点总结
    js重新讲解继承,es5的一些继承,es6继承的改变 ----------由浅入深
    node.js里的buffer常见操作,copy,concat等实例讲解
    node.js 写流 createWriteStream----由浅入深
    node.js 读取文件--createReadStream
    Java的位运算符—— 与(&)、非(~)、或(|)、异或(^)
    XML的特殊字符处理
    mysql语句收藏
    MYSQL学习
    利用HTML5 LocalStorage实现跨页面通信channel
  • 原文地址:https://www.cnblogs.com/hy96/p/11914757.html
Copyright © 2011-2022 走看看