网站安全性优化与bug修复

master
Sara 1 year ago
parent 8aa3390d90
commit 3fc2d8d165

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

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

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

@ -105,7 +105,6 @@
data() { data() {
return { return {
id: this.$route.query.id, id: this.$route.query.id,
token: "",
article: { article: {
articleTitle: "", articleTitle: "",
articleContent: "", articleContent: "",
@ -164,7 +163,6 @@
created() { created() {
this.getSortAndLabel(); this.getSortAndLabel();
this.getUpToken();
}, },
mounted() { mounted() {
@ -172,45 +170,35 @@
}, },
methods: { 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) { imgAdd(pos, file) {
if (this.$common.isEmpty(this.token)) {
this.$message({
message: "上传出错!",
type: "warning"
});
return;
}
let suffix = ""; let suffix = "";
if (file.name.lastIndexOf('.') !== -1) { if (file.name.lastIndexOf('.') !== -1) {
suffix = file.name.substring(file.name.lastIndexOf('.')); 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(); let fd = new FormData();
fd.append("file", file); fd.append("file", file);
fd.append("token", this.token); fd.append("key", key);
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);
this.$http.uploadQiniu(this.$constant.qiniuUrl, fd) this.$http.get(this.$constant.baseURL + "/qiniu/getUpToken", {key: key}, true)
.then((res) => { .then((res) => {
if (!this.$common.isEmpty(res.key)) { if (!this.$common.isEmpty(res.data)) {
let url = this.$constant.qiniuDownload + res.key; fd.append("token", res.data);
this.$common.saveResource(this, "articlePicture", url, file.size, file.type, true);
this.$refs.md.$img2Url(pos, url); this.$http.uploadQiniu(this.$constant.qiniuUrl, fd)
.then((res) => {
if (!this.$common.isEmpty(res.key)) {
let url = this.$constant.qiniuDownload + res.key;
this.$common.saveResource(this, "articlePicture", url, file.size, file.type, true);
this.$refs.md.$img2Url(pos, url);
}
})
.catch((error) => {
this.$message({
message: error.message,
type: "error"
});
});
} }
}) })
.catch((error) => { .catch((error) => {

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

@ -85,7 +85,6 @@
}, },
data() { data() {
return { return {
token: "",
context: {}, context: {},
canvasMoveUse: false, canvasMoveUse: false,
// - // -
@ -144,25 +143,8 @@
this.setCanvasStyle(); this.setCanvasStyle();
}, },
created() { created() {
if (!this.$common.isEmpty(this.$store.state.currentUser)) {
this.getUpToken();
}
}, },
methods: { 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) { canvasOutMove(e) {
const canvas = document.querySelector("#canvas"); const canvas = document.querySelector("#canvas");
if (e.target !== canvas) { if (e.target !== canvas) {
@ -264,14 +246,6 @@
return; return;
} }
if (this.$common.isEmpty(this.token)) {
this.$message({
message: "上传出错!",
type: "warning"
});
return;
}
if (this.preDrawAry.length < 1) { if (this.preDrawAry.length < 1) {
this.$message({ this.$message({
message: "你还没画呢~", message: "你还没画呢~",
@ -291,19 +265,32 @@
u8arr[n] = str.charCodeAt(n); u8arr[n] = str.charCodeAt(n);
} }
let obj = new Blob([u8arr], {type: mine}); 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(); let fd = new FormData();
fd.append("file", obj); fd.append("file", obj);
fd.append("token", this.token); fd.append("key", key);
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");
this.$http.uploadQiniu(this.$constant.qiniuUrl, fd) this.$http.get(this.$constant.baseURL + "/qiniu/getUpToken", {key: key})
.then((res) => { .then((res) => {
if (!this.$common.isEmpty(res.key)) { if (!this.$common.isEmpty(res.data)) {
this.clearContext(); fd.append("token", res.data);
let url = this.$constant.qiniuDownload + res.key;
this.$common.saveResource(this, "graffiti", url, obj.size, obj.type); this.$http.uploadQiniu(this.$constant.qiniuUrl, fd)
let img = "<你画我猜," + url + ">"; .then((res) => {
this.$emit("addGraffitiComment", img); if (!this.$common.isEmpty(res.key)) {
this.clearContext();
let url = this.$constant.qiniuDownload + res.key;
this.$common.saveResource(this, "graffiti", url, obj.size, obj.type);
let img = "<你画我猜," + url + ">";
this.$emit("addGraffitiComment", img);
}
})
.catch((error) => {
this.$message({
message: error.message,
type: "error"
});
});
} }
}) })
.catch((error) => { .catch((error) => {

@ -6,11 +6,12 @@
multiple multiple
drag drag
:action="$constant.qiniuUrl" :action="$constant.qiniuUrl"
:data="qiniuParam"
:on-change="handleChange" :on-change="handleChange"
:before-upload="beforeUpload" :before-upload="beforeUpload"
:on-success="handleSuccess" :on-success="handleSuccess"
:on-error="handleError" :on-error="handleError"
:on-remove="handleRemove"
:http-request="customUpload"
:list-type="listType" :list-type="listType"
:accept="accept" :accept="accept"
:limit="maxNumber" :limit="maxNumber"
@ -48,6 +49,8 @@
</template> </template>
<script> <script>
import upload from '../../utils/ajaxUpload';
export default { export default {
props: { props: {
isAdmin: { isAdmin: {
@ -77,12 +80,7 @@
}, },
data() { data() {
return { return {}
qiniuParam: {
token: "",
key: ""
}
}
}, },
computed: {}, computed: {},
@ -90,9 +88,6 @@
watch: {}, watch: {},
created() { created() {
if ((!this.isAdmin && !this.$common.isEmpty(this.$store.state.currentUser)) || (this.isAdmin && !this.$common.isEmpty(this.$store.state.currentAdmin))) {
this.getUpToken();
}
}, },
mounted() { mounted() {
@ -100,64 +95,66 @@
}, },
methods: { 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() { submitUpload() {
this.$refs.upload.submit(); 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) { handleSuccess(response, file, fileList) {
this.qiniuParam.key = "";
let url = this.$constant.qiniuDownload + response.key; let url = this.$constant.qiniuDownload + response.key;
this.$common.saveResource(this, this.prefix, url, file.size, file.raw.type, this.isAdmin); this.$common.saveResource(this, this.prefix, url, file.size, file.raw.type, this.isAdmin);
this.$emit("addPicture", url); this.$emit("addPicture", url);
}, },
handleError(err, file, fileList) { handleError(err, file, fileList) {
this.qiniuParam.key = "";
this.$message({ this.$message({
message: "上传出错!", message: err,
type: "warning" type: "error"
}); });
}, },
// false Promise reject // false Promise reject
beforeUpload(file) { beforeUpload(file) {
if (this.$common.isEmpty(this.qiniuParam.token)) { },
this.$message({ //
message: "上传出错!", handleRemove(file, fileList) {
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;
}, },
// //
handleChange(file, fileList) { handleChange(file, fileList) {
let flag = false; let flag = false;
// if (!/image\/\w+/.test(file.type)) {
// this.$message({
// message: "",
// type: "warning"
// });
// flag = true;
// }
if (file.size > this.maxSize * 1024 * 1024) { if (file.size > this.maxSize * 1024 * 1024) {
this.$message({ this.$message({

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

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

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

Loading…
Cancel
Save