• luffy-(7)


    内容概览

    • 登录注册模态框分析
    • 登录注册前端页面
    • 腾讯短信功能二次封装
    • 短信验证码接口
    • 短信登录接口

    登录注册模态框分析

    Login.vue

    登录框直接显示在主页上,所以在components中写一格登录组件

    <template>
      <div class="login">
        
        <span @click="handleClose">Xspan>
      div>
    template>
    
    <script>
    export default {
      name: "Login",
      methods: {
        handleClose() {  // 使用的自定义事件与父组件交互
          this.$emit('close')
        }
      }
    }
    script>
    
    <style scoped>
    .login {
      width: 100vw;
      height: 100vh; /*当前组件的宽高占满整个屏幕*/
      position: fixed; /*固定定位*/
      top: 0;
      left: 0;
      z-index: 10; /*z坐标,越高越优先显示*/
      background-color: rgba(0, 0, 0, 0.5); /*三原色加透明度*/
    }
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    Header.vue
    <div class="right-part">
      <div>
        <span @click="put_login">登录span>
        <span class="line">|span>
        <span>注册span>
      div>
      <Login v-if="is_login" @close="closeLogin">Login>
    div>
    
    <script>
    import Login from '@/components/Login'
    
    export default {
      name: "Header",
      data() {
        return {
          url_path: sessionStorage.url_path || '/',
          is_login: false,  // 控制登录框是否显示
        }
      },
      methods: {
        goPage(url_path) {
          // 已经是当前路由就没有必要重新跳转
          if (this.url_path !== url_path) {
            // 传入的参数,如果不等于当前路径,就跳转
            this.$router.push(url_path)
          }
          sessionStorage.url_path = url_path;
        },
        put_login() {  // 当点击登录显示登录框
          this.is_login = true
        },
        closeLogin() {  // 当子组件点击关闭时关闭登录框
          this.is_login = false
        },
      },
      components: {Login},
      created() {
        sessionStorage.url_path = this.$route.path
        this.url_path = this.$route.path
      }
    }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    登录注册前端页面

    Header.vue
    <template>
      <div class="header">
        <div class="slogan">
          <p>老男孩IT教育 | 帮助有志向的年轻人通过努力学习获得体面的工作和生活p>
        div>
        <div class="nav">
          <ul class="left-part">
            <li class="logo">
              <router-link to="/">
                <img src="../assets/img/head-logo.svg" alt="">
              router-link>
            li>
            <li class="ele">
              <span @click="goPage('/free-course')" :class="{active: url_path === '/free-course'}">免费课span>
            li>
            <li class="ele">
              <span @click="goPage('/actual-course')" :class="{active: url_path === '/actual-course'}">实战课span>
            li>
            <li class="ele">
              <span @click="goPage('/light-course')" :class="{active: url_path === '/light-course'}">轻课span>
            li>
          ul>
    
          <div class="right-part">
            <div>
              <span @click="put_login">登录span>
              <span class="line">|span>
              <span @click="put_register">注册span>
            div>
            <Login v-if="is_login" @close="close_login" @go="put_register">Login>
            <Register v-if="is_register" @close="close_register" @go="put_login">Register>
    
          div>
        div>
      div>
    
    template>
    
    <script>
    import Login from "@/components/Login";
    import Register from "@/components/Register";
    
    export default {
      name: "Header",
      data() {
        return {
          url_path: sessionStorage.url_path || '/',
          is_login: false,
          is_register: false,
        }
      },
      methods: {
        goPage(url_path) {
          // 已经是当前路由就没有必要重新跳转
          if (this.url_path !== url_path) {
            // 传入的参数,如果不等于当前路径,就跳转
            this.$router.push(url_path)
          }
          sessionStorage.url_path = url_path;
        },
        goLogin() {
          this.loginShow = true
        },
        put_login() {
          this.is_login = true;
          this.is_register = false;
        },
        put_register() {
          this.is_login = false;
          this.is_register = true;
        },
        close_login() {
          this.is_login = false;
        },
        close_register() {
          this.is_register = false;
        }
      },
      created() {
        sessionStorage.url_path = this.$route.path
        this.url_path = this.$route.path
      },
      components: {
        Login,
        Register
      }
    }
    script>
    
    <style scoped>
    .header {
      background-color: white;
      box-shadow: 0 0 5px 0 #aaa;
    }
    
    .header:after {
      content: "";
      display: block;
      clear: both;
    }
    
    .slogan {
      background-color: #eee;
      height: 40px;
    }
    
    .slogan p {
      width: 1200px;
      margin: 0 auto;
      color: #aaa;
      font-size: 13px;
      line-height: 40px;
    }
    
    .nav {
      background-color: white;
      user-select: none;
      width: 1200px;
      margin: 0 auto;
    
    }
    
    .nav ul {
      padding: 15px 0;
      float: left;
    }
    
    .nav ul:after {
      clear: both;
      content: '';
      display: block;
    }
    
    .nav ul li {
      float: left;
    }
    
    .logo {
      margin-right: 20px;
    }
    
    .ele {
      margin: 0 20px;
    }
    
    .ele span {
      display: block;
      font: 15px/36px '微软雅黑';
      border-bottom: 2px solid transparent;
      cursor: pointer;
    }
    
    .ele span:hover {
      border-bottom-color: orange;
    }
    
    .ele span.active {
      color: orange;
      border-bottom-color: orange;
    }
    
    .right-part {
      float: right;
    }
    
    .right-part .line {
      margin: 0 10px;
    }
    
    .right-part span {
      line-height: 68px;
      cursor: pointer;
    }
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    Login.vue
    <template>
      <div class="login">
        <div class="box">
          <i class="el-icon-close" @click="close_login">i>
          <div class="content">
            <div class="nav">
              <span :class="{active: login_method === 'is_pwd'}"
                    @click="change_login_method('is_pwd')">密码登录span>
              <span :class="{active: login_method === 'is_sms'}"
                    @click="change_login_method('is_sms')">短信登录span>
            div>
            <el-form v-if="login_method === 'is_pwd'">
              <el-input
                  placeholder="用户名/手机号/邮箱"
                  prefix-icon="el-icon-user"
                  v-model="username"
                  clearable>
              el-input>
              <el-input
                  placeholder="密码"
                  prefix-icon="el-icon-key"
                  v-model="password"
                  clearable
                  show-password>
              el-input>
              <el-button type="primary">登录el-button>
            el-form>
            <el-form v-if="login_method === 'is_sms'">
              <el-input
                  placeholder="手机号"
                  prefix-icon="el-icon-phone-outline"
                  v-model="mobile"
                  clearable
                  @blur="check_mobile">
              el-input>
              <el-input
                  placeholder="验证码"
                  prefix-icon="el-icon-chat-line-round"
                  v-model="sms"
                  clearable>
                <template slot="append">
                  <span class="sms" @click="send_sms">{{ sms_interval }}span>
                template>
              el-input>
              <el-button type="primary">登录el-button>
            el-form>
            <div class="foot">
              <span @click="go_register">立即注册span>
            div>
          div>
        div>
      div>
    template>
    
    <script>
    export default {
      name: "Login",
      data() {
        return {
          username: '',
          password: '',
          mobile: '',
          sms: '',
          login_method: 'is_pwd',
          sms_interval: '获取验证码',
          is_send: false,
        }
      },
      methods: {
        close_login() {
          this.$emit('close')
        },
        go_register() {
          this.$emit('go')
        },
        change_login_method(method) {
          this.login_method = method;
        },
        check_mobile() {
          if (!this.mobile) return;
          if (!this.mobile.match(/^1[3-9][0-9]{9}$/)) {
            this.$message({
              message: '手机号有误',
              type: 'warning',
              duration: 1000,
              onClose: () => {
                this.mobile = '';
              }
            });
            return false;
          }
          this.is_send = true;
        },
        send_sms() {
    
          if (!this.is_send) return;
          this.is_send = false;
          let sms_interval_time = 60;
          this.sms_interval = "发送中...";
          let timer = setInterval(() => {
            if (sms_interval_time <= 1) {
              clearInterval(timer);
              this.sms_interval = "获取验证码";
              this.is_send = true; // 重新回复点击发送功能的条件
            } else {
              sms_interval_time -= 1;
              this.sms_interval = `${sms_interval_time}秒后再发`;
            }
          }, 1000);
        }
      }
    }
    script>
    
    <style scoped>
    .login {
      width: 100vw;
      height: 100vh;
      position: fixed;
      top: 0;
      left: 0;
      z-index: 10;
      background-color: rgba(0, 0, 0, 0.3);
    }
    
    .box {
      width: 400px;
      height: 420px;
      background-color: white;
      border-radius: 10px;
      position: relative;
      top: calc(50vh - 210px);
      left: calc(50vw - 200px);
    }
    
    .el-icon-close {
      position: absolute;
      font-weight: bold;
      font-size: 20px;
      top: 10px;
      right: 10px;
      cursor: pointer;
    }
    
    .el-icon-close:hover {
      color: darkred;
    }
    
    .content {
      position: absolute;
      top: 40px;
      width: 280px;
      left: 60px;
    }
    
    .nav {
      font-size: 20px;
      height: 38px;
      border-bottom: 2px solid darkgrey;
    }
    
    .nav > span {
      margin: 0 20px 0 35px;
      color: darkgrey;
      user-select: none;
      cursor: pointer;
      padding-bottom: 10px;
      border-bottom: 2px solid darkgrey;
    }
    
    .nav > span.active {
      color: black;
      border-bottom: 3px solid black;
      padding-bottom: 9px;
    }
    
    .el-input, .el-button {
      margin-top: 40px;
    }
    
    .el-button {
      width: 100%;
      font-size: 18px;
    }
    
    .foot > span {
      float: right;
      margin-top: 20px;
      color: orange;
      cursor: pointer;
    }
    
    .sms {
      color: orange;
      cursor: pointer;
      display: inline-block;
      width: 70px;
      text-align: center;
      user-select: none;
    }
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    Register.vue
    <template>
      <div class="register">
        <div class="box">
          <i class="el-icon-close" @click="close_register">i>
          <div class="content">
            <div class="nav">
              <span class="active">新用户注册span>
            div>
            <el-form>
              <el-input
                  placeholder="手机号"
                  prefix-icon="el-icon-phone-outline"
                  v-model="mobile"
                  clearable
                  @blur="check_mobile">
              el-input>
              <el-input
                  placeholder="密码"
                  prefix-icon="el-icon-key"
                  v-model="password"
                  clearable
                  show-password>
              el-input>
              <el-input
                  placeholder="验证码"
                  prefix-icon="el-icon-chat-line-round"
                  v-model="sms"
                  clearable>
                <template slot="append">
                  <span class="sms" @click="send_sms">{{ sms_interval }}span>
                template>
              el-input>
              <el-button type="primary">注册el-button>
            el-form>
            <div class="foot">
              <span @click="go_login">立即登录span>
            div>
          div>
        div>
      div>
    template>
    
    <script>
    export default {
      name: "Register",
      data() {
        return {
          mobile: '',
          password: '',
          sms: '',
          sms_interval: '获取验证码',
          is_send: false,
        }
      },
      methods: {
        close_register() {
          this.$emit('close', false)
        },
        go_login() {
          this.$emit('go')
        },
        check_mobile() {
          if (!this.mobile) return;
          if (!this.mobile.match(/^1[3-9][0-9]{9}$/)) {
            this.$message({
              message: '手机号有误',
              type: 'warning',
              duration: 1000,
              onClose: () => {
                this.mobile = '';
              }
            });
            return false;
          }
          this.is_send = true;
        },
        send_sms() {
          if (!this.is_send) return;
          this.is_send = false;
          let sms_interval_time = 60;
          this.sms_interval = "发送中...";
          let timer = setInterval(() => {
            if (sms_interval_time <= 1) {
              clearInterval(timer);
              this.sms_interval = "获取验证码";
              this.is_send = true; // 重新回复点击发送功能的条件
            } else {
              sms_interval_time -= 1;
              this.sms_interval = `${sms_interval_time}秒后再发`;
            }
          }, 1000);
        }
      }
    }
    script>
    
    <style scoped>
    .register {
      width: 100vw;
      height: 100vh;
      position: fixed;
      top: 0;
      left: 0;
      z-index: 10;
      background-color: rgba(0, 0, 0, 0.3);
    }
    
    .box {
      width: 400px;
      height: 480px;
      background-color: white;
      border-radius: 10px;
      position: relative;
      top: calc(50vh - 240px);
      left: calc(50vw - 200px);
    }
    
    .el-icon-close {
      position: absolute;
      font-weight: bold;
      font-size: 20px;
      top: 10px;
      right: 10px;
      cursor: pointer;
    }
    
    .el-icon-close:hover {
      color: darkred;
    }
    
    .content {
      position: absolute;
      top: 40px;
      width: 280px;
      left: 60px;
    }
    
    .nav {
      font-size: 20px;
      height: 38px;
      border-bottom: 2px solid darkgrey;
    }
    
    .nav > span {
      margin-left: 90px;
      color: darkgrey;
      user-select: none;
      cursor: pointer;
      padding-bottom: 10px;
      border-bottom: 2px solid darkgrey;
    }
    
    .nav > span.active {
      color: black;
      border-bottom: 3px solid black;
      padding-bottom: 9px;
    }
    
    .el-input, .el-button {
      margin-top: 40px;
    }
    
    .el-button {
      width: 100%;
      font-size: 18px;
    }
    
    .foot > span {
      float: right;
      margin-top: 20px;
      color: orange;
      cursor: pointer;
    }
    
    .sms {
      color: orange;
      cursor: pointer;
      display: inline-block;
      width: 70px;
      text-align: center;
      user-select: none;
    }
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183

    腾讯短信功能二次封装

    封装v3版本

    sdk:https://cloud.tencent.com/document/product/382/43196#

    # 1. 下载模块:pip install tencentcloud-sdk-python
    
    # -*- coding: utf-8 -*-
    from tencentcloud.common import credential
    from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
    # 导入对应产品模块的client models。
    from tencentcloud.sms.v20210111 import sms_client, models
    
    # 导入可选配置类
    from tencentcloud.common.profile.client_profile import ClientProfile
    from tencentcloud.common.profile.http_profile import HttpProfile
    
    try:
        cred = credential.Credential("AKIDTXcdXG6u5C9xxxxxxxxxD5VwPp", "LZgaKxTOI0VowxxxxxxvDKDtLcfWCiqm")
        httpProfile = HttpProfile()
        httpProfile.reqMethod = "POST"  # post请求(默认为post请求)
        httpProfile.reqTimeout = 30  # 请求超时时间,单位为秒(默认60秒)
        httpProfile.endpoint = "sms.tencentcloudapi.com"  # 指定接入地域域名(默认就近接入)
    
        # 非必要步骤:
        # 实例化一个客户端配置对象,可以指定超时时间等配置
        clientProfile = ClientProfile()
        clientProfile.signMethod = "TC3-HMAC-SHA256"  # 指定签名算法
        clientProfile.language = "en-US"
        clientProfile.httpProfile = httpProfile
        client = sms_client.SmsClient(cred, "ap-guangzhou", clientProfile)
        req = models.SendSmsRequest()
        req.SmsSdkAppId = "1400763090" # 腾讯短信创建app把app的id号复制过来https://console.cloud.tencent.com/smsv2/app-manage
        # 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名
        # 签名信息可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-sign) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-sign) 的签名管理查看
        req.SignName = "XX公众号"
        # 模板 ID: 必须填写已审核通过的模板 ID
        # 模板 ID 可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-template) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-template) 的正文模板管理查看
        req.TemplateId = "1603526"
        # 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致,,若无模板参数,则设置为空
        req.TemplateParamSet = ["8888",'100']
        # 下发手机号码,采用 E.164 标准,+[国家或地区码][手机号]
        # 示例如:+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号
        req.PhoneNumberSet = ["8613711112222"]
        # 用户的 session 内容(无需要可忽略): 可以携带用户侧 ID 等上下文信息,server 会原样返回
        req.SessionContext = ""
        req.ExtendCode = ""
        req.SenderId = ""
    
        resp = client.SendSms(req)
    
        # 输出json格式的字符串回包
        print(resp.to_json_string(indent=2))
    
    except TencentCloudSDKException as err:
        print(err)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    把发送短信封装成包
    """后期别的项目可能也需要发送短信;将发送短信的功能封装成包后,后续只需要调用包到项目中使用即可"""
    
    """
    包的目录结构
    	send_tx_sms  # 包名
    		__init__.py
    		settings.py  # 配置文件
    		sms.py  # 核心代码
    """
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • _init_.py
      from .sms import get_code,send_sms_by_phone
      
      • 1
    • settiongs.py
      SECRET_ID = ''
      SECRET_KEY = ''
      APP_ID = ''
      SIGN_NAME=''
      TEMPLATE_ID=''
      
      • 1
      • 2
      • 3
      • 4
      • 5
    • sms.py
      import random
      
      from tencentcloud.common import credential
      from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
      # 导入对应产品模块的client models。
      from tencentcloud.sms.v20210111 import sms_client, models
      
      # 导入可选配置类
      from tencentcloud.common.profile.client_profile import ClientProfile
      from tencentcloud.common.profile.http_profile import HttpProfile
      from . import settings
      
      
      # 获取n位随机数组验证码的函数
      def get_code(num=4):
          code = ''
          for i in range(num):
              random_num = random.randint(0, 9)
              code += str(random_num)
          return code
      
      
      # 发送短信函数
      def send_sms_by_phone(mobile, code):
          try:
              cred = credential.Credential(settings.SECRET_ID, settings.SECRET_KEY)
              httpProfile = HttpProfile()
              httpProfile.reqMethod = "POST"  # post请求(默认为post请求)
              httpProfile.reqTimeout = 30  # 请求超时时间,单位为秒(默认60秒)
              httpProfile.endpoint = "sms.tencentcloudapi.com"  # 指定接入地域域名(默认就近接入)
      
              # 非必要步骤:
              # 实例化一个客户端配置对象,可以指定超时时间等配置
              clientProfile = ClientProfile()
              clientProfile.signMethod = "TC3-HMAC-SHA256"  # 指定签名算法
              clientProfile.language = "en-US"
              clientProfile.httpProfile = httpProfile
              client = sms_client.SmsClient(cred, "ap-guangzhou", clientProfile)
              req = models.SendSmsRequest()
              req.SmsSdkAppId = settings.APP_ID  # 腾讯短信创建app把app的id号复制过来https://console.cloud.tencent.com/smsv2/app-manage
              # 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名
              # 签名信息可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-sign) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-sign) 的签名管理查看
              req.SignName = settings.SIGN_NAME
              # 模板 ID: 必须填写已审核通过的模板 ID
              # 模板 ID 可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-template) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-template) 的正文模板管理查看
              req.TemplateId = settings.TEMPLATE_ID
              # 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致,,若无模板参数,则设置为空
              req.TemplateParamSet = [code, '1']
              # 下发手机号码,采用 E.164 标准,+[国家或地区码][手机号]
              # 示例如:+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号
              req.PhoneNumberSet = ["+86" + mobile, ]
              # 用户的 session 内容(无需要可忽略): 可以携带用户侧 ID 等上下文信息,server 会原样返回
              req.SessionContext = ""
              req.ExtendCode = ""
              req.SenderId = ""
      
              resp = client.SendSms(req)
      
              # 输出json格式的字符串回包
              # 字符串类型
              print(type(resp.to_json_string(indent=2)))
      
              return True
          except TencentCloudSDKException as err:
      
              return False
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66

    短信验证码接口

    from libs.send_tx_sms import get_code, send_sms_by_phone
    from django.core.cache import cache
    
    class UserView(ViewSet):
    
        @action(methods=['GET'], detail=False)
        def send_sms(self, request):
            mobile = request.query_params.get('mobile')
            if re.match(r'^1[3-9]\d{9}$', mobile):  # 通过正则校验手机号
                code = get_code()  # 获取随机验证码
                cache.set(f'sms_code_{mobile}', code)  # 保存验证码到内存中
                res = send_sms_by_phone(mobile, code)
                if res:
                    return APIResponse(msg='发送短信成功')
                else:
                    # raise APIException('发送短信失败')
                    return APIResponse(msg='发送短信失败', code=101)
            else:
                return APIResponse(msg='手机号不合法', code=102)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    短信登录接口

    view.py
    class UserView(ViewSet):
    
        def get_serializer(self, data):
            # 判断如果请求的action是:mul_login,返回UserMulLoginSerializer
            # 判断如果请求的action是:mobile_login,返回UserMobileLoginSerializer
            if self.action == 'mul_login':
                return UserMulLoginSerializer(data=data)
            else:
                return UserMobileLoginSerializer(data=data)
    
        def common_login(self, request):
            ser = self.get_serializer(data=request.data)
            ser.is_valid(raise_exception=True)
            token = ser.context.get('token')
            username = ser.context.get('username')
            icon = ser.context.get('icon')
            return APIResponse(token=token, username=username, icon=icon)
    
        @action(methods=['GET'], detail=False)
        def send_sms(self, request):
            mobile = request.query_params.get('mobile')
            if re.match(r'^1[3-9]\d{9}$', mobile):  # 通过正则校验手机号
                code = get_code()  # 获取随机验证码
                cache.set(f'sms_code_{mobile}', code)  # 保存验证码到内存中
                res = send_sms_by_phone(mobile, code)
                if res:
                    return APIResponse(msg='发送短信成功')
                else:
                    # raise APIException('发送短信失败')
                    return APIResponse(msg='发送短信失败', code=101)
            else:
                return APIResponse(msg='手机号不合法', code=102)
    
        @action(methods=['GET'], detail=False)
        def mobile(self, request):
            try:
                mobile = request.query_params.get('mobile')
                UserInfo.objects.get(mobile=mobile)
                return APIResponse(msg='手机号存在')
            except Exception as e:
                raise APIException('手机号不存在')
    
    	"""多用户登录与手机号登录内部代码只有序列化类不同,抽取成一个方法"""
        @action(methods=['POST'], detail=False)
        def mul_login(self, request):
            return self.common_login(request)
    
        @action(methods=['POST'], detail=False)
        def mobile_login(self, request):
            return self.common_login(request)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    serializers.py
    # 多个序列化类使用了相同的代码,抽取出一个父类
    class ModelUserSerializer(serializers.ModelSerializer):
    
        def _get_token(self, user):
            try:
                payload = jwt_payload_handler(user)
                token = jwt_encode_handler(payload)
                return token
            except Exception as e:
                raise APIException(str(e))
    
    
        def validate(self, attrs):
            # 1  手机号和code
            user = self._get_user(attrs)
            # 2 签发token
            token = self._get_token(user)
            # 3 把token放到序列化类对象中
            self.context['token'] = token
            self.context['username'] = user.username
            self.context['icon'] = 'http://127.0.0.1:8000/media/' + str(user.icon)
            return attrs
    
    
    class UserMulLoginSerializer(ModelUserSerializer):
        username = serializers.CharField()
    
        class Meta:
            model = UserInfo
            fields = ['username', 'password']
    
        def _get_user(self, attrs):
            # attrs是校验后的数据:通过了字段自己的校验和局部钩子才会执行到这里
            username = attrs.get('username')
            password = attrs.get('password')
            # username是手机号/邮箱/用户名其中之一,我们使用正则判断
            if re.match(r'^1[3-9]\d{9}$', username):
                user = authenticate(mobile=username, password=password)
            elif re.match(r'^.+@.+\..+$', username):
                user = authenticate(email=username, password=password)
            else:
                user = authenticate(username=username, password=password)
            if user:
                return user
            else:
                raise ValidationError('用户名或密码错误')
    
    
    class UserMobileLoginSerializer(ModelUserSerializer):
        code = serializers.CharField()
        mobile = serializers.CharField()
    
        class Meta:
            model = UserInfo
            fields = ['mobile', 'code']
    
        def _get_user(self, attrs):
            mobile = attrs.get('mobile')
            code = attrs.get('code')
            # 校验code是否正确
            old_code = cache.get('sms_code_%s' % mobile)
            cache.set('sms_code_%s' % mobile, '')  # 验证码用过了要清除
            if code == old_code:  # 万能验证码,在测试阶段,测试用的
                user = UserInfo.objects.filter(mobile=mobile).first()
                return user
    
            raise APIException('验证码错误')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
  • 相关阅读:
    servlet注解开发,简化web.xml文件
    车路协同 智能路侧设备网络安全接入技术要求
    「网页开发|前端开发|Vue」09 Vue状态管理Vuex:让页面根据用户登录状态渲染不同内容
    laravel练习03
    2022年python国际化课程上机考试二题解
    解析用户消费记录(数据分析三剑客综合使用)
    SQL语句书写规范
    Android GB28181设备接入端语音广播和语音对讲技术实现探究
    Java基础篇 IO流
    【C语言】数据的存储_数据类型:浮点型存储
  • 原文地址:https://blog.csdn.net/AL_QX/article/details/127807195