开发网页版加密访问文档

Source

开发一个支持加密访问文档的网页版应用程序涉及多个步骤,包括用户认证、文档加密和解密、文件上传和下载,以及确保整个过程中的数据安全性。下面是一个详细的步骤指南:

步骤1:设置项目环境

首先,设置一个新的项目环境。假设你使用的是Node.js和React.js进行前后端开发。

  1. 初始化项目

    npx create-react-app encrypted-docs-app
    cd encrypted-docs-app
    npm init -y
    
  2. 安装必要的依赖

    npm install express mongoose bcryptjs jsonwebtoken dotenv multer crypto-js
    npm install --save-dev nodemon
    

步骤2:设置后端(Node.js + Express)

  1. 创建服务器文件
    在项目根目录下创建server.js文件。

    const 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}`));
    
  2. 用户认证
    创建一个新的目录routes,并在其中创建auth.js文件。

    const 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;
    
  3. 文档上传和下载
    routes目录下创建docs.js文件。

    const 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;
    
  4. 创建用户模型
    models目录下创建User.js文件。

    const 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)

  1. 创建登录和注册页面
    src目录下创建components目录,并在其中创建Login.jsRegister.js文件。

    // 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;
    
    // 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;
    
  2. 创建文档上传和下载页面
    src/components目录下创建Upload.jsDownload.js文件。

    // 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;
  1. 设置App组件和路由
    修改src/App.js文件,整合上述组件并设置路由。

    // 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;
    

总结

通过上述步骤,你可以开发一个支持加密访问文档的网页版应用程序。该应用程序包括用户认证、文件上传与下载、文件加密与解密等功能,确保文档的安全性。