文章目录

1-登录注册页面

前提:基于element-ui环境

模态登录组件

<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>

模态注册组件

<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>

导航条:结合实际情况完成样式

<template>
<div class="nav">
<span @click="put_login">登录</span>
<span @click="put_register">注册</span>
<Login v-if="is_login" @close="close_login" @go="put_register" />
<Register v-if="is_register" @close="close_register" @go="put_login" />
</div>
</template> <script>
import Login from "./Login";
import Register from "./Register";
export default {
name: "Nav",
data() {
return {
is_login: false,
is_register: false,
}
},
methods: {
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;
}
},
components: {
Login,
Register
}
}
</script> <style scoped> </style>

登录业务分析

多方式登录

"""
1)前台提供账号密码,账号可能是 用户名、手机号、邮箱等 接口:
后台只需要提供一个多方式登录接口即可 - 多方式登录接口
"""
验证码登录

"""
1)前台提供手机号和验证码完成登录 接口:
前台填完手机号,往后台发送校验手机号的请求,如果存在继续,不存在提示注册 - 手机号存在与否接口
前台点击发送验证码,将手机再次发送给后台,后台将手机号通知给第三方,发送短信 - 手机验证码接口
前台点击登录提交手机号与验证码,完成验证码登录 - 验证码登录接口
"""

注册业务分析

验证码注册

"""
1)前台提供手机号、验证码、密码完成注册 接口:
前台填完手机号,往后台发送校验手机号的请求,如果不存在继续,存在提示登录 - 手机号存在与否接口
前台点击发送验证码,将手机再次发送给后台,后台将手机号通知给第三方,发送短信 - 手机验证码接口
前台点击注册提交手机号、验证码及密码,完成验证码注册 - 验证码注册接口
"""

汇总

"""
多方式登录接口
手机号存在与否接口
手机验证码接口
验证码登录接口
验证码注册接口
"""

前台填完手机号,往后台发送校验手机号的请求,如果存在继续,不存在提示注册 - 手机号存在与否接口
前台点击发送验证码,将手机再次发送给后台,后台将手机号通知给第三方,发送短信 - 手机验证码接口
前台点击登录提交手机号与验证码,完成验证码登录 - 验证码登录接口

2 多方式登录

后台

插件
>: pip install djangorestframework-jwt
urls.py
path('login/', views.LoginViewSet.as_view({'post': 'login'})),
dev.py
import datetime
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
}
views.py
from rest_framework.viewsets import ViewSet
from . import serializers, models
from utils.response import APIResponse
class LoginViewSet(ViewSet):
# 局部禁用认证、权限组件
authentication_classes = ()
permission_classes = () def login(self, request, *args, **kwargs):
serializer = serializers.LoginSerializer(data=request.data, context={'request': request})
if serializer.is_valid():
token = serializer.context.get('token') # 原来要一个个拿信息
# username = serializer.context.get('username')
# icon = serializer.context.get('icon') # 拿到登录用户,直接走序列化过程,将要返回给前台的数据直接序列化好给前台
user = serializer.context.get('user')
result = serializers.LoginSerializer(user, context={'request': request}).data
result['token'] = token # id,username,icon,token
return APIResponse(result=result)
return APIResponse(1, serializer.errors)
serializers.py
from rest_framework import serializers
from rest_framework import exceptions
from django.conf import settings
from . import models class LoginSerializer(serializers.ModelSerializer):
# 覆盖,避免login校验username有数据库唯一字段约束的限制
username = serializers.CharField() class Meta:
model = models.User
# username、password可以通过局部钩子指定详细的校验规则
fields = ('id', 'username', 'password', 'icon')
extra_kwargs = {
'id': {
'read_only': True,
},
'icon': {
'read_only': True,
},
'password': {
'write_only': True,
}
} def validate(self, attrs):
# 多方式得到user
user = self._get_user(attrs)
# user签发token
token = self._get_token(user)
# token用context属性携带给视图类
self.context['token'] = token ''' 自己将user的信息逐个处理,传给视图
# 前台可能不仅仅只需要登录成功的token,可能还需要用户名、用户头像等
self.context['username'] = user.username # 通过请求头格式化icon
request = self.context['request']
icon = 'http://%s%s%s' % (request.META['HTTP_HOST'], settings.MEDIA_URL, user.icon)
self.context['icon'] = icon
'''
# 将登录用户对象直接传给视图
self.context['user'] = user return attrs def _get_user(self, attrs):
import re
username = attrs.get('username')
if re.match(r'^1[3-9][0-9]{9}$', username):
user = models.User.objects.filter(mobile=username).first()
else:
user = models.User.objects.filter(username=username).first()
if not user:
raise exceptions.ValidationError({'username': 'username error'}) password = attrs.get('password')
if not user.check_password(password):
raise exceptions.ValidationError({'password': 'password error'}) return user def _get_token(self, user):
from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
return token

前台

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" @click="login">登录</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);
}, login() {
if (!(this.username && this.password)) {
this.$message({
message: '请填好账号密码',
type: 'warning',
duration: 1500
});
return false // 直接结束逻辑
} this.$axios({
url: this.$settings.base_url + '/user/login/',
method: 'post',
data: {
username: this.username,
password: this.password,
}
}).then(response => {
let username = response.data.result.username;
let token = response.data.result.token;
let user_id = response.data.result.id;
this.$cookies.set('username', username, '7d');
this.$cookies.set('token', token, '7d');
this.$cookies.set('user_id', user_id, '7d');
this.$emit('success', response.data.result);
}).catch(error => {
}).catch(error => {
console.log(error.response.data)
})
}
}
}
</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>
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 v-if="!token">
<span @click="put_login">登录</span>
<span class="line">|</span>
<span @click="put_register">注册</span>
</div>
<div v-else>
<span>{{ username }}</span>
<span class="line">|</span>
<span @click="logout">注销</span>
</div>
</div>
</div>
<Login v-if="is_login" @close="close_login" @go="put_register" @success="success_login" />
<Register v-if="is_register" @close="close_register" @go="put_login" />
</div>
</template> <script>
import Login from './Login'
import Register from "./Register" export default {
name: "Header",
data() {
return {
url_path: sessionStorage.url_path || '/',
token: '',
username: '',
user_id: '',
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;
},
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;
},
success_login(data) {
this.is_login = false;
this.username = data.username;
this.token = data.token;
this.user_id = data.user_id;
},
logout() {
this.token = '';
this.username = '';
this.user_id = '';
this.$cookies.remove('username');
this.$cookies.remove('token');
this.$cookies.remove('user_id');
}
},
created() {
sessionStorage.url_path = this.$route.path;
this.url_path = this.$route.path; // 检测cookies,查看登录状态
this.username = this.$cookies.get('username');
this.token = this.$cookies.get('token');
this.user_id = this.$cookies.get('user_id');
},
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>

3-手机是否存在验证接口

后台

urls.py
path('mobile/', views.MobileViewSet.as_view({'post': 'check'})),
views.py
# 手机是否存在接口
import re
class MobileViewSet(ViewSet):
def check(self, request, *args, **kwargs):
mobile = request.data.get('mobile', None)
if not mobile:
return APIResponse(1, 'mobile field required')
if not re.match(r'^1[3-9][0-9]{9}$', mobile):
return APIResponse(1, 'mobile field error')
try:
models.User.objects.get(mobile=mobile)
return APIResponse(result=True) # 手机号存在
except:
return APIResponse(result=False) # 手机号不存在

4-腾讯云短信开发

腾讯云短信开发

短信服务应用申请

""" 准备工作
1)创建短信应用 - 应用管理
2)申请短信签名 - 国内短信 > 签名管理
3)申请短信模块 - 国内短信 > 正文模板管理
"""

python中开发腾讯云短信服务

"""
1)API文档,接口的使用说吧
2)SDK,基于开发语言封装的可以直接调用的功能(工具)集合
官网sdk使用文档中找到安装命令:pip install qcloudsms_py
按照sdk使用说明进行开发:https://cloud.tencent.com/document/product/382/11672
"""
t_sms.py
# 所有配置换成申请的数据

# 申请的短信应用 SDK AppID
appid = 1400
# 申请的短信应用 SDK AppKey
appkey = "ba81"
# 申请的短信模板ID,需要在短信控制台中申请
template_id = 5447
# 申请的签名,参数使用的是`签名内容`,而不是`签名ID`
sms_sign = "Owen的技术栈" from qcloudsms_py import SmsSingleSender
sender = SmsSingleSender(appid, appkey) import random
def get_code():
code = ''
for i in range(4):
code += str(random.randint(0, 9))
return code mobile = 13344556677
# 模板所需参数,和申请的模板中占位符要保持一致
code = get_code()
print(code)
params = [code, 5]
try:
result = sender.send_with_param(86, mobile, template_id, params, sign=sms_sign, extend="", ext="")
if result and result.get('result') == 0:
print('发送成功')
except Exception as e:
print('短信发送失败:%s' % e)

短信服务二次封装

在libs下创建 tx_sms 包
init.py
from .sms import get_code, send_code
settings.py
# 申请的短信应用 SDK AppID
APP_ID = 1400 # 申请的短信应用 SDK AppKey
APP_KEY = "ba81" # 申请的短信模板ID,需要在短信控制台中申请
TEMPLATE_ID = 5447 # 申请的签名,参数使用的是`签名内容`,而不是`签名ID`
SIGN = "Owen的技术栈"
sms.py
import random
def get_code():
code = ''
for i in range(4):
code += str(random.randint(0, 9))
return code from qcloudsms_py import SmsSingleSender
from . import settings
from utils.logging import logger
sender = SmsSingleSender(settings.APP_ID, settings.APP_KEY)
def send_code(mobile, code, exp):
try:
result = sender.send_with_param(
86,
mobile,
settings.TEMPLATE_ID,
(code, exp),
sign=settings.SIGN,
extend="", ext=""
)
if result and result.get('result') == 0:
return True
logger.error('短信发送失败:%s' % result.get('errmsg'))
except Exception as e:
logger.critical('短信发送异常:%s' % e)
return False

5 短信验证码接口

后台

urls.py
path('sms/', views.SMSViewSet.as_view({'get': 'send'})),
throttles.py
from rest_framework.throttling import SimpleRateThrottle
from django.core.cache import cache
from django.conf import settings
# 结合手机验证码接口来书写
class SMSRateThrottle(SimpleRateThrottle):
scope = 'sms'
def get_cache_key(self, request, view):
# 手机号是通过get请求提交的
mobile = request.query_params.get('mobile', None)
if not mobile:
return None # 不限制 # 手机验证码发送失败,不限制,只有发送成功才限制,如果需求是发送失败也做频率限制,就注释下方三行
code = cache.get(settings.SMS_CACHE_KEY % {'mobile': mobile})
if not code:
return None return self.cache_format % {
'scope': self.scope,
'ident': mobile,
}
const.py
# 短信验证码缓存key
SMS_CACHE_KEY = 'sms_cache_%(mobile)s' # 短信验证码缓存时间s
SMS_CACHE_TIME = 300
dev.py
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'utils.exception.exception_handler',
'DEFAULT_THROTTLE_RATES': {
'sms': '1/min'
}
}
views.py
from libs import tx_sms
from django.core.cache import cache
from django.conf import settings
from .throttles import SMSRateThrottle
class SMSViewSet(ViewSet):
# 设置频率限制,一个手机号一分钟只能访问一次
throttle_classes = [SMSRateThrottle] def send(self, request, *args, **kwargs):
# return APIResponse(result=False)
# 1)接收前台手机号验证手机格式
mobile = request.query_params.get('mobile', None)
if not mobile:
return APIResponse(1, 'mobile field required')
if not re.match(r'^1[3-9][0-9]{9}$', mobile):
return APIResponse(1, 'mobile field error')
# 2)后台产生短信验证码
code = tx_sms.get_code()
# 3)把验证码交给第三方,发送短信
result = tx_sms.send_code(mobile, code, settings.SMS_CACHE_TIME // 60)
# 4)如果短信发送成功,服务器缓存验证码(内存数据库),方便下一次校验
if result:
cache.set(settings.SMS_CACHE_KEY % {'mobile': mobile}, code, settings.SMS_CACHE_TIME)
# 5)响应前台短信是否发生成功
return APIResponse(result=result)

6-短信登录接口

后台

urls.py
path('mobile/login/', views.MobileLoginViewSet.as_view({'post': 'login'})),
serializers.py
import re
from django.core.cache import cache
class MobileLoginSerializer(serializers.ModelSerializer):
# 覆盖
mobile = serializers.CharField(required=True, write_only=True)
# 自定义
code = serializers.CharField(min_length=4, max_length=4, required=True, write_only=True)
class Meta:
model = models.User
fields = ('id', 'username', 'icon', 'mobile', 'code')
extra_kwargs = {
'id': {
'read_only': True,
},
'username': {
'read_only': True,
},
'icon': {
'read_only': True,
}, } # 手机号格式校验(手机号是否存在校验规则自己考量)
def validate_mobile(self, value):
if not re.match(r'^1[3-9][0-9]{9}$', value):
raise exceptions.ValidationError('mobile field error')
return value def validate(self, attrs):
# 验证码校验 - 需要验证码与手机号两者参与
mobile = self._check_code(attrs)
# 多方式得到user
user = self._get_user(mobile)
# user签发token
token = self._get_token(user)
# token用context属性携带给视图类
self.context['token'] = token
# 将登录用户对象直接传给视图
self.context['user'] = user
return attrs def _check_code(self, attrs):
mobile = attrs.get('mobile')
code = attrs.pop('code')
old_code = cache.get(settings.SMS_CACHE_KEY % {'mobile': mobile})
if code != old_code:
raise exceptions.ValidationError({'code': 'double code error'})
else:
# 验证码的时效性:一旦验证码验证通过,代表改验证码已使用,需要立即失效
# cache.set(settings.SMS_CACHE_KEY % {'mobile': mobile}, '', -1)
pass
return mobile def _get_user(self, mobile):
try:
return models.User.objects.get(mobile=mobile)
except:
raise exceptions.ValidationError({'mobile': 'user not exist'}) def _get_token(self, user):
from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
return token
views.py
class MobileLoginViewSet(ViewSet):
# 局部禁用认证、权限组件
authentication_classes = ()
permission_classes = () def login(self, request, *args, **kwargs):
serializer = serializers.MobileLoginSerializer(data=request.data, context={'request': request})
if serializer.is_valid():
token = serializer.context.get('token')
# 拿到登录用户,直接走序列化过程,将要返回给前台的数据直接序列化好给前台
user = serializer.context.get('user')
# 返回给前台的数据结果:id,username,icon,token
result = serializers.MobileLoginSerializer(user, context={'request': request}).data
result['token'] = token return APIResponse(result=result)
return APIResponse(1, serializer.errors)

7-短信注册接口

后台

urls.py
router.register('register', views.RegisterViewSet, 'register')
serializers.py
class RegisterSerializer(serializers.ModelSerializer):
code = serializers.CharField(min_length=4, max_length=4, required=True, write_only=True)
class Meta:
model = models.User
fields = ('mobile', 'password', 'code')
extra_kwargs = {
'password': {
'min_length': 8,
'max_length': 16,
'write_only': True,
}
} def validate_mobile(self, value):
if not re.match(r'^1[3-9][0-9]{9}$', value):
raise exceptions.ValidationError('mobile field error')
return value def validate(self, attrs):
# 验证码校验 - 需要验证码与手机号两者参与
mobile = attrs.get('mobile')
code = attrs.pop('code')
old_code = cache.get(settings.SMS_CACHE_KEY % {'mobile': mobile})
if code != old_code:
raise exceptions.ValidationError({'code': 'double code error'})
else:
# 验证码的时效性:一旦验证码验证通过,代表改验证码已使用,需要立即失效
# cache.set(settings.SMS_CACHE_KEY % {'mobile': mobile}, '', -1)
pass # 数据入库必须需要唯一账号:1)前台注册必须提供账号 2)默认用手机号作为账号名,后期可以修改
attrs['username'] = mobile return attrs def create(self, validated_data): # 入库的数据:mobile,password,username
return models.User.objects.create_user(**validated_data)
views.py
# 手机验证码注册
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
class RegisterViewSet(GenericViewSet, mixins.CreateModelMixin):
queryset = models.User.objects.all()
serializer_class = serializers.RegisterSerializer def create(self, request, *args, **kwargs):
response = super().create(request, *args, **kwargs)
return APIResponse(result=response.data, http_status=response.status_code)

8-前台登录注册修订

前台

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" @click="login">登录</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 @click="mobile_login" 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;
// js正则:/正则语法/
// '字符串'.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({
url: this.$settings.base_url + '/user/mobile/',
method: 'post',
data: {
mobile: this.mobile
}
}).then(response => {
let result = response.data.result;
if (result) {
this.$message({
message: '账号正常',
type: 'success',
duration: 1000,
});
// 发生验证码按钮才可以被点击
this.is_send = true;
} else {
this.$message({
message: '账号不存在',
type: 'warning',
duration: 1000,
onClose: () => {
this.mobile = '';
}
})
}
}).catch(() => {
});
},
send_sms() {
// this.is_send必须允许发生验证码,才可以往下执行逻辑
if (!this.is_send) return;
// 按钮点一次立即禁用
this.is_send = false; let sms_interval_time = 60;
this.sms_interval = "发送中..."; // 定时器: setInterval(fn, time, args) // 往后台发送验证码
this.$axios({
url: this.$settings.base_url + '/user/sms/',
method: 'get',
params: {
mobile: this.mobile
}
}).then(response => {
let result = response.data.result;
if (result) { // 发送成功
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);
} else { // 发送失败
this.sms_interval = "重新获取";
this.is_send = true;
this.$message({
message: '短信发送失败',
type: 'warning',
duration: 3000
});
}
}).catch(() => {
this.sms_interval = "频率过快";
this.is_send = true;
}) },
login() {
if (!(this.username && this.password)) {
this.$message({
message: '请填好账号密码',
type: 'warning',
duration: 1500
});
return false // 直接结束逻辑
} this.$axios({
url: this.$settings.base_url + '/user/login/',
method: 'post',
data: {
username: this.username,
password: this.password,
}
}).then(response => {
let username = response.data.result.username;
let token = response.data.result.token;
let user_id = response.data.result.id;
this.$cookies.set('username', username, '7d');
this.$cookies.set('token', token, '7d');
this.$cookies.set('user_id', user_id, '7d');
this.$emit('success', response.data.result);
}).catch(error => {
console.log(error.response.data)
})
},
mobile_login () {
if (!(this.mobile && this.sms)) {
this.$message({
message: '请填好手机与验证码',
type: 'warning',
duration: 1500
});
return false // 直接结束逻辑
} this.$axios({
url: this.$settings.base_url + '/user/mobile/login/',
method: 'post',
data: {
mobile: this.mobile,
code: this.sms,
}
}).then(response => {
let username = response.data.result.username;
let token = response.data.result.token;
let user_id = response.data.result.id;
this.$cookies.set('username', username, '7d');
this.$cookies.set('token', token, '7d');
this.$cookies.set('user_id', user_id, '7d');
this.$emit('success', response.data.result);
}).catch(error => {
console.log(error.response.data)
})
}
}
}
</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>
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 @click="register" 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;
// js正则:/正则语法/
// '字符串'.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({
url: this.$settings.base_url + '/user/mobile/',
method: 'post',
data: {
mobile: this.mobile
}
}).then(response => {
let result = response.data.result;
if (!result) {
this.$message({
message: '欢迎注册我们的平台',
type: 'success',
duration: 1500,
});
// 发生验证码按钮才可以被点击
this.is_send = true;
} else {
this.$message({
message: '账号已存在,请直接登录',
type: 'warning',
duration: 1500,
})
}
}).catch(() => {});
},
send_sms() {
// this.is_send必须允许发生验证码,才可以往下执行逻辑
if (!this.is_send) return;
// 按钮点一次立即禁用
this.is_send = false; let sms_interval_time = 60;
this.sms_interval = "发送中..."; // 定时器: setInterval(fn, time, args) // 往后台发送验证码
this.$axios({
url: this.$settings.base_url + '/user/sms/',
method: 'get',
params: {
mobile: this.mobile
}
}).then(response => {
let result = response.data.result;
if (result) { // 发送成功
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);
} else { // 发送失败
this.sms_interval = "重新获取";
this.is_send = true;
this.$message({
message: '短信发送失败',
type: 'warning',
duration: 3000
});
}
}).catch(() => {
this.sms_interval = "频率过快";
this.is_send = true;
}) },
register () {
if (!(this.mobile && this.sms && this.password)) {
this.$message({
message: '请填好手机、密码与验证码',
type: 'warning',
duration: 1500
});
return false // 直接结束逻辑
} this.$axios({
url: this.$settings.base_url + '/user/register/',
method: 'post',
data: {
mobile: this.mobile,
code: this.sms,
password: this.password
}
}).then(response => {
this.$message({
message: '注册成功,3秒跳转登录页面',
type: 'success',
duration: 3000,
showClose: true,
onClose: () => {
// 去向成功页面
this.$emit('success')
}
});
}).catch(error => {
this.$message({
message: '注册失败,请重新注册',
type: 'warning',
duration: 1500,
showClose: true,
onClose: () => {
// 清空所有输入框
this.mobile = '';
this.password = '';
this.sms = '';
}
});
})
}
}
}
</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>
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 v-if="!token">
<span @click="put_login">登录</span>
<span class="line">|</span>
<span @click="put_register">注册</span>
</div>
<div v-else>
<span>{{ username }}</span>
<span class="line">|</span>
<span @click="logout">注销</span>
</div>
</div>
</div>
<Login v-if="is_login" @close="close_login" @go="put_register" @success="success_login" />
<Register v-if="is_register" @close="close_register" @go="put_login" @success="success_register" />
</div>
</template> <script>
import Login from './Login'
import Register from "./Register" export default {
name: "Header",
data() {
return {
url_path: sessionStorage.url_path || '/',
token: '',
username: '',
user_id: '',
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;
},
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;
},
success_login(data) {
this.is_login = false;
this.username = data.username;
this.token = data.token;
this.user_id = data.user_id;
},
logout() {
this.token = '';
this.username = '';
this.user_id = '';
this.$cookies.remove('username');
this.$cookies.remove('token');
this.$cookies.remove('user_id');
},
success_register () {
this.is_register = false;
this.is_login = true;
}
},
created() {
sessionStorage.url_path = this.$route.path;
this.url_path = this.$route.path; // 检测cookies,查看登录状态
this.username = this.$cookies.get('username');
this.token = this.$cookies.get('token');
this.user_id = this.$cookies.get('user_id');
},
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>

Django框架项目之登录注册——1-登录注册页面、2 多方式登录、3-手机是否存在验证接口、4-腾讯云短信开发、5 短信验证码接口、6-短信登录接口、7-短信注册接口、8-前台登录注册修订的更多相关文章

  1. django框架项目 国际化和本地化的实现方法

    转自 https://blog.csdn.net/scissors0707/article/details/79042458 Django国际化 所谓的国际化,是指使用不同语言的用户在访问同一个网站页 ...

  2. 使用Pycharm开发python下django框架项目生成的文件解释

    目录MyDjangoProject下表示工程的全局配置,分别为setttings.py.urls.py和wsgi.py,1.其中setttings.py包括了系统的数据库配置.应用配置和其他配置,2. ...

  3. 关于调接口和腾讯云cos方面。

    腾讯云的cos js jdk那个文档使用说明不好用. 都没看懂,而且图片上传也没有具体的详细.对于新手来说强制使用这个,弄得自己一头雾水. 工作效率就会下降. 为此我在网上搜了对象存储cos的常见错误 ...

  4. ThinkPHP5——接入腾讯云短信API

    接入腾讯云短信API相当简单,最重要的是第一次使用腾讯云短信的话有100条免费短信可以测试,对于开发人员是足够了,下面我就教大家接入腾讯云短信. 第一步:准备工作 首先要在腾讯云短信申请短信签名与短信 ...

  5. 为开源项目贡献代码-pycharm使用git-登录注册功能分析-判断手机号存在接口-多种方式登录接口-腾讯云短信申请

    目录 为开源项目贡献代码-pycharm使用git-登录注册功能分析-判断手机号存在接口-多种方式登录接口-腾讯云短信申请 git内容大回顾 今日内容概要 今日内容详细 1 为开源项目贡献代码 2 p ...

  6. Django商城项目笔记No.5用户部分-注册接口-短信验证码

    Django商城项目笔记No.4用户部分-注册接口-短信验证码 短信验证码也保存在redis里(sms_code_15101234567) 在views中新增SMSCodeView类视图,并且写出步骤 ...

  7. Django商城项目笔记No.6用户部分-注册接口-短信验证码实现celery异步

    Django商城项目笔记No.4用户部分-注册接口-短信验证码实现celery异步 接上一篇,如何解决前后端请求跨域问题? 首先想一下,为什么图片验证码请求的也是后端的api.meiduo.site: ...

  8. Django框架(十八)—— auth框架:用户登录、注册、认证

    目录 auth模块 一.什么是author模块 二.auth模块的使用 1.创建超级用户(create_superuser()) 2.验证用户(authenticate()) 3.登录用户(login ...

  9. django框架--登录注册功能(ajax)

    注册 实现一个注册功能 编写 html 内容 input 标签 csrf_token ajax 路由 视图: 提供页面 负责处理业务,返回响应 接收到   post   请求传递的参数 写库 返回   ...

  10. Django商城项目笔记No.8用户部分-注册接口实现

    Django商城项目笔记No.8用户部分-注册接口实现 users的view.py中增加如下代码 class RegisterUserView(CreateAPIView): "" ...

随机推荐

  1. Android 巧用ImageView属性实现选中和未选中效果

    原文地址: Android 巧用ImageView属性实现选中和未选中效果 - Stars-One的杂货小窝 选中和未选中状态变更是很常见需求,UI那边出的两个不同状态的图片,然后开发这边去实现 通常 ...

  2. 免费好用的录屏工具 —— EVCapture --九五小庞

    下载地址:https://wwfv.lanzoue.com/b022u08ib密码:acdu 1,简介 使用过很多种屏幕录像软件,最终这个软件留下来存到我的工具宝库. 因为界面非常简单友好,功能也很好 ...

  3. 今天做php缓存发现一个批量替换非法关键词的php代码

    <?php $badwords = array('aa', 'bb'); // 敏感词数组 $str = "aa阿斯顿克拉拉斯柯达bb阿萨达速度"; $result = fi ...

  4. spingmvc配置AOP 之 非注解方式

    spingmvc配置AOP有两种方式,一种是利用注解的方式配置,另一种是XML配置实现. 应用注解的方式配置: 先在maven中引入AOP用到的依赖 <dependency> <gr ...

  5. 4.4 x64dbg 绕过反调试保护机制

    在Windows平台下,应用程序为了保护自己不被调试器调试会通过各种方法限制进程调试自身,通常此类反调试技术会限制我们对其进行软件逆向与漏洞分析,下面是一些常见的反调试保护方法: IsDebugger ...

  6. 分享一个Java功能小案例(代码已开源哦)

    工程合集 工程列表 地址预览 B站 抖音

  7. jar包、war包项目部署

    部署 部署 jar包 部署 war包 部署 jar包 环境准备 JDK Tomcat Linux 环境 1.将jar文件上传至服务器 2.编写脚本 启动脚本放在跟jar 一起的路径下,如果不放在同一路 ...

  8. Linux chroot的关联操作 mount --bind

    假设新的根文件系统已经挂载到 /mnt mount --bind /dev /mnt/dev/ mount --bind /sys /mnt/sys/ mount --bind /proc /mnt/ ...

  9. yum与软件包

    从一个命令开始 yum provides iostat ## 找到软件包名为:sysstat-10.1.5-19.el7.x86_64 安装软件包 yum install sysstat-10.1.5 ...

  10. Linux 命令:time

    参考链接: time 命令