【pure-admin】前端使用pure-admin后台管理系统框架,后端使用FastAPI的前端向后端加密发送用户登录密码的完整示例
在Pure-Admin前端框架和FastAPI后端之间如何加密传输用户登录密码。使用RSA非对称加密算法来确保密码安全。
实现方案概述
- 后端生成RSA密钥对,并提供接口返回公钥
- 前端获取公钥后,使用公钥加密用户密码
- 前端将加密后的密码发送到后端
- 后端使用私钥解密密码,进行验证
后端实现(FastAPI)
首先实现FastAPI后端,负责生成密钥对、提供公钥和处理登录请求:
```python
from fastapi import FastAPI, HTTPException, Depends
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
import rsa
import os
import json
import bcryptapp = FastAPI()# 允许跨域请求,适应前端开发
app.add_middleware(CORSMiddleware,allow_origins=["http://localhost:8080"], # Pure-Admin默认端口allow_credentials=True,allow_methods=["*"],allow_headers=["*"],
)# 生成并保存RSA密钥对(首次运行时)
def generate_rsa_keys():if not os.path.exists("private.pem") or not os.path.exists("public.pem"):(pubkey, privkey) = rsa.newkeys(2048)with open("public.pem", "wb") as f:f.write(pubkey.save_pkcs1())with open("private.pem", "wb") as f:f.write(privkey.save_pkcs1())# 加载密钥
def load_keys():with open("public.pem", "rb") as f:pubkey = rsa.PublicKey.load_pkcs1(f.read())with open("private.pem", "rb") as f:privkey = rsa.PrivateKey.load_pkcs1(f.read())return pubkey, privkey# 初始化密钥
generate_rsa_keys()
pubkey, privkey = load_keys()# 模拟数据库中的用户(实际项目中应使用数据库)
fake_db = {"admin": {# 密码是"password123"的bcrypt哈希值"password_hash": "$2b$12$9T4R3QJZJZJZJZJZJZJZOeUQZJZJZJZJZJZJZJZJZJZJZJZJZ"}
}# 请求模型
class LoginRequest(BaseModel):username: strpassword: str # 这里将是加密后的密码# 获取公钥接口
@app.get("/api/public-key", tags=["auth"])
async def get_public_key():with open("public.pem", "rb") as f:public_key = f.read().decode("utf-8")return {"public_key": public_key}# 登录接口
@app.post("/api/login", tags=["auth"])
async def login(login_data: LoginRequest):try:# 解密密码encrypted_password = login_data.password# 将加密的密码从base64字符串转换为字节encrypted_bytes = bytes.fromhex(encrypted_password)# 使用私钥解密decrypted_password = rsa.decrypt(encrypted_bytes, privkey).decode("utf-8")# 验证用户user = fake_db.get(login_data.username)if not user:raise HTTPException(status_code=401, detail="用户名或密码错误")# 验证密码(使用bcrypt)if not bcrypt.checkpw(decrypted_password.encode('utf-8'), user["password_hash"].encode('utf-8')):raise HTTPException(status_code=401, detail="用户名或密码错误")# 这里可以生成JWT令牌等return {"success": True,"message": "登录成功","token": "fake-jwt-token" # 实际项目中应生成真实的JWT令牌}except Exception as e:raise HTTPException(status_code=400, detail=f"登录失败: {str(e)}")# 启动服务器
if __name__ == "__main__":import uvicornuvicorn.run(app, host="0.0.0.0", port=8000)
前端实现(Pure-Admin)
接下来实现Pure-Admin前端部分,包括获取公钥、加密密码和发送登录请求:
<template><div class="login-container"><el-card class="login-card"><div slot="header" class="login-header"><h2>系统登录</h2></div><el-form ref="loginForm" :model="loginForm" :rules="loginRules" label-width="80px"><el-form-item label="用户名" prop="username"><el-input v-model="loginForm.username" placeholder="请输入用户名"prefix-icon="User"></el-input></el-form-item><el-form-item label="密码" prop="password"><el-input v-model="loginForm.password" type="password" placeholder="请输入密码"prefix-icon="Lock"></el-input></el-form-item><el-form-item><el-button type="primary" class="login-btn" @click="handleLogin":loading="loading">登录</el-button></el-form-item></el-form></el-card></div>
</template><script setup>
import { ref, reactive, onMounted } from 'vue';
import { ElMessage, ElForm } from 'element-plus';
import { useRouter } from 'vue-router';
import JSEncrypt from 'jsencrypt'; // 导入加密库
import { getPublicKey, userLogin } from '@/api/auth'; // 导入API// 登录表单数据
const loginForm = reactive({username: '',password: ''
});// 表单验证规则
const loginRules = {username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
};// 状态变量
const loading = ref(false);
const publicKey = ref('');
const loginFormRef = ref(null);
const router = useRouter();// 获取公钥
const fetchPublicKey = async () => {try {const response = await getPublicKey();publicKey.value = response.public_key;} catch (error) {ElMessage.error('获取加密公钥失败,请刷新页面重试');console.error('获取公钥失败:', error);}
};// 加密密码
const encryptPassword = (password) => {if (!publicKey.value) {ElMessage.warning('加密公钥尚未加载完成,请稍候');return null;}const encrypt = new JSEncrypt();encrypt.setPublicKey(publicKey.value);// 加密并转换为十六进制字符串(避免特殊字符问题)return encrypt.encrypt(password) ? encrypt.encrypt(password).toString('hex') : null;
};// 处理登录
const handleLogin = async () => {// 表单验证if (!loginFormRef.value) return;try {await loginFormRef.value.validate();} catch (error) {return;}// 加密密码const encryptedPwd = encryptPassword(loginForm.password);if (!encryptedPwd) {return;}// 发送登录请求loading.value = true;try {const response = await userLogin({username: loginForm.username,password: encryptedPwd // 发送加密后的密码});if (response.success) {ElMessage.success('登录成功');// 保存tokenlocalStorage.setItem('token', response.token);// 跳转到首页router.push('/');} else {ElMessage.error(response.message || '登录失败');}} catch (error) {ElMessage.error('登录请求失败,请稍后重试');console.error('登录错误:', error);} finally {loading.value = false;}
};// 页面加载时获取公钥
onMounted(() => {fetchPublicKey();
});
</script><style scoped>
.login-container {display: flex;justify-content: center;align-items: center;min-height: 100vh;background-color: #f5f7fa;
}.login-card {width: 400px;box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}.login-header {text-align: center;
}.login-btn {width: 100%;
}
</style>
实现说明
-
后端依赖安装:
pip install fastapi uvicorn rsa bcrypt python-multipart
-
前端依赖安装:
npm install jsencrypt
-
安全要点:
- 使用RSA非对称加密,公钥在前端加密,私钥仅在后端解密
- 密码在传输过程中始终是加密状态,不会暴露明文
- 后端存储的是密码的bcrypt哈希值,而非明文
- 结合了HTTPS会更加安全(示例中为了开发方便未包含)
-
工作流程:
- 前端页面加载时获取公钥
- 用户输入用户名和密码后,前端使用公钥加密密码
- 前端发送加密后的密码到后端
- 后端使用私钥解密密码,与数据库中的哈希值比对
- 验证成功后返回登录结果和令牌
这个示例可以直接集成到Pure-Admin和FastAPI项目中,确保用户密码在传输过程中的安全性。在实际生产环境中,还可以添加更多安全措施,如定期更换密钥对、添加验证码等。
ps:本文借助ai生成,仅供参考