开发一个支持加密访问文档的网页版应用程序涉及多个步骤,包括用户认证、文档加密和解密、文件上传和下载,以及确保整个过程中的数据安全性。下面是一个详细的步骤指南:
步骤1:设置项目环境
首先,设置一个新的项目环境。假设你使用的是Node.js和React.js进行前后端开发。
-
初始化项目:
shnpx create-react-app encrypted-docs-app cd encrypted-docs-app npm init -y
-
安装必要的依赖:
shnpm install express mongoose bcryptjs jsonwebtoken dotenv multer crypto-js npm install --save-dev nodemon
步骤2:设置后端(Node.js + Express)
-
创建服务器文件 :
在项目根目录下创建
server.js
文件。jsconst express = require('express'); const mongoose = require('mongoose'); const dotenv = require('dotenv'); const cors = require('cors'); const authRoutes = require('./routes/auth'); const docRoutes = require('./routes/docs'); dotenv.config(); const app = express(); app.use(cors()); app.use(express.json()); mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true }); app.use('/api/auth', authRoutes); app.use('/api/docs', docRoutes); const PORT = process.env.PORT || 5000; app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
-
用户认证 :
创建一个新的目录
routes
,并在其中创建auth.js
文件。jsconst express = require('express'); const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); const User = require('../models/User'); const router = express.Router(); // 注册 router.post('/register', async (req, res) => { const { username, password } = req.body; const salt = await bcrypt.genSalt(10); const hashedPassword = await bcrypt.hash(password, salt); const newUser = new User({ username, password: hashedPassword }); try { const savedUser = await newUser.save(); res.status(201).json(savedUser); } catch (err) { res.status(400).json(err); } }); // 登录 router.post('/login', async (req, res) => { const { username, password } = req.body; try { const user = await User.findOne({ username }); if (!user) return res.status(400).json({ message: "User not found" }); const isMatch = await bcrypt.compare(password, user.password); if (!isMatch) return res.status(400).json({ message: "Invalid credentials" }); const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '1h' }); res.status(200).json({ token }); } catch (err) { res.status(500).json(err); } }); module.exports = router;
-
文档上传和下载 :
在
routes
目录下创建docs.js
文件。jsconst express = require('express'); const multer = require('multer'); const jwt = require('jsonwebtoken'); const fs = require('fs'); const crypto = require('crypto'); const path = require('path'); const router = express.Router(); const storage = multer.diskStorage({ destination: (req, file, cb) => { cb(null, 'uploads/'); }, filename: (req, file, cb) => { cb(null, `${file.fieldname}-${Date.now()}${path.extname(file.originalname)}`); } }); const upload = multer({ storage }); // 中间件验证JWT const verifyToken = (req, res, next) => { const token = req.header('x-auth-token'); if (!token) return res.status(401).json({ message: 'No token, authorization denied' }); try { const decoded = jwt.verify(token, process.env.JWT_SECRET); req.user = decoded; next(); } catch (err) { res.status(400).json({ message: 'Token is not valid' }); } }; // 文件加密 const encryptFile = (filePath) => { const cipher = crypto.createCipher('aes-256-ctr', process.env.ENCRYPTION_SECRET); const input = fs.createReadStream(filePath); const output = fs.createWriteStream(`${filePath}.enc`); input.pipe(cipher).pipe(output); }; // 文件解密 const decryptFile = (filePath, res) => { const decipher = crypto.createDecipher('aes-256-ctr', process.env.ENCRYPTION_SECRET); const input = fs.createReadStream(filePath); const output = res; input.pipe(decipher).pipe(output); }; // 上传文档 router.post('/upload', verifyToken, upload.single('document'), (req, res) => { const filePath = req.file.path; encryptFile(filePath); fs.unlinkSync(filePath); // 删除原始文件,保留加密文件 res.status(200).json({ message: 'File uploaded and encrypted successfully' }); }); // 下载文档 router.get('/download/:filename', verifyToken, (req, res) => { const filePath = `uploads/${req.params.filename}`; decryptFile(filePath, res); }); module.exports = router;
-
创建用户模型 :
在
models
目录下创建User.js
文件。jsconst mongoose = require('mongoose'); const UserSchema = new mongoose.Schema({ username: { type: String, required: true, unique: true }, password: { type: String, required: true }, }); module.exports = mongoose.model('User', UserSchema);
步骤3:设置前端(React)
-
创建登录和注册页面 :
在
src
目录下创建components
目录,并在其中创建Login.js
和Register.js
文件。jsx// src/components/Register.js import React, { useState } from 'react'; import axios from 'axios'; const Register = () => { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const handleSubmit = async (e) => { e.preventDefault(); try { const res = await axios.post('http://localhost:5000/api/auth/register', { username, password }); console.log(res.data); } catch (err) { console.error(err); } }; return ( <form onSubmit={handleSubmit}> <input type="text" value={username} onChange={(e) => setUsername(e.target.value)} placeholder="Username" /> <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="Password" /> <button type="submit">Register</button> </form> ); }; export default Register;
jsx// src/components/Login.js import React, { useState } from 'react'; import axios from 'axios'; const Login = ({ setToken }) => { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const handleSubmit = async (e) => { e.preventDefault(); try { const res = await axios.post('http://localhost:5000/api/auth/login', { username, password }); setToken(res.data.token); } catch (err) { console.error(err); } }; return ( <form onSubmit={handleSubmit}> <input type="text" value={username} onChange={(e) => setUsername(e.target.value)} placeholder="Username" /> <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="Password" /> <button type="submit">Login</button> </form> ); }; export default Login;
-
创建文档上传和下载页面 :
在
src/components
目录下创建Upload.js
和Download.js
文件。jsx// src/components/Upload.js import React, { useState } from 'react'; import axios from 'axios'; const Upload = ({ token }) => { const [file, setFile] = useState(null); const handleFileChange = (e) => { setFile(e.target.files[0]); }; const handleSubmit = async (e) => { e.preventDefault(); const formData = new FormData(); formData.append('document', file); try { await axios.post('http://localhost:5000/api/docs/upload', formData, { headers: { 'Content-Type': 'multipart/form-data', 'x-auth-token': token } }); alert('File uploaded and encrypted successfully'); } catch (err) { console.error(err); } }; return ( <form onSubmit={handleSubmit}>
Upload ); };
export default Upload;
```jsx
// src/components/Download.js
import React, { useState } from 'react';
import axios from 'axios';
const Download = ({ token }) => {
const [filename, setFilename] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
try {
const res = await axios.get(`http://localhost:5000/api/docs/download/${filename}`, {
headers: {
'x-auth-token': token
},
responseType: 'blob'
});
const url = window.URL.createObjectURL(new Blob([res.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', filename);
document.body.appendChild(link);
link.click();
} catch (err) {
console.error(err);
}
};
return (
<form onSubmit={handleSubmit}>
<input type="text" value={filename} onChange={(e) => setFilename(e.target.value)} placeholder="Filename" />
<button type="submit">Download</button>
</form>
);
};
export default Download;
-
设置App组件和路由 :
修改
src/App.js
文件,整合上述组件并设置路由。jsx// src/App.js import React, { useState } from 'react'; import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; import Login from './components/Login'; import Register from './components/Register'; import Upload from './components/Upload'; import Download from './components/Download'; const App = () => { const [token, setToken] = useState(''); return ( <Router> <div> <Switch> <Route path="/register"> <Register /> </Route> <Route path="/login"> <Login setToken={setToken} /> </Route> <Route path="/upload"> <Upload token={token} /> </Route> <Route path="/download"> <Download token={token} /> </Route> <Route path="/"> <h1>Welcome to Encrypted Docs App</h1> </Route> </Switch> </div> </Router> ); }; export default App;
总结
通过上述步骤,你可以开发一个支持加密访问文档的网页版应用程序。该应用程序包括用户认证、文件上传与下载、文件加密与解密等功能,确保文档的安全性。