zoukankan      html  css  js  c++  java
  • 短信接口频率限制、验证码登陆接口、前台发送验证码、前台验证码登陆、后台注册接口、前台注册功能、redis

    ## 1 短信接口频率限制

    ```python
    # throttlings.py
    from rest_framework.throttling import SimpleRateThrottle
    class SMSThrotting(SimpleRateThrottle):
    scope = 'sms'
    def get_cache_key(self, request, view):
    telephone = request.query_params.get('telephone')
    #'throttle_%(scope)s_%(ident)s'%{}
    return self.cache_format%{'scope':self.scope,'ident':telephone}

    # 配置在视图类上
    from .throttlings import SMSThrotting
    class SendSmSView(ViewSet):
    throttle_classes = [SMSThrotting,]

    # 早setting中配置
    REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES':{
    'sms':'1/m' # key要跟类中的scop对应
    }
    }
    ```



    ## 2 验证码登陆接口

    ```python
    # views.py
    @action(methods=['POST'],detail=False)
    def code_login(self,request,*args,**kwargs):
    ser = serializer.CodeUserSerilaizer(data=request.data)
    if ser.is_valid():
    token = ser.context['token']
    username = ser.context['user'].username
    return APIResponse(token=token, username=username)
    else:
    return APIResponse(code=0,msg=ser.errors)

    # serializer
    class CodeUserSerilaizer(serializers.ModelSerializer):
    code=serializers.CharField()
    class Meta:
    model = models.User
    fields = ['telephone', 'code']

    def validate(self, attrs):
    user=self._get_user(attrs)
    # 用户存在,签发token
    token = self._get_token(user)
    self.context['token'] = token
    self.context['user'] = user
    return attrs


    def _get_user(self, attrs):
    from django.core.cache import cache
    from django.conf import settings
    import re
    telephone = attrs.get('telephone')
    code = attrs.get('code')

    # 取出原来的code
    cache_code=cache.get(settings.PHONE_CACHE_KEY%telephone)
    if code ==cache_code:
    # 验证码通过
    if re.match('^1[3-9][0-9]{9}$', telephone):
    user = models.User.objects.filter(telephone=telephone).first()
    if user:
    # 把使用过的验证码删除
    cache.set(settings.PHONE_CACHE_KEY % telephone,'')
    return user
    else:
    raise ValidationError('用户不存在')
    else:
    raise ValidationError('手机号不合法')
    else:
    raise ValidationError('验证码错误')


    def _get_token(self, user):
    from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler
    payload = jwt_payload_handler(user) # 通过user对象获得payload
    token = jwt_encode_handler(payload) # 通过payload获得token
    return token

    ```



    ## 3 前台发送验证码

    ```python
    <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" @click="login_password">登录</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" @click="code_login">登录</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;
    //字符串.match(/正则表达式/)
    if (!this.mobile.match(/^1[3-9][0-9]{9}$/)) {
    this.$message({
    message: '手机号有误',
    type: 'warning',
    duration: 1000,
    onClose: () => {
    this.mobile = '';
    }
    });
    return false;
    }
    //发送axios请求
    // this.$axios.get(this.$settings.base_url+'/user/check_telephone/telephone='+this.mobile})
    this.$axios.get(this.$settings.base_url + '/user/check_telephone/', {params: {telephone: this.mobile}}).then(response => {
    if (response.data.code) {
    //手机号存在,允许发送验证码
    this.is_send = true;
    } else {
    this.$message({
    message: '手机号不存在',
    type: 'warning',
    duration: 1000,
    onClose: () => {
    this.mobile = '';
    }
    });
    }
    }).catch(error => {
    console.log(error)
    })

    },
    send_sms() {

    if (!this.is_send) return;
    this.is_send = false;
    let sms_interval_time = 60;
    this.sms_interval = "发送中...";

    this.$axios.get(this.$settings.base_url + '/user/send/', {params: {'telephone': this.mobile}})
    .then(response => {
    if (response.data.code) {
    this.$message({
    message: '发送验证码成功',
    type: 'success',
    duration: 1000,

    });
    }
    })


    // setInterval(()=>{},100)
    //定时器:每隔一秒种,把数字减一,当减到小于1,按钮又能点了,显示获取验证码
    let timer = setInterval(() => {
    if (sms_interval_time <= 1) {
    clearInterval(timer); //如果小于等于1,把定时器清除
    this.sms_interval = "获取验证码";
    this.is_send = true; // 重新回复点击发送功能的条件
    } else {
    sms_interval_time -= 1;
    this.sms_interval = `${sms_interval_time}秒后再发`;
    }
    }, 1000);
    },

    login_password() {
    if (this.username && this.password) {
    //发送请求
    this.$axios.post(this.$settings.base_url + '/user/login/', {
    username: this.username,
    password: this.password


    }).then(response => {
    console.log(response.data)
    //把用户信息保存到cookie中
    // this.$cookies.set('key','value','过期时间,按s计')
    this.$cookies.set('token', response.data.token, '7d')
    this.$cookies.set('username', response.data.username, '7d')
    //关闭登录窗口(子传父)
    this.$emit('close')
    //给父组件,Head传递一个事件,让它从cookie中取出token和username
    this.$emit('loginsuccess')
    }).catch(errors => {
    })
    } else {
    this.$message({
    message: '用户名或密码必须填哦',
    type: 'warning',

    });
    }
    },
    code_login() {

    if (this.mobile && this.sms) {
    //发送请求
    this.$axios.post(this.$settings.base_url + '/user/code_login/', {
    telephone: this.mobile,
    code: this.sms

    }).then(response => {
    console.log(response.data)
    //把用户信息保存到cookie中
    // this.$cookies.set('key','value','过期时间,按s计')
    this.$cookies.set('token', response.data.token, '7d')
    this.$cookies.set('username', response.data.username, '7d')
    //关闭登录窗口(子传父)
    this.$emit('close')
    //给父组件,Head传递一个事件,让它从cookie中取出token和username
    this.$emit('loginsuccess')
    }).catch(errors => {
    })
    } else {
    this.$message({
    message: '手机号或验证码必填',
    type: 'warning',

    });
    }

    },
    }
    }
    </script>

    <style scoped>
    .login {
    100vw;
    height: 100vh;
    position: fixed;
    top: 0;
    left: 0;
    z-index: 10;

    }

    .box {
    400px;
    height: 420px;

    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;
    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 {
    100%;
    font-size: 18px;
    }

    .foot > span {
    float: right;
    margin-top: 20px;
    color: orange;
    cursor: pointer;
    }

    .sms {
    color: orange;
    cursor: pointer;
    display: inline-block;
    70px;
    text-align: center;
    user-select: none;
    }
    </style>
    ```



    ## 4 前台验证码登陆

    ```python
    #详细代码看第三条,前台发送验证码
    ```



    ## 5 后台注册接口

    ```python
    # urls.py
    router.register('register', views.RegisterView, 'register') # /user/register post请求就是新增

    # views.py
    class RegisterView(GenericViewSet,CreateModelMixin):
    queryset = models.User.objects.all()
    serializer_class = serializer.UserRegisterSerilaizer
    def create(self, request, *args, **kwargs):
    response=super().create(request, *args, **kwargs)
    username=response.data.get('username')
    return APIResponse(code=1,msg='注册成功',username=username)

    # serializer.py
    class UserRegisterSerilaizer(serializers.ModelSerializer):
    code=serializers.CharField(max_length=4,min_length=4,write_only=True)
    class Meta:
    model = models.User
    fields = ['telephone', 'code','password','username']
    extra_kwargs = {
    'password': {'max_length': 18,'min_length':8},
    'username': {'read_only':True}
    }



    def validate(self, attrs):
    telephone = attrs.get('telephone')
    code = attrs.get('code')
    # 取出原来的code
    cache_code = cache.get(settings.PHONE_CACHE_KEY % telephone)
    if code == cache_code:
    # 验证码通过
    if re.match('^1[3-9][0-9]{9}$', telephone):
    attrs['username']=telephone # 把用户的名字设成手机号
    attrs.pop('code')
    return attrs
    else:
    raise ValidationError('手机号不合法')
    else:
    raise ValidationError('验证码错误')


    # 重写create方法
    def create(self, validated_data):
    user=models.User.objects.create_user(**validated_data)
    return user
    ```



    ## 6 前台注册功能

    ```python
    # 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" @click="register">注册</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;
    //字符串.match(/正则表达式/)
    if (!this.mobile.match(/^1[3-9][0-9]{9}$/)) {
    this.$message({
    message: '手机号有误',
    type: 'warning',
    duration: 1000,
    onClose: () => {
    this.mobile = '';
    }
    });
    return false;
    }
    this.$axios.get(this.$settings.base_url + '/user/check_telephone/', {params: {telephone: this.mobile}}).then(response => {
    if (response.data.code) {
    this.$message({
    message: '您已经注册过了,快去登录把',
    type: 'warning',
    duration: 1000,
    onClose: () => {
    this.go_login()
    }
    });

    } else {
    this.is_send = true;
    this.$message({
    message: '该用户没有注册过,欢迎注册我们的平台',
    type: 'success',
    duration: 1000,
    });
    }
    }).catch(error => {
    console.log(error)
    })

    },
    send_sms() {

    if (!this.is_send) return;
    this.is_send = false;
    let sms_interval_time = 60;
    this.sms_interval = "发送中...";

    this.$axios.get(this.$settings.base_url + '/user/send/', {params: {'telephone': this.mobile}})
    .then(response => {
    if (response.data.code) {
    this.$message({
    message: '发送验证码成功',
    type: 'success',
    duration: 1000,

    });
    }
    })


    // setInterval(()=>{},100)
    //定时器:每隔一秒种,把数字减一,当减到小于1,按钮又能点了,显示获取验证码
    let timer = setInterval(() => {
    if (sms_interval_time <= 1) {
    clearInterval(timer); //如果小于等于1,把定时器清除
    this.sms_interval = "获取验证码";
    this.is_send = true; // 重新回复点击发送功能的条件
    } else {
    sms_interval_time -= 1;
    this.sms_interval = `${sms_interval_time}秒后再发`;
    }
    }, 1000);
    },
    register() {
    if (this.mobile && this.sms && this.password) {
    this.$axios.post(this.$settings.base_url + '/user/register/', {
    telephone: this.mobile,
    code: this.sms,
    password: this.password
    }).then(response => {
    if (response.data.code) {
    //注册成功,来个提示,跳转到登录
    this.$message({
    message: '注册成功',
    type: 'success',
    duration: 1000,
    onClose: () => {
    this.go_login()
    }
    });
    } else {
    this.$message({
    message: '未知错误',
    type: 'error',
    duration: 1000,
    onClose: () => {
    this.mobile = ''
    this.sms = ''
    this.password = ''
    }
    });
    }
    })
    } else {
    this.$message({
    message: '你有没填的信息',
    type: 'error',
    duration: 1000,
    });
    }
    },
    }
    }
    </script>

    <style scoped>
    .register {
    100vw;
    height: 100vh;
    position: fixed;
    top: 0;
    left: 0;
    z-index: 10;

    }

    .box {
    400px;
    height: 480px;

    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;
    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 {
    100%;
    font-size: 18px;
    }

    .foot > span {
    float: right;
    margin-top: 20px;
    color: orange;
    cursor: pointer;
    }

    .sms {
    color: orange;
    cursor: pointer;
    display: inline-block;
    70px;
    text-align: center;
    user-select: none;
    }
    </style>
    ```



    ## 7 redis

    ```python
    #1 key-value的存储方式,value有很多数据类型:5大:string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型
    #2 内存数据库
    #3 与Memcached比较:
    -1 Memcached只支持一种数据类型字符串
    -2 Memcached不支持持久化(不支持存到硬盘上,只要一断电,数据就没了)

    # 4 使用Redis有哪些好处?
    (1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
    (2) 支持丰富数据类型,支持string,list,set,sorted set,hash
    (3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
    (4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
    # 5 单线程,单进程,不存在并发访问的问题(新版本已经不是了)
    -单线程为什么这么快
    -数据在内存(最重要的)
    -io多路复用技术
    -因为没有进程,线程间的切换
    # 6 redis适合的场景
    1 排行榜
    2 网站访问量,文章访问量
    3 缓存数据库(用的最多,就是做缓存)
    4 发布订阅
    5 去重
    6 分布式(blpop)

    # 7 安装
    -Redis-x64-3.2.100.msi 安装包
    -redis-desktop-manager-0.9.3.817.exe 等同于navicate


    # 8 使用
    -服务端和客户端
    -安装完,服务端自动启动:redis-server 配置文件.conf
    -redis-cli :客户端连接服务端(redis-cli -h 127.0.0.1 -p 6379)
    ```
  • 相关阅读:
    ETL利器Kettle实战应用解析系列三 【ETL后台进程执行配置方式】
    ETL利器Kettle实战应用解析系列二 【应用场景和实战DEMO下载】
    Kettle使用介绍
    java反射详解
    request详解
    java访问接口
    原生JS写Ajax的请求函数-原生ajax
    阿拉伯数字金额转换为大写
    深入理解Java中的String
    Strust2中,加入监听器来判断用户是否在session中存在。
  • 原文地址:https://www.cnblogs.com/0B0S/p/13618066.html
Copyright © 2011-2022 走看看