Articles Catalogue
User Registration and Authentication
Front-end display registration page
Adjust the link between the header of the home page and the registration button of the landing page.
Registration page Register, mainly through the login page to change.
<template> <div class="box"> <img src="/static/image/Loginbg.3377d0c.jpg" alt=""> <div class="register"> <div class="register_box"> <div class="register-title">Registered Lufei School Town</div> <div class="inp"> <input v-model="mobile" type="text" placeholder="Phone number" class="user"> <input v-model="password" type="password" placeholder="Input password" class="user"> <input v-model="sms" type="text" placeholder="Input Verification Code" class="user"> <div id="geetest"></div> <button class="register_btn" >register</button> <p class="go_login" >Existing accounts <router-link to="/user/login">Direct login</router-link></p> </div> </div> </div> </div> </template> <script> export default { name: 'Register', data(){ return { sms:"", mobile:"", validateResult:false, } }, created(){ }, methods:{}, }; </script> <style scoped> .box{ width: 100%; height: 100%; position: relative; overflow: hidden; } .box img{ width: 100%; min-height: 100%; } .box .register { position: absolute; width: 500px; height: 400px; left: 0; margin: auto; right: 0; bottom: 0; top: -338px; } .register .register-title{ width: 100%; font-size: 24px; text-align: center; padding-top: 30px; padding-bottom: 30px; color: #4a4a4a; letter-spacing: .39px; } .register-title img{ width: 190px; height: auto; } .register-title p{ font-size: 18px; color: #fff; letter-spacing: .29px; padding-top: 10px; padding-bottom: 50px; } .register_box{ width: 400px; height: auto; background: #fff; box-shadow: 0 2px 4px 0 rgba(0,0,0,.5); border-radius: 4px; margin: 0 auto; padding-bottom: 40px; } .register_box .title{ font-size: 20px; color: #9b9b9b; letter-spacing: .32px; border-bottom: 1px solid #e6e6e6; display: flex; justify-content: space-around; padding: 50px 60px 0 60px; margin-bottom: 20px; cursor: pointer; } .register_box .title span:nth-of-type(1){ color: #4a4a4a; border-bottom: 2px solid #84cc39; } .inp{ width: 350px; margin: 0 auto; } .inp input{ outline: 0; width: 100%; height: 45px; border-radius: 4px; border: 1px solid #d9d9d9; text-indent: 20px; font-size: 14px; background: #fff !important; } .inp input.user{ margin-bottom: 16px; } .inp .rember{ display: flex; justify-content: space-between; align-items: center; position: relative; margin-top: 10px; } .inp .rember p:first-of-type{ font-size: 12px; color: #4a4a4a; letter-spacing: .19px; margin-left: 22px; display: -ms-flexbox; display: flex; -ms-flex-align: center; align-items: center; /*position: relative;*/ } .inp .rember p:nth-of-type(2){ font-size: 14px; color: #9b9b9b; letter-spacing: .19px; cursor: pointer; } .inp .rember input{ outline: 0; width: 30px; height: 45px; border-radius: 4px; border: 1px solid #d9d9d9; text-indent: 20px; font-size: 14px; background: #fff !important; } .inp .rember p span{ display: inline-block; font-size: 12px; width: 100px; /*position: absolute;*/ /*left: 20px;*/ } #geetest{ margin-top: 20px; } .register_btn{ width: 100%; height: 45px; background: #84cc39; border-radius: 5px; font-size: 16px; color: #fff; letter-spacing: .26px; margin-top: 30px; } .inp .go_login{ text-align: center; font-size: 14px; color: #9b9b9b; letter-spacing: .26px; padding-top: 20px; } .inp .go_login span{ color: #84cc39; cursor: pointer; } </style>
Front-end registered routing:
import Register from "../components/Register" // Configure Routing List export default new Router({ mode:"history", routes:[ // Routing List ... { path: "/user/register", name:"Register", component:Register, } ] })
Modify the connection at the head of the home page:
# Header.vue <span class="header-register"><router-link to="/register">register</router-link></span> #Login.vue <p class="go_login" >No account <router-link to="/register">Immediate registration</router-link></p>
Implementation of Registration Function
View code:
from rest_framework.generics import CreateAPIView from .models import User from .seriazliers import UserModelSerializer class UserAPIView(CreateAPIView): queryset = User.objects.filter(is_active=True).all() serializer_class = UserModelSerializer
Serializer, user sub-application to create sreializers.py file, code:
from rest_framework import serializers from .models import User class UserModelSerializer(serializers.ModelSerializer): """User Registered Serializer""" # Field declaration sms_code = serializers.CharField(write_only=True, max_length=6, help_text="Short Message Verification Code") token = serializers.CharField(max_length=1024, read_only=True, help_text="jwt Of token Character string") # Model information class Meta: model = User fields = ["id", "mobile", "password", "sms_code", "token"] extra_kwargs = { "id":{ "read_only":True, }, "mobile":{ "max_length": 15, "required": True }, "password":{ "write_only": True, "max_length": 128, "required": True } } # Validation data # Save information def create(self, validated_data): """Adding users""" # Delete fields that do not exist in the database del validated_data["sms_code"] # Save user information and set default values for some fields validated_data["username"] = validated_data["mobile"] # Call the create of the current model serializer parent class user = super().create(validated_data) # Generate the token value of jwt to record login status from rest_framework_jwt.settings import api_settings jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER payload = jwt_payload_handler(user) # Finally, token is returned to the client as a field of the user model user.token = jwt_encode_handler(payload) return user
Routing address
from django.urls import path, re_path from . import views from rest_framework_jwt.views import obtain_jwt_token urlpatterns = [ path(r'authorizations/', obtain_jwt_token, name='authorizations'), path(r'captcha/', views.CaptchaAPIView.as_view()), path(r'', views.UserAPIView.as_view() ), ]
Client submits registration information
Next, we need to re-register the page to provide the function of sending SMS validation, so the page is adjusted, code:
<template> <div class="box"> <img src="/static/image/Loginbg.3377d0c.jpg" alt=""> <div class="register"> <div class="register_box"> <div class="register-title">Registered Lufei School Town</div> <div class="inp"> <input v-model="mobile" type="text" placeholder="Phone number" class="user"> <input v-model="password" type="password" placeholder="Input password" class="user"> <div class="sms_code"> <input v-model="sms" type="text" maxlength="6" placeholder="Input Verification Code" class="user"> <span class="code_text">Click Send SMS</span> </div> <div id="geetest"></div> <button class="register_btn" @click="registerHander">register</button> <p class="go_login" >Existing accounts <router-link to="/user/login">Direct login</router-link></p> </div> </div> </div> </div> </template> <script> export default { name: 'Register', data(){ return { sms:"", mobile:"", password:"", validateResult:false, } }, created(){ }, methods:{ registerHander(){ // User registration // 1. Accept validation data if( !/1[3-9]\d{9}/.test(this.mobile) ){ this.$message("Sorry, there's a wrong cell phone number!"); } if( this.password.length < 6 || this.password.length > 16 ){ this.$message("Sorry, password must be kept at 6-16 Between Bit Characters"); } if( this.sms.length != 6 ){ this.$message("Sorry, the SMS authentication code is wrong!"); } // 2. Send ajax this.$axios.post(`${this.$settings.Host}/user/`,{ mobile:this.mobile, password:this.password, sms_code:this.sms }).then(response=>{ // Save login status sessionStorage.user_id = response.data.id; sessionStorage.user_name = response.data.mobile; sessionStorage.user_token = response.data.token; let self = this; this.$alert("Welcome to register Lufei School Town!","login was successful",{ callback(){ self.$router.push("/"); } }) }) } }, }; </script> <style scoped> .box{ width: 100%; height: 100%; position: relative; overflow: hidden; } .box img{ width: 100%; min-height: 100%; } .box .register { position: absolute; width: 500px; height: 400px; left: 0; margin: auto; right: 0; bottom: 0; top: -338px; } .register .register-title{ width: 100%; font-size: 24px; text-align: center; padding-top: 30px; padding-bottom: 30px; color: #4a4a4a; letter-spacing: .39px; } .register-title img{ width: 190px; height: auto; } .register-title p{ font-size: 18px; color: #fff; letter-spacing: .29px; padding-top: 10px; padding-bottom: 50px; } .register_box{ width: 400px; height: auto; background: #fff; box-shadow: 0 2px 4px 0 rgba(0,0,0,.5); border-radius: 4px; margin: 0 auto; padding-bottom: 40px; } .register_box .title{ font-size: 20px; color: #9b9b9b; letter-spacing: .32px; border-bottom: 1px solid #e6e6e6; display: flex; justify-content: space-around; padding: 50px 60px 0 60px; margin-bottom: 20px; cursor: pointer; } .register_box .title span:nth-of-type(1){ color: #4a4a4a; border-bottom: 2px solid #84cc39; } .inp{ width: 350px; margin: 0 auto; } .inp input{ outline: 0; width: 100%; height: 45px; border-radius: 4px; border: 1px solid #d9d9d9; text-indent: 20px; font-size: 14px; background: #fff !important; } .inp input.user{ margin-bottom: 16px; } .inp .rember{ display: flex; justify-content: space-between; align-items: center; position: relative; margin-top: 10px; } .inp .rember p:first-of-type{ font-size: 12px; color: #4a4a4a; letter-spacing: .19px; margin-left: 22px; display: -ms-flexbox; display: flex; -ms-flex-align: center; align-items: center; /*position: relative;*/ } .inp .rember p:nth-of-type(2){ font-size: 14px; color: #9b9b9b; letter-spacing: .19px; cursor: pointer; } .inp .rember input{ outline: 0; width: 30px; height: 45px; border-radius: 4px; border: 1px solid #d9d9d9; text-indent: 20px; font-size: 14px; background: #fff !important; } .inp .rember p span{ display: inline-block; font-size: 12px; width: 100px; /*position: absolute;*/ /*left: 20px;*/ } #geetest{ margin-top: 20px; } .register_btn{ width: 100%; height: 45px; background: #84cc39; border-radius: 5px; font-size: 16px; color: #fff; letter-spacing: .26px; margin-top: 30px; } .inp .go_login{ text-align: center; font-size: 14px; color: #9b9b9b; letter-spacing: .26px; padding-top: 20px; } .inp .go_login span{ color: #84cc39; cursor: pointer; } .sms_code{ position: relative; } .code_text{ position: absolute; right: 14px; top: 13px; border-left: 1px solid orange; padding-left: 14px; cursor: pointer; background-color: #fff; } </style>
Next, we complete the configuration of redis database in the registration process.
If you need to operate redis directly through code in django, you need to install django-redis.
This module is based on pyredis. Pyredis is the most commonly used module to operate redis in python.
It will provide all commands of redis as function names for developers to use, and the parameters of each command function are the command parameters in the terminal.
pip install django-redis
Add a code to the settings/dev.py configuration:
# Setting up redis cache CACHES = { # Default cache "default": { "BACKEND": "django_redis.cache.RedisCache", # When the project goes online, you need to adjust the path here. "LOCATION": "redis://127.0.0.1:6379/0", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", } }, # session storage for xadmin or admin "session": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379/1", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", } }, # Provide Storage Short Message Verification Code "sms_code":{ "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379/2", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", } } } # When setting xadmin user login, login information session is saved to redis SESSION_ENGINE = "django.contrib.sessions.backends.cache" SESSION_CACHE_ALIAS = "session"
For the use of django-redis, the documentation is available at http://django-redis-chs.readthedocs.io/zh_CN/latest./
django-redis provides a method to get_redis_connection. The configuration name of redis can be passed by calling get_redis_connection method to get the connection object of redis. The redis command can be executed through the connection object of redis.
https://redis-py.readthedocs.io/en/latest/
Use examples:
from django_redis import get_redis_connection // link redis data base redis_conn = get_redis_connection("default")
Sending SMS Using Cloud Communication
Common text messaging platforms: Aliyun, Tencent, Netease, Sina, Baidu, Yung Lianyun, Rose Cat
Get information on the login platform:
ACCOUNT SID: xxx AUTH TOKEN : xxx AppID(default): xxx Rest URL(production): app.cloopen.com:8883 [Use Real Short Message Sending Server for Project Online] Rest URL(Development): sandboxapp.cloopen.com:8883 [Sandbox Short Message Sending Server for Project Development]
Find sdkdemo for download
In the development process, in order to save the cost of sending short messages, you can add your own or colleague's mobile phone to the test number.
Back-end Generation of Short Message Verification Code
settings/dev.py configuration
# Configuration of Short Message Verification Code SMS_ACCOUNTSID = "8aa*************************d228e" SMS_ACCOUNTTOKEN = "62fc*************************03de" SMS_APPID = "8aaf*************************f782295" SMS_SERVERIP = "sandboxapp.cloopen.com" SMS_EXPIRE_TIME = 300 SMS_INTERVAL_TIME = 60 SMS_TEMPLATE_ID = 1
users/views.py view file
from rest_framework.views import APIView from luffyapi.libs.geetest import GeetestLib from rest_framework.response import Response from django.conf import settings class CaptchaAPIView(APIView): """Extreme Verification Code""" def get(self,request): """Pipeline Number and State for Generating Verification Codes""" gt = GeetestLib(settings.PC_GEETEST_ID, settings.PC_GEETEST_KEY) status = gt.pre_process(settings.PC_GEETEST_USER_ID) response_str = gt.get_response_str() return Response(response_str) def post(self,request): """Secondary verification""" gt = GeetestLib(settings.PC_GEETEST_ID, settings.PC_GEETEST_KEY) challenge = request.data.get(gt.FN_CHALLENGE, '') validate = request.data.get(gt.FN_VALIDATE, '') seccode = request.data.get(gt.FN_SECCODE, '') result = gt.success_validate(challenge, validate, seccode, settings.PC_GEETEST_USER_ID) if not result: result = gt.failback_validate(challenge, validate, seccode) return Response({"status": result}) from rest_framework.generics import CreateAPIView from .models import User from .seriazliers import UserModelSerializer class UserAPIView(CreateAPIView): queryset = User.objects.filter(is_active=True).all() serializer_class = UserModelSerializer """ /users/sms/13112345678 """ from rest_framework import status import random from django_redis import get_redis_connection from luffyapi.libs.yuntongxun.sms import CCP class SMSCodeAPIView(APIView): def get(self,request,mobile): """Send short messages""" # 1. Verify that the mobile phone number has been registered. try: User.objects.get(mobile=mobile) return Response({"message": "Sorry, the mobile phone number has been registered at present."}, status=status.HTTP_400_BAD_REQUEST) except User.DoesNotExist: # If the mobile phone number does not exist in the database, it means no registration and no processing. pass # 2. Verify the sending interval of SMS [60s] # 2.1 Link redis redis = get_redis_connection("sms_code") # Use get to get the value of the specified key and return None if not interval = redis.get("exp_%s" % mobile) if interval: return Response({"message": "Sorry, the short message interval!"}, status=status.HTTP_403_FORBIDDEN) # 3.1 Generating Random Short Message Verification Code sms_code = "%06d" % random.randint(0,999999) # 3.2 Save the validation code sent to redis [mobile phone number, short message validation code, short message sending interval, short message validity] """ //Character string setex sms_Mobile phone number 300 short message verification code setex exp_Mobile phone number 60 _ """ redis.setex("sms_%s" % mobile, settings.SMS_EXPIRE_TIME, sms_code) redis.setex("exp_%s" % mobile, settings.SMS_INTERVAL_TIME, "_") # 3.3 Call sdk to send short messages ccp = CCP() result = ccp.send_template_sms(mobile, [sms_code, settings.SMS_EXPIRE_TIME//60 ], settings.SMS_TEIMPLATE_ID) # 4. Return the result of sending SMS if result == -1: return Response({"message":"Failed to send SMS!"},status=status.HTTP_500_INTERNAL_SERVER_ERROR) else: return Response({"message":"Successful text messaging!"},status=status.HTTP_200_OK)
Backend saves user registration information
Create serializer objects [not involving mobile phone validation code functionality for the time being]
from rest_framework import serializers from .models import User import re class UserModelSerializer(serializers.ModelSerializer): """User Information Serializer""" sms_code = serializers.CharField(label='Mobile phone verification code', required=True, allow_null=False, allow_blank=False, write_only=True) password2 = serializers.CharField(label='Confirm password', required=True, allow_null=False, allow_blank=False, write_only=True) class Meta: model=User fields = ('sms_code', 'mobile', 'password','password2') extra_kwargs={ "password":{ "write_only":True } } def validate_mobile(self, value): """Verification of mobile phone number""" if not re.match(r'^1[345789]\d{9}$', value): raise serializers.ValidationError('Incorrect format of mobile phone number') # Verify that the phone number has been registered # try: # user = User.objects.get(mobile=value) # except: # user = None # # if user: # Raise serializers. Validation Error ('The current phone number has been registered') # Code above to verify the existence of mobile phone number [Optimized Edition] try: User.objects.get(mobile=value) # If the user information is obtained, the following code will not be executed. If the user information is not obtained, the mobile phone number has not been registered and can be directly pass ed. raise serializers.ValidationError('Current mobile phone number has been registered') except: pass return value def validate(self,data): # Verify password password = data.get("password") password2 = data.get("password2") if len(password)<6: raise serializers.ValidationError('Passwords are too short and unsafe~') if password !=password2: raise serializers.ValidationError('Password and confirmation must be consistent~') return data def create(self, validated_data): # Delete fields that do not need to be saved to the database del validated_data['password2'] del validated_data['sms_code'] # Because the default user name is unique in the database, we use the user's mobile phone number as the user name. validated_data["username"] = validated_data["mobile"] # Continue calling the built-in add data function of ModelSerializer user = super().create(validated_data) # Encryption for passwords user.set_password(user.password) # Modifying passwords and so on are used to update passwords, so they need to be saved. user.save() return user
View code:
users/views.py
# users/views.py from rest_framework.generics import CreateAPIView from .models import User from .serializers import UserModelSerializer class UserAPIView(CreateAPIView): """user management""" queryset = User.objects.all() serializer_class = UserModelSerializer
Setting up routing
urls.py
# Subapplication routing urls.py urlpatterns=[ ... path(r"user", views.UserAPIView.as_view()), ]
Client sends registration information and SMS
<template> <div class="box"> <img src="https://www.luffycity.com/static/img/Loginbg.3377d0c.jpg" alt=""> <div class="register"> <div class="register_box"> <div class="register-title">Registered Lufei School Town</div> <div class="inp"> <!--<el-select v-model="region">--> <!--<el-option v-for="item in region_list" :label="item.nation_name+'('+item.nation_code+')'" :value="item.nation_code"></el-option>--> <!--</el-select>--> <input v-model = "mobile" type="text" placeholder="Phone number" class="user"> <input v-model = "password" type="password" placeholder="Password" class="user"> <input v-model = "password2" type="password" placeholder="Confirm password" class="user"> <div id="geetest"></div> <div class="sms"> <input v-model="sms_code" maxlength="16" type="text" placeholder="Input Verification Code" class="user"> <span class="get_sms" @click="send_sms">{{get_sms_text}}</span> </div> <button class="register_btn" @click="registerHander">register</button> <p class="go_login" >Existing accounts <router-link to="/login">Direct login</router-link></p> </div> </div> </div> </div> </template> <script> export default { name: 'Register', data(){ return { region:"+86", sms_code:"", password:"", password2:"", mobile:"", validateResult:false, get_sms_text:"Get the authentication code", } }, created(){ // Set the area number when the page is initialized // this.region_list = this.$nation; // Display Picture Verification Code this.$axios.get("http://127.0.0.1:8000/users/verify").then(response=>{ // Successful request let data = response.data; // Using initGeetest interface // Parametric 1: Configuration parameters // Parametric 2: Callback, the first parameter validation code object of the callback, which can then be used for events such as appendTo console.log(response.data); data = JSON.parse(data); initGeetest({ gt: data.gt, challenge: data.challenge, width: "350px", product: "embed", // Product form, including: float, embed ded, popup. Note that only valid for PC version validation code offline: !data.success // Represents that the user background checks whether the polar test server is down or not, and generally does not need attention. // For more configuration parameters, see http://www.geetest.com/install/sections/idx-client-sdk.html#config }, this.handlerPopup); }).catch(error=>{ console.log(error) }) }, methods:{ send_sms(){ let reg = /1[1-9]{2}\d{8}/; if( !reg.test(this.mobile) ){ return false; } // If get_sms_text is not text, but a number, it means that the current mobile phone number is still within 60 seconds of sending short messages. if(this.get_sms_text != "Get the authentication code"){ return false; } // Send short messages let _this = this; this.$axios.get("http://127.0.0.1:8000/users/sms?mobile="+this.mobile).then(response=>{ console.log(response); // Display text countdown after sending SMS let time = 60; let timer = setInterval(()=>{ --time; if(time <=1){ // If the countdown is 0, turn off the current timer _this.get_sms_text = "Get the authentication code"; clearInterval(timer); }else{ _this.get_sms_text = time; } },1000) }).catch(error=>{ console.log(error); }) }, registerHander(){ // Registration Information Submission // Determine whether the user has passed the validation code before submitting the data if(!this.validateResult){ alert("Error in Verification Code Validation"); return false; } this.$axios.post("http://127.0.0.1:8000/users/user",{ "mobile":this.mobile, "password":this.password, "password2":this.password2, "sms_code":this.sms_code, },{ responseType:"json", }). then(response=>{ // Successful request, save login status localStorage.removeItem("token"); let data = response.data; sessionStorage.token = data.token; sessionStorage.id = data.id; sessionStorage.username = data.mobile; // After successful registration, the default indication is that you have logged in and jumped to the user center page. // this.$router.push("/user"); alert("login was successful!"); }).catch(error=>{ console.log(error); }) }, handlerPopup(captchaObj){ // Callback of Verification Code Successfully let _this = this; captchaObj.onSuccess(function () { var validate = captchaObj.getValidate(); _this.$axios.post("http://127.0.0.1:8000/users/verify",{ geetest_challenge: validate.geetest_challenge, geetest_validate: validate.geetest_validate, geetest_seccode: validate.geetest_seccode },{ responseType:"json", }).then(response=>{ // Successful request console.log(response.data); if(response.data.status == "success") { _this.validateResult = true; // Get the validation results } }).catch(error=>{ // request was aborted console.log(error) }) }); // Add the validation code to the element with id captcha captchaObj.appendTo("#geetest"); } }, }; </script> <style scoped> .box{ width: 100%; height: 100%; position: relative; overflow: hidden; } .el-select{ width:100%; margin-bottom: 15px; } .box img{ width: 100%; min-height: 100%; } .box .register { position: absolute; width: 500px; height: 400px; top: 0; left: 0; margin: auto; right: 0; bottom: 0; top: -338px; } .register .register-title{ width: 100%; font-size: 24px; text-align: center; padding-top: 30px; padding-bottom: 30px; color: #4a4a4a; letter-spacing: .39px; } .register-title img{ width: 190px; height: auto; } .register-title p{ font-family: PingFangSC-Regular; font-size: 18px; color: #fff; letter-spacing: .29px; padding-top: 10px; padding-bottom: 50px; } .sms{ margin-top: 15px; position: relative; } .sms .get_sms{ position: absolute; right: 15px; top: 14px; font-size: 14px; color: #ffc210; cursor: pointer; border-left: 1px solid #979797; padding-left: 20px; } .register_box{ width: 400px; height: auto; background: #fff; box-shadow: 0 2px 4px 0 rgba(0,0,0,.5); border-radius: 4px; margin: 0 auto; padding-bottom: 40px; } .register_box .title{ font-size: 20px; color: #9b9b9b; letter-spacing: .32px; border-bottom: 1px solid #e6e6e6; display: flex; justify-content: space-around; padding: 50px 60px 0 60px; margin-bottom: 20px; cursor: pointer; } .register_box .title span:nth-of-type(1){ color: #4a4a4a; border-bottom: 2px solid #84cc39; } .inp{ width: 350px; margin: 0 auto; } .inp input{ border: 0; outline: 0; width: 100%; height: 45px; border-radius: 4px; border: 1px solid #d9d9d9; text-indent: 20px; font-size: 14px; background: #fff !important; } .inp input.user{ margin-bottom: 16px; } .inp .rember{ display: flex; justify-content: space-between; align-items: center; position: relative; margin-top: 10px; } .inp .rember p:first-of-type{ font-size: 12px; color: #4a4a4a; letter-spacing: .19px; margin-left: 22px; display: -ms-flexbox; display: flex; -ms-flex-align: center; align-items: center; /*position: relative;*/ } .inp .rember p:nth-of-type(2){ font-size: 14px; color: #9b9b9b; letter-spacing: .19px; cursor: pointer; } .inp .rember input{ outline: 0; width: 30px; height: 45px; border-radius: 4px; border: 1px solid #d9d9d9; text-indent: 20px; font-size: 14px; background: #fff !important; } .inp .rember p span{ display: inline-block; font-size: 12px; width: 100px; /*position: absolute;*/ /*left: 20px;*/ } #geetest{ margin-top: 20px; } .register_btn{ width: 100%; height: 45px; background: #84cc39; border-radius: 5px; font-size: 16px; color: #fff; letter-spacing: .26px; margin-top: 30px; } .inp .go_login{ text-align: center; font-size: 14px; color: #9b9b9b; letter-spacing: .26px; padding-top: 20px; } .inp .go_login span{ color: #84cc39; cursor: pointer; } </style>
Be careful:
If HTTP 403 error occurs during sending validation code test, clear the local cookie and try again.