网站安全性优化与bug修复

master
Sara 1 year ago
parent 8aa3390d90
commit 3fc2d8d165

@ -48,6 +48,12 @@
- 优化:前端美化
- 优化个别Bug修复
### 2023年9月1日更新安全安全安全
- 优化:所有保存接口、邮件发送接口、文件上传接口都限制次数,防止恶意调用
- 优化修复vuex中用户信息丢失错乱的Bug
- 优化:文件上传模块改造,每次上传之前获取上传密钥,每个密钥只能上传一个文件
- 优化个别Bug修复
### 首页
![首页](首页.jpg)
@ -103,7 +109,7 @@ npm run build
一定要`Star`
## 欢迎进群
## 欢迎进群一定要Star
1. 交流(摸鱼)
2. 安装部署:互相帮助,争取每个人都零基础拥有自己的个人网站
3. 博客答疑:每段代码都是我自己写的,爱学习的小伙伴可以在这里提问,互相学习,互相进步

@ -17,11 +17,9 @@
"qs": "^6.10.3",
"vue": "^2.6.11",
"vue-baberrage": "^3.2.4",
"vue-ripple-directive": "^2.0.1",
"vue-router": "^3.2.0",
"vue-seamless-scroll": "^1.1.23",
"vuex": "^3.4.0",
"vuex-persistedstate": "^4.0.0"
"vuex": "^3.4.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",

@ -23,8 +23,6 @@
/* 主题悬停背景 */
--gradualRed: linear-gradient(to right, #ff4b2b, #ff416c);
/* 水波纹 */
--rippleColor: rgba(0, 0, 0, 0.5);
/* 导航栏字体 */
--toolbarFont: #333333;
/* 导航栏背景 */

@ -105,7 +105,6 @@
data() {
return {
id: this.$route.query.id,
token: "",
article: {
articleTitle: "",
articleContent: "",
@ -164,7 +163,6 @@
created() {
this.getSortAndLabel();
this.getUpToken();
},
mounted() {
@ -172,38 +170,20 @@
},
methods: {
getUpToken() {
this.$http.get(this.$constant.baseURL + "/qiniu/getUpToken", {}, true)
.then((res) => {
if (!this.$common.isEmpty(res.data)) {
this.token = res.data;
}
})
.catch((error) => {
this.$message({
message: error.message,
type: "error"
});
});
},
imgAdd(pos, file) {
if (this.$common.isEmpty(this.token)) {
this.$message({
message: "上传出错!",
type: "warning"
});
return;
}
let suffix = "";
if (file.name.lastIndexOf('.') !== -1) {
suffix = file.name.substring(file.name.lastIndexOf('.'));
}
let key = "articlePicture" + "/" + this.$store.state.currentAdmin.username.replace(/[^a-zA-Z]/g, '') + this.$store.state.currentAdmin.id + new Date().getTime() + Math.floor(Math.random() * 1000) + suffix;
let fd = new FormData();
fd.append("file", file);
fd.append("token", this.token);
fd.append("key", "articlePicture" + "/" + this.$store.state.currentAdmin.username.replace(/[^a-zA-Z]/g, '') + this.$store.state.currentAdmin.id + new Date().getTime() + Math.floor(Math.random() * 1000) + suffix);
fd.append("key", key);
this.$http.get(this.$constant.baseURL + "/qiniu/getUpToken", {key: key}, true)
.then((res) => {
if (!this.$common.isEmpty(res.data)) {
fd.append("token", res.data);
this.$http.uploadQiniu(this.$constant.qiniuUrl, fd)
.then((res) => {
@ -219,6 +199,14 @@
type: "error"
});
});
}
})
.catch((error) => {
this.$message({
message: error.message,
type: "error"
});
});
},
addArticleCover(res) {
this.article.articleCover = res;

@ -3,6 +3,7 @@
<div>
<div class="handle-box">
<el-select clearable v-model="pagination.resourceType" placeholder="资源类型" class="handle-select mrb10">
<el-option key="20" label="公共资源" value="assets"></el-option>
<el-option key="10" label="表情包" value="internetMeme"></el-option>
<el-option key="1" label="用户头像" value="userAvatar"></el-option>
<el-option key="2" label="文章封面" value="articleCover"></el-option>

@ -85,7 +85,6 @@
},
data() {
return {
token: "",
context: {},
canvasMoveUse: false,
// -
@ -144,25 +143,8 @@
this.setCanvasStyle();
},
created() {
if (!this.$common.isEmpty(this.$store.state.currentUser)) {
this.getUpToken();
}
},
methods: {
getUpToken() {
this.$http.get(this.$constant.baseURL + "/qiniu/getUpToken")
.then((res) => {
if (!this.$common.isEmpty(res.data)) {
this.token = res.data;
}
})
.catch((error) => {
this.$message({
message: error.message,
type: "error"
});
});
},
canvasOutMove(e) {
const canvas = document.querySelector("#canvas");
if (e.target !== canvas) {
@ -264,14 +246,6 @@
return;
}
if (this.$common.isEmpty(this.token)) {
this.$message({
message: "上传出错!",
type: "warning"
});
return;
}
if (this.preDrawAry.length < 1) {
this.$message({
message: "你还没画呢~",
@ -291,10 +265,15 @@
u8arr[n] = str.charCodeAt(n);
}
let obj = new Blob([u8arr], {type: mine});
let key = "graffiti" + "/" + this.$store.state.currentUser.username.replace(/[^a-zA-Z]/g, '') + this.$store.state.currentUser.id + new Date().getTime() + Math.floor(Math.random() * 1000) + ".png";
let fd = new FormData();
fd.append("file", obj);
fd.append("token", this.token);
fd.append("key", "graffiti" + "/" + this.$store.state.currentUser.username.replace(/[^a-zA-Z]/g, '') + this.$store.state.currentUser.id + new Date().getTime() + Math.floor(Math.random() * 1000) + ".png");
fd.append("key", key);
this.$http.get(this.$constant.baseURL + "/qiniu/getUpToken", {key: key})
.then((res) => {
if (!this.$common.isEmpty(res.data)) {
fd.append("token", res.data);
this.$http.uploadQiniu(this.$constant.qiniuUrl, fd)
.then((res) => {
@ -313,6 +292,14 @@
});
});
}
})
.catch((error) => {
this.$message({
message: error.message,
type: "error"
});
});
}
}
}
</script>

@ -6,11 +6,12 @@
multiple
drag
:action="$constant.qiniuUrl"
:data="qiniuParam"
:on-change="handleChange"
:before-upload="beforeUpload"
:on-success="handleSuccess"
:on-error="handleError"
:on-remove="handleRemove"
:http-request="customUpload"
:list-type="listType"
:accept="accept"
:limit="maxNumber"
@ -48,6 +49,8 @@
</template>
<script>
import upload from '../../utils/ajaxUpload';
export default {
props: {
isAdmin: {
@ -77,12 +80,7 @@
},
data() {
return {
qiniuParam: {
token: "",
key: ""
}
}
return {}
},
computed: {},
@ -90,9 +88,6 @@
watch: {},
created() {
if ((!this.isAdmin && !this.$common.isEmpty(this.$store.state.currentUser)) || (this.isAdmin && !this.$common.isEmpty(this.$store.state.currentAdmin))) {
this.getUpToken();
}
},
mounted() {
@ -100,64 +95,66 @@
},
methods: {
getUpToken() {
this.$http.get(this.$constant.baseURL + "/qiniu/getUpToken", {}, this.isAdmin)
.then((res) => {
if (!this.$common.isEmpty(res.data)) {
this.qiniuParam.token = res.data;
}
})
.catch((error) => {
this.$message({
message: error.message,
type: "error"
});
});
},
submitUpload() {
this.$refs.upload.submit();
},
customUpload(options) {
let suffix = "";
if (options.file.name.lastIndexOf('.') !== -1) {
suffix = options.file.name.substring(options.file.name.lastIndexOf('.'));
}
let key = this.prefix + "/" + (!this.$common.isEmpty(this.$store.state.currentUser.username) ? (this.$store.state.currentUser.username.replace(/[^a-zA-Z]/g, '') + this.$store.state.currentUser.id) : (this.$store.state.currentAdmin.username.replace(/[^a-zA-Z]/g, '') + this.$store.state.currentAdmin.id)) + new Date().getTime() + Math.floor(Math.random() * 1000) + suffix;
const xhr = new XMLHttpRequest();
xhr.open('get', this.$constant.baseURL + "/qiniu/getUpToken?key=" + key, false);
if (this.isAdmin) {
xhr.setRequestHeader("Authorization", localStorage.getItem("adminToken"));
} else {
xhr.setRequestHeader("Authorization", localStorage.getItem("userToken"));
}
try {
xhr.send();
const res = JSON.parse(xhr.responseText);
if (res !== null && res.hasOwnProperty("code") && res.code === 200) {
options.data = {
token: res.data,
key: key
};
return upload(options);
} else if (res !== null && res.hasOwnProperty("code") && res.code !== 200) {
return Promise.reject(res.message);
} else {
return Promise.reject("服务异常!");
}
} catch (e) {
return Promise.reject(e.message);
}
},
//
handleSuccess(response, file, fileList) {
this.qiniuParam.key = "";
let url = this.$constant.qiniuDownload + response.key;
this.$common.saveResource(this, this.prefix, url, file.size, file.raw.type, this.isAdmin);
this.$emit("addPicture", url);
},
handleError(err, file, fileList) {
this.qiniuParam.key = "";
this.$message({
message: "上传出错!",
type: "warning"
message: err,
type: "error"
});
},
// false Promise reject
beforeUpload(file) {
if (this.$common.isEmpty(this.qiniuParam.token)) {
this.$message({
message: "上传出错!",
type: "warning"
});
return false;
}
let suffix = "";
if (file.name.lastIndexOf('.') !== -1) {
suffix = file.name.substring(file.name.lastIndexOf('.'));
}
this.qiniuParam.key = this.prefix + "/" + (!this.$common.isEmpty(this.$store.state.currentUser.username) ? (this.$store.state.currentUser.username.replace(/[^a-zA-Z]/g, '') + this.$store.state.currentUser.id) : (this.$store.state.currentAdmin.username.replace(/[^a-zA-Z]/g, '') + this.$store.state.currentAdmin.id)) + new Date().getTime() + Math.floor(Math.random() * 1000) + suffix;
},
//
handleRemove(file, fileList) {
},
//
handleChange(file, fileList) {
let flag = false;
// if (!/image\/\w+/.test(file.type)) {
// this.$message({
// message: "",
// type: "warning"
// });
// flag = true;
// }
if (file.size > this.maxSize * 1024 * 1024) {
this.$message({

@ -18,12 +18,8 @@ import './assets/css/color.css'
import './assets/css/markdown-highlight.css'
import './assets/css/font-awesome.min.css'
import 'mavon-editor/dist/css/index.css'
//点击涟漪效果
import Ripple from 'vue-ripple-directive'
import {vueBaberrage} from 'vue-baberrage'
Ripple.color = 'var(--rippleColor)'
Vue.directive("ripple", Ripple)
import {vueBaberrage} from 'vue-baberrage'
Vue.use(ElementUI)
Vue.use(vueBaberrage)

@ -1,26 +1,15 @@
import Vue from 'vue'
import Vuex from 'vuex'
import createPersistedState from "vuex-persistedstate";
Vue.use(Vuex)
export default new Vuex.Store({
state: {
toolbar: {
visible: false,
enter: true
},
sortInfo: [],
currentUser: {},
currentAdmin: {},
webInfo: {
webName: "",
webTitle: [],
notices: [],
footer: "",
backgroundImage: "",
avatar: ""
}
toolbar: JSON.parse(localStorage.getItem("toolbar") || '{"visible": false, "enter": true}'),
sortInfo: JSON.parse(localStorage.getItem("sortInfo") || '[]'),
currentUser: JSON.parse(localStorage.getItem("currentUser") || '{}'),
currentAdmin: JSON.parse(localStorage.getItem("currentAdmin") || '{}'),
webInfo: JSON.parse(localStorage.getItem("webInfo") || '{"webName": "", "webTitle": [], "notices": [], "footer": "", "backgroundImage": "", "avatar": ""}')
},
getters: {
articleTotal: state => {
@ -51,29 +40,30 @@ export default new Vuex.Store({
mutations: {
changeToolbarStatus(state, toolbarState) {
state.toolbar = toolbarState;
localStorage.setItem("toolbar", JSON.stringify(toolbarState));
},
loadSortInfo(state, sortInfo) {
if (sortInfo !== null && sortInfo.length !== 0) {
state.sortInfo = sortInfo.sort((s1, s2) => s1.priority - s2.priority);
localStorage.setItem("sortInfo", JSON.stringify(sortInfo.sort((s1, s2) => s1.priority - s2.priority)));
}
},
loadCurrentUser(state, user) {
state.currentUser = user;
localStorage.setItem("currentUser", JSON.stringify(user));
},
loadCurrentAdmin(state, user) {
state.currentAdmin = user;
localStorage.setItem("currentAdmin", JSON.stringify(user));
},
loadWebInfo(state, webInfo) {
webInfo.webTitle = webInfo.webTitle.split('');
webInfo.notices = JSON.parse(webInfo.notices);
state.webInfo = webInfo;
localStorage.setItem("webInfo", JSON.stringify(webInfo));
}
},
actions: {},
modules: {},
plugins: [
createPersistedState({
storage: window.localStorage
})
]
plugins: []
})

@ -0,0 +1,77 @@
function getError(action, option, xhr) {
let msg;
if (xhr.response) {
msg = `${xhr.response.error || xhr.response}`;
} else if (xhr.responseText) {
msg = `${xhr.responseText}`;
} else {
msg = `fail to ${action} ${xhr.status}`;
}
return new Error(msg);
}
function getBody(xhr) {
const text = xhr.responseText || xhr.response;
if (!text) {
return text;
}
try {
return JSON.parse(text);
} catch (e) {
return text;
}
}
export default function upload(option) {
const xhr = new XMLHttpRequest();
const action = option.action;
if (xhr.upload) {
xhr.upload.onprogress = function progress(e) {
if (e.total > 0) {
e.percent = e.loaded / e.total * 100;
}
option.onProgress(e);
};
}
const formData = new FormData();
if (option.data) {
Object.keys(option.data).forEach(key => {
formData.append(key, option.data[key]);
});
}
formData.append(option.filename, option.file, option.file.name);
xhr.onerror = function error(e) {
option.onError(e);
};
xhr.onload = function onload() {
if (xhr.status < 200 || xhr.status >= 300) {
return option.onError(getError(action, option, xhr));
}
option.onSuccess(getBody(xhr));
};
xhr.open('post', action, true);
if (option.withCredentials && 'withCredentials' in xhr) {
xhr.withCredentials = true;
}
const headers = option.headers || {};
for (let item in headers) {
if (headers.hasOwnProperty(item) && headers[item] !== null) {
xhr.setRequestHeader(item, headers[item]);
}
}
xhr.send(formData);
return xhr;
}

@ -18,7 +18,7 @@ export default {
//前后端定义的密钥AES使用16位
cryptojs_key: "aoligeimeimaobin",
qiniuUrl: "https://upload.qiniup.com",
qiniuDownload: "$$$$七牛云下载地址",
qiniuDownload: "$$$$七牛云下载地址仿照【https://file.poetize.cn/】",
favoriteVideo: "$$$$自己找一个视频链接作为百宝箱的封面",

Loading…
Cancel
Save