diff --git a/README.md b/README.md index e6442553c911423f2244a5c5f9e03de4272c4731..2b8d7f5ddaa074abc2e0f5eaad867acf87e0941e 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@
-
+
@@ -18,6 +18,7 @@
+
## 平台简介
RuoYi-Vue-FastAPI是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。
diff --git a/ruoyi-fastapi-backend/.env.dev b/ruoyi-fastapi-backend/.env.dev
index 6249b39f8240c8cd3cbf38c2e801722c5b6b4da2..e3916a844f6681d669f614cf8f8dfa3f8c696c2c 100644
--- a/ruoyi-fastapi-backend/.env.dev
+++ b/ruoyi-fastapi-backend/.env.dev
@@ -10,7 +10,7 @@ APP_HOST = '0.0.0.0'
# 应用端口
APP_PORT = 9099
# 应用版本
-APP_VERSION= '1.1.2'
+APP_VERSION= '1.1.3'
# 应用是否开启热重载
APP_RELOAD = true
# 应用是否开启IP归属区域查询
diff --git a/ruoyi-fastapi-backend/.env.prod b/ruoyi-fastapi-backend/.env.prod
index c9e201f14b2acb52a091fc197284f50743bba802..b9132b58be6b4a0fe36fe154c137cae1f2232766 100644
--- a/ruoyi-fastapi-backend/.env.prod
+++ b/ruoyi-fastapi-backend/.env.prod
@@ -10,7 +10,7 @@ APP_HOST = '0.0.0.0'
# 应用端口
APP_PORT = 9099
# 应用版本
-APP_VERSION= '1.1.2'
+APP_VERSION= '1.1.3'
# 应用是否开启热重载
APP_RELOAD = false
# 应用是否开启IP归属区域查询
diff --git a/ruoyi-fastapi-backend/exceptions/exception.py b/ruoyi-fastapi-backend/exceptions/exception.py
index ef2fda06fdc21e8ea79bf5969483e4a97e7018c5..28c39a673a1ba1d6010033a8924f8c1a447a6323 100644
--- a/ruoyi-fastapi-backend/exceptions/exception.py
+++ b/ruoyi-fastapi-backend/exceptions/exception.py
@@ -26,3 +26,13 @@ class PermissionException(Exception):
def __init__(self, data: str = None, message: str = None):
self.data = data
self.message = message
+
+
+class ModelValidatorException(Exception):
+ """
+ 自定义模型校验异常ModelValidatorException
+ """
+
+ def __init__(self, data: str = None, message: str = None):
+ self.data = data
+ self.message = message
diff --git a/ruoyi-fastapi-backend/exceptions/handle.py b/ruoyi-fastapi-backend/exceptions/handle.py
index 61b7f9009f07b85e14f95399ee2671527792790a..040a11fb71d3a151d85061ec0276624b555d4ab0 100644
--- a/ruoyi-fastapi-backend/exceptions/handle.py
+++ b/ruoyi-fastapi-backend/exceptions/handle.py
@@ -1,6 +1,6 @@
from fastapi import FastAPI, Request
from fastapi.exceptions import HTTPException
-from exceptions.exception import AuthException, PermissionException
+from exceptions.exception import AuthException, PermissionException, ModelValidatorException
from utils.response_util import ResponseUtil, JSONResponse, jsonable_encoder
@@ -18,6 +18,11 @@ def handle_exception(app: FastAPI):
async def permission_exception_handler(request: Request, exc: PermissionException):
return ResponseUtil.forbidden(data=exc.data, msg=exc.message)
+ # 自定义模型检验异常
+ @app.exception_handler(ModelValidatorException)
+ async def model_validator_exception_handler(request: Request, exc: ModelValidatorException):
+ return ResponseUtil.failure(data=exc.data, msg=exc.message)
+
# 处理其他http请求异常
@app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException):
diff --git a/ruoyi-fastapi-backend/module_admin/controller/user_controller.py b/ruoyi-fastapi-backend/module_admin/controller/user_controller.py
index 46ea0b82f9c708c8b535c67427978a4b5d25b42d..9385a6d960c9c9d061cadd93f37be220c2d61fdb 100644
--- a/ruoyi-fastapi-backend/module_admin/controller/user_controller.py
+++ b/ruoyi-fastapi-backend/module_admin/controller/user_controller.py
@@ -198,11 +198,20 @@ async def change_system_user_profile_avatar(request: Request, avatarfile: bytes
@log_decorator(title='个人信息', business_type=2)
async def change_system_user_profile_info(request: Request, user_info: UserInfoModel, query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)):
try:
- edit_user = EditUserModel(**user_info.model_dump(by_alias=True, exclude={'role_ids', 'post_ids'}), roleIds=user_info.role_ids.split(','), postIds=user_info.post_ids.split(','))
- edit_user.user_id = current_user.user.user_id
- edit_user.update_by = current_user.user.user_name
- edit_user.update_time = datetime.now()
- print(edit_user.model_dump())
+ edit_user = EditUserModel(
+ **user_info.model_dump(
+ exclude_unset=True,
+ by_alias=True,
+ exclude={'role_ids', 'post_ids'}
+ ),
+ userId=current_user.user.user_id,
+ userName=current_user.user.user_name,
+ updateBy=current_user.user.user_name,
+ updateTime=datetime.now(),
+ roleIds=current_user.user.role_ids.split(',') if current_user.user.role_ids else [],
+ postIds=current_user.user.post_ids.split(',') if current_user.user.post_ids else [],
+ role=current_user.user.role
+ )
edit_user_result = UserService.edit_user_services(query_db, edit_user)
if edit_user_result.is_success:
logger.info(edit_user_result.message)
@@ -217,12 +226,12 @@ async def change_system_user_profile_info(request: Request, user_info: UserInfoM
@userController.put("/profile/updatePwd")
@log_decorator(title='个人信息', business_type=2)
-async def reset_system_user_password(request: Request, old_password: str = Query(alias='oldPassword'), new_password: str = Query(alias='newPassword'), query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)):
+async def reset_system_user_password(request: Request, reset_password: ResetPasswordModel = Depends(ResetPasswordModel.as_query), query_db: Session = Depends(get_db), current_user: CurrentUserModel = Depends(LoginService.get_current_user)):
try:
reset_user = ResetUserModel(
userId=current_user.user.user_id,
- oldPassword=old_password,
- password=PwdUtil.get_password_hash(new_password),
+ oldPassword=reset_password.old_password,
+ password=PwdUtil.get_password_hash(reset_password.new_password),
updateBy=current_user.user.user_name,
updateTime=datetime.now()
)
diff --git a/ruoyi-fastapi-backend/module_admin/dao/notice_dao.py b/ruoyi-fastapi-backend/module_admin/dao/notice_dao.py
index 2d3646f420496263489c7c29f1f16610e5c4073b..79f32a3016600551cd00829a01d81958fd7eb014 100644
--- a/ruoyi-fastapi-backend/module_admin/dao/notice_dao.py
+++ b/ruoyi-fastapi-backend/module_admin/dao/notice_dao.py
@@ -51,7 +51,7 @@ class NoticeDao:
"""
query = db.query(SysNotice) \
.filter(SysNotice.notice_title.like(f'%{query_object.notice_title}%') if query_object.notice_title else True,
- SysNotice.update_by.like(f'%{query_object.update_by}%') if query_object.update_by else True,
+ SysNotice.create_by.like(f'%{query_object.create_by}%') if query_object.create_by else True,
SysNotice.notice_type == query_object.notice_type if query_object.notice_type else True,
SysNotice.create_time.between(
datetime.combine(datetime.strptime(query_object.begin_time, '%Y-%m-%d'), time(00, 00, 00)),
diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/login_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/login_vo.py
index 09a595f2a7cbbaf257045debc372790010e2d0a0..13ad73ad5e5bae602116c9b262a204fb95b9e219 100644
--- a/ruoyi-fastapi-backend/module_admin/entity/vo/login_vo.py
+++ b/ruoyi-fastapi-backend/module_admin/entity/vo/login_vo.py
@@ -1,6 +1,8 @@
-from pydantic import BaseModel, ConfigDict
+import re
+from pydantic import BaseModel, ConfigDict, model_validator
from pydantic.alias_generators import to_camel
from typing import Optional
+from exceptions.exception import ModelValidatorException
class UserLogin(BaseModel):
@@ -23,6 +25,14 @@ class UserRegister(BaseModel):
code: Optional[str] = None
uuid: Optional[str] = None
+ @model_validator(mode='after')
+ def check_password(self) -> 'UserRegister':
+ pattern = r'''^[^<>"'|\\]+$'''
+ if self.password is None or re.match(pattern, self.password):
+ return self
+ else:
+ raise ModelValidatorException(message="密码不能包含非法字符:< > \" ' \\ |")
+
class Token(BaseModel):
access_token: str
diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/user_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/user_vo.py
index fbc39831eb2976d5e6796f0191850f539b1b7352..fa761ef5048da3482dfd9977ce2a61ea020398fa 100644
--- a/ruoyi-fastapi-backend/module_admin/entity/vo/user_vo.py
+++ b/ruoyi-fastapi-backend/module_admin/entity/vo/user_vo.py
@@ -1,3 +1,4 @@
+import re
from pydantic import BaseModel, ConfigDict, model_validator
from pydantic.alias_generators import to_camel
from typing import Union, Optional, List
@@ -6,6 +7,7 @@ from module_admin.entity.vo.role_vo import RoleModel
from module_admin.entity.vo.dept_vo import DeptModel
from module_admin.entity.vo.post_vo import PostModel
from module_admin.annotation.pydantic_annotation import as_query, as_form
+from exceptions.exception import ModelValidatorException
class TokenData(BaseModel):
@@ -42,6 +44,14 @@ class UserModel(BaseModel):
remark: Optional[str] = None
admin: Optional[bool] = False
+ @model_validator(mode='after')
+ def check_password(self) -> 'UserModel':
+ pattern = r'''^[^<>"'|\\]+$'''
+ if self.password is None or re.match(pattern, self.password):
+ return self
+ else:
+ raise ModelValidatorException(message="密码不能包含非法字符:< > \" ' \\ |")
+
@model_validator(mode='after')
def check_admin(self) -> 'UserModel':
if self.user_id == 1:
@@ -144,6 +154,25 @@ class EditUserModel(AddUserModel):
role: Optional[List] = []
+@as_query
+class ResetPasswordModel(BaseModel):
+ """
+ 重置密码模型
+ """
+ model_config = ConfigDict(alias_generator=to_camel)
+
+ old_password: Optional[str] = None
+ new_password: Optional[str] = None
+
+ @model_validator(mode='after')
+ def check_new_password(self) -> 'ResetPasswordModel':
+ pattern = r'''^[^<>"'|\\]+$'''
+ if self.new_password is None or re.match(pattern, self.new_password):
+ return self
+ else:
+ raise ModelValidatorException(message="密码不能包含非法字符:< > \" ' \\ |")
+
+
class ResetUserModel(UserModel):
"""
重置用户密码模型
diff --git a/ruoyi-fastapi-frontend/package.json b/ruoyi-fastapi-frontend/package.json
index 8b59cc2d8d853d023be9447ed12ff995f3c5cb16..437e611e0ab1965ac443e6dbea2fae4503cf501a 100644
--- a/ruoyi-fastapi-frontend/package.json
+++ b/ruoyi-fastapi-frontend/package.json
@@ -1,6 +1,6 @@
{
"name": "vfadmin",
- "version": "1.1.2",
+ "version": "1.1.3",
"description": "vfadmin管理系统",
"author": "insistence",
"license": "MIT",
@@ -71,7 +71,7 @@
"babel-plugin-dynamic-import-node": "2.3.3",
"babel-plugin-import": "^1.13.8",
"chalk": "4.1.0",
- "compression-webpack-plugin": "5.0.2",
+ "compression-webpack-plugin": "6.1.2",
"connect": "3.6.6",
"eslint": "7.15.0",
"eslint-plugin-vue": "7.2.0",
diff --git a/ruoyi-fastapi-frontend/src/views/register.vue b/ruoyi-fastapi-frontend/src/views/register.vue
index f660f39b80749f88ed39f6e2fc78de9594b12179..e0f8a3b8876814f930e6a7998f216cfee39d98fb 100644
--- a/ruoyi-fastapi-frontend/src/views/register.vue
+++ b/ruoyi-fastapi-frontend/src/views/register.vue
@@ -95,7 +95,8 @@ export default {
],
password: [
{ required: true, trigger: "blur", message: "请输入您的密码" },
- { min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' }
+ { min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' },
+ { pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" }
],
confirmPassword: [
{ required: true, trigger: "blur", message: "请再次输入您的密码" },
diff --git a/ruoyi-fastapi-frontend/src/views/system/user/index.vue b/ruoyi-fastapi-frontend/src/views/system/user/index.vue
index 582c1c53806e1d78d2d21903ffe21544fe1624b3..1666cf09c5248d0aa7fc71a94093e22d62ba070d 100644
--- a/ruoyi-fastapi-frontend/src/views/system/user/index.vue
+++ b/ruoyi-fastapi-frontend/src/views/system/user/index.vue
@@ -433,7 +433,8 @@ export default {
],
password: [
{ required: true, message: "用户密码不能为空", trigger: "blur" },
- { min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' }
+ { min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' },
+ { pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" }
],
email: [
{
@@ -591,7 +592,12 @@ export default {
cancelButtonText: "取消",
closeOnClickModal: false,
inputPattern: /^.{5,20}$/,
- inputErrorMessage: "用户密码长度必须介于 5 和 20 之间"
+ inputErrorMessage: "用户密码长度必须介于 5 和 20 之间",
+ inputValidator: (value) => {
+ if (/<|>|"|'|\||\\/.test(value)) {
+ return "不能包含非法字符:< > \" ' \\\ |"
+ }
+ },
}).then(({ value }) => {
resetUserPwd(row.userId, value).then(response => {
this.$modal.msgSuccess("修改成功,新密码是:" + value);
diff --git a/ruoyi-fastapi-frontend/src/views/system/user/profile/resetPwd.vue b/ruoyi-fastapi-frontend/src/views/system/user/profile/resetPwd.vue
index 64e8f8c4f4464a571a522b924422956a1718193e..f329e6e870dd8bc535bf4299befb4bf1accd25d0 100644
--- a/ruoyi-fastapi-frontend/src/views/system/user/profile/resetPwd.vue
+++ b/ruoyi-fastapi-frontend/src/views/system/user/profile/resetPwd.vue
@@ -41,7 +41,8 @@ export default {
],
newPassword: [
{ required: true, message: "新密码不能为空", trigger: "blur" },
- { min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" }
+ { min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" },
+ { pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" }
],
confirmPassword: [
{ required: true, message: "确认密码不能为空", trigger: "blur" },
diff --git a/ruoyi-fastapi-frontend/vue.config.js b/ruoyi-fastapi-frontend/vue.config.js
index bdd2b2458b8e1048b3df6b77305ad3a45f16d7cd..71066a1507e7e1afc6806a3666929671b6edec12 100644
--- a/ruoyi-fastapi-frontend/vue.config.js
+++ b/ruoyi-fastapi-frontend/vue.config.js
@@ -64,11 +64,13 @@ module.exports = {
plugins: [
// http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件
new CompressionPlugin({
- cache: false, // 不启用文件缓存
- test: /\.(js|css|html)?$/i, // 压缩文件格式
- filename: '[path].gz[query]', // 压缩后的文件名
- algorithm: 'gzip', // 使用gzip压缩
- minRatio: 0.8 // 压缩率小于1才会压缩
+ cache: false, // 不启用文件缓存
+ test: /\.(js|css|html|jpe?g|png|gif|svg)?$/i, // 压缩文件格式
+ filename: '[path][base].gz[query]', // 压缩后的文件名
+ algorithm: 'gzip', // 使用gzip压缩
+ // threshold: 10240, // 只有大于 10kb 的文件会被压缩
+ minRatio: 0.8, // 压缩比例,小于 80% 的文件不会被压缩
+ deleteOriginalAssets: false // 压缩后删除原文件
})
],
},