目录:
-
- 🍉🍉🍉UPDATE:
- ✨✨✨前言
- ✨✨✨概述
- ✨✨✨技术栈
- ✨✨✨效果图
- ✨✨✨代码
- ✨✨✨github链接
🍉🍉🍉UPDATE:
突然发现喜提榜一,蟹蟹读者老爷们的支持(づ ̄ 3 ̄)づ
润色了一些格式,加了一些emoji,还配上了一些颜色,希望能在枯燥的技术学习中多一些色彩。
(看了学弟的博客之后发现这样读下去的欲望会大大增加,于是就借鉴了一些hh,在这里强烈推荐一下他的博客:
阳树阳树
是个很厉害,进步超快的博主呢!
✨✨✨前言
登录功能对于前端刚入门不久的我来说较为困难,花了很久查阅了许多资料。代码部分涉及的技术栈和细节也很多。
过了一段时间来看,发现对自己写的代码竟然感觉十分陌生,故想写篇博客,对整个过程进行梳理。方便自己重看代码,同时也希望能帮到在实现这一功能受阻的程序猿
✨✨✨概述
登录功能的实现大致可分成6步:
1.前端验证用户输入是否符合规范,并将账号密码用公钥进行加密
2. 前端调用后端登录接口,向后端发起登录请求
3. 后端收到请求,通过私钥解密后查验数据库中是否有相应账号以及密码是否正确
4.验证通过后,将成功信息连同token一起发送给前端
5.前端将token储存起来,每次用户进入需要登录才能访问的页面时或者每次进入网站时向后端发送token
6. 若过期,则清除用户信息,回到未登录状态
✨✨✨技术栈
-
前端:JavaScript,Vue
-
axios (发送请求的第三方库)
-
element-plus (基于 Vue 3 的组件库,简化UI代码)
-
-
后端:Node.js
- express (简化请求响应代码)
- cors (解决跨域问题)
- bcryptjs(密码加密解密)
- jsonwebtoken(实现token的生成与验证)
✨✨✨效果图
登陆前
登录界面
登录成功
✨✨✨代码
表单验证及发送请求
<template>
<el-form
ref="ruleFormRef"
:model="ruleForm"
:rules="rules"
label-width="35rem"
>
<el-form-item prop="phone_number" class="phone_number">
<el-input
type="text"
placeholder="你的手机号/邮箱"
v-model="ruleForm.phone_number"
/>
</el-form-item>
<el-form-item prop="password" class="password">
<el-input
type="password"
placeholder="密码"
v-model="ruleForm.password"
autocomplete="off"
/>
</el-form-item>
<el-form-item class="login_in_button">
<el-button type="primary" @click="submitForm(ruleFormRef)"
>登录</el-button
>
<el-button>注册</el-button>
</el-form-item>
</el-form>
</template>
<script lang="ts" setup>
import { reactive, ref } from "vue";
import type { FormInstance } from "element-plus";
import { ElMessage } from "element-plus";
import { LoginInPost } from "../../api/LoginIn";
import { useRouter } from "vue-router";
import axios from "axios";
import { useStore } from "../../../store.js";
import { storeToRefs } from "pinia";
const router = useRouter();
const store = useStore();
// const { islogin } = storeToRefs(store);
const formSize = ref("default");
const ruleFormRef = ref<FormInstance>();
const ruleForm = reactive({
phone_number: "",
password: "",
});
const checkPhonenumber = (rule: any, value: string, callback: any) => {
if (!value) {
callback(new Error("请输入注册时用的邮箱或者手机号呀"));
}
let re = /[0-9]*$/;
// console.log(value.length);
// ||value.length != 11
if (!re.test(value)) {
// console.log(re.test(value));
callback(new Error("输入格式有误哟"));
} else {
callback();
}
};
const checkPassword = (rule: any, value: string, callback: any) => {
if (!value) {
return callback(new Error("请输入密码呀"));
}
let re = /[0-9a-zA-Z]*$/;
if (!re.test(value)) {
// console.log(re.test(value));
return callback(new Error("输入格式有误哟"));
} else {
return callback();
}
};
const rules = reactive({
phone_number: [{ validator: checkPhonenumber, trigger: "blur" }],
password: [{ validator: checkPassword, trigger: "blur" }],
});
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
await formEl.validate((valid, fields) => {
if (valid) {
let url = "http://localhost:3007/api/user_register";
let dataObject = reactive({
phoneNumber: ruleForm.phone_number,
password: ruleForm.password,
});
try {
LoginInPost(dataObject).then(function (res) {
// console.log(res.data.token);
if (res.data.status == 0) {
store.loginIn();
localStorage.setItem("token",res.data.token);
router.go(-1);
} else if (res.data.status == 403) {
ElMessage({ message: res.data.message, offset: 200 });
}
});
} catch (error) {
console.log(error);
}
} else {
console.log("error submit!", fields);
}
});
};
</script>
axios封装
index.js
import db from "../db/user_db.js"
import bcrypt from "bcryptjs"
import jwt from "jsonwebtoken"
import config from "../config.js"
export async function regUser(req, res) {
try {
// console.log(req.body);
const data = req.body;
// console.log(data.phoneNumber);
const selectSql = "select * from users where phoneNumber=?";
let [result] = await db.query(selectSql, data.phoneNumber)
.catch(err => { console.log(err) });
// console.log(result.length);
//号码已占用
if (result.length > 0) {
throw new Error(res.send({
status: 403,
message: "电话号码已注册过了嗷",
}))
}
//对密码进行加密
// console.log(data.password);
data.password = bcrypt.hashSync(data.password, 10);
console.log(data.password);
const insertSql = "insert into users set ?";
const insertStr = { phoneNumber: data.phoneNumber, password: data.password };
[result] = await db.query(insertSql, insertStr)
.catch(err => { console.log(err) });
// console.log(result.affectedRows);
if (result.affectedRows != 1) {
res.send({
status: 403,
message: "请求失败",
})
}
res.send({
status: 0,
message: "请求成功",
})
} catch (err) {
res.send({
status: 403,
message: "请求失败",
desc: err.message
})
}
}
export async function loginUser(req, res) {
try {
// console.log(req.body);
const data = req.body;
// console.log(data.phoneNumber);
const selectSql = "select * from users where phoneNumber=?";
let [result] = await db.query(selectSql, data.phoneNumber)
.catch(err => { console.log(err) });
// console.log(result);
//无注册账号
if (result.length == 0) {
return res.send({
status: 403,
message: "无注册账号",
})
}
//验证密码
// console.log(result[0].password);
const compareResult = bcrypt.compareSync(data.password, result[0].password);
if (compareResult == 0) {
return res.send({
status: 403,
message: "密码错误",
})
}
const user = { ...result[0], password: '' };
console.log(user);
const tokenStr = jwt.sign(user, config.jwtSecretKey, { expiresIn: "10h" });
return res.send({
status: 0,
message: "登录成功",
token: tokenStr
})
} catch (err) {
return res.send({
status: 403,
message: "请求失败",
desc: err.message
})
}
}
export async function GetInfo(req, res) {
console.log(req.headers.authorization);
const token = req.headers.authorization;
if (!token) {
return res.send({
status: 403,
message: "无token"
})
}
try{
const flag = jwt.verify(token, config.jwtSecretKey);
if (flag) {
return res.send({
status: 0,
message: "验证成功"
})
} else {
return res.send({
status: 403,
message: "token错误"
})
}
}catch(err){
return res.send({
status: 403,
message: "token已过期"
})
}
}
request.js
// http/axios.js
import instance from "./index"
/**
* @param {String} method 请求的方法:get、post、delete、put
* @param {String} url 请求的url:
* @param {Object} data 请求的参数
* @param {Object} config 请求的配置
* @returns {Promise} 返回一个promise对象,其实就相当于axios请求数据的返回值
*/
export const axios = ({
method,
url,
data,
config
}) => {
method = method.toLowerCase();
if (method == 'post') {
return instance.post(url, data, {...config})
} else if (method == 'get') {
return instance.get(url, {
params: data,
...config
})
} else if (method == 'delete') {
return instance.delete(url, {
params: data,
...config
}, )
} else if (method == 'put') {
return instance.put(url, data,{...config})
} else {
console.error('未知的method' + method)
return false
}
}
Login.js
import { axios } from "../utils/request"
export const LoginInPost = (data) => {
return axios({
url: "http://localhost:3007/api/user_login",
method: "post",
data
})
}
export const GetUsersInfo = (data) => {
return axios({
url: "http://localhost:3007/api/get_info",
method: "get",
data
})
}
登录验证
export async function loginUser(req, res) {
try {
// console.log(req.body);
const data = req.body;
// console.log(data.phoneNumber);
const selectSql = "select * from users where phoneNumber=?";
let [result] = await db.query(selectSql, data.phoneNumber)
.catch(err => { console.log(err) });
// console.log(result);
//无注册账号
if (result.length == 0) {
return res.send({
status: 403,
message: "无注册账号",
})
}
//验证密码
// console.log(result[0].password);
const compareResult = bcrypt.compareSync(data.password, result[0].password);
if (compareResult == 0) {
return res.send({
status: 403,
message: "密码错误",
})
}
const user = { ...result[0], password: '' };
console.log(user);
const tokenStr = jwt.sign(user, config.jwtSecretKey, { expiresIn: "10h" });
return res.send({
status: 0,
message: "登录成功",
token: tokenStr
})
} catch (err) {
return res.send({
status: 403,
message: "请求失败",
desc: err.message
})
}
}
✨✨✨github链接
想要看完整版代码以及页面效果,请移步github哦
https://github.com/Ki-Wi-Berry/bilibili-videos
⛄码字不易,如果觉得对您有帮助的话,麻烦点个免费的赞~⛄