本系统需实现员工在线报名、管理员审核、培训资料分发与学习记录追踪。为保障档案信息安全,系统需具备严格的权限控制与操作审计。技术栈选择成熟、安全且易于维护的方案。
操作系统:Ubuntu Server 22.04 LTS。数据库:PostgreSQL 15,因其ACID特性与行级安全策略适合保密数据。后端:Python 3.10 + Django 4.2,内置强大的用户认证与安全中间件。前端:使用Bootstrap 5构建响应式界面,减少前端安全风险。
以下操作均在Ubuntu终端完成。
首先更新系统并安装基础依赖:
sudo apt update && sudo apt upgrade -y
sudo apt install -y python3-pip python3-venv postgresql postgresql-contrib nginx git
创建项目目录并设置虚拟环境:
mkdir ~/archive_training && cd ~/archive_training
python3 -m venv venv
source venv/bin/activate
安装Django及相关依赖:
pip install django==4.2.6 cryptography==41.0.7 psycopg2-binary==2.9.9 structlog==23.1.0 gunicorn==21.2.0
创建Django项目与应用:
django-admin startproject config .
python manage.py startapp training
登录PostgreSQL,创建数据库与专属用户:
sudo -u postgres psql
CREATE DATABASE archive_training_db;
CREATE USER training_user WITH PASSWORD 'YourStrongPassword123!';
ALTER ROLE training_user SET client_encoding TO 'utf8';
ALTER ROLE training_user SET default_transaction_isolation TO 'read committed';
ALTER ROLE training_user SET timezone TO 'UTC';
GRANT ALL PRIVILEGES ON DATABASE archive_training_db TO training_user;
\q
修改`config/settings.py`中的数据库配置部分:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'archive_training_db',
'USER': 'training_user',
'PASSWORD': 'YourStrongPassword123!',
'HOST': 'localhost',
'PORT': '5432',
}
}
将`'training'`应用添加到`INSTALLED_APPS`列表中。
在`training/models.py`中定义模型。模型设计需遵循最小权限原则,分离用户信息与培训记录。
from django.db import models
from django.contrib.auth.models import User
from cryptography.fernet import Fernet
import structlog
logger = structlog.get_logger()
class TrainingCourse(models.Model):
"""培训课程表"""
name = models.CharField(max_length=200, verbose_name="课程名称")
description = models.TextField(verbose_name="课程描述")
start_date = models.DateTimeField(verbose_name="开始时间")
capacity = models.IntegerField(default=50, verbose_name="报名限额")
is_active = models.BooleanField(default=True, verbose_name="是否开放报名")
confidential_material_path = models.CharField(max_length=500, blank=True, verbose_name="保密材料路径")
def __str__(self):
return self.name
class Enrollment(models.Model):
"""报名记录表,关联用户与课程"""
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='enrollments')
course = models.ForeignKey(TrainingCourse, on_delete=models.CASCADE, related_name='enrollments')
applied_at = models.DateTimeField(auto_now_add=True, verbose_name="申请时间")
STATUS_CHOICES = (
('pending', '待审核'),
('approved', '已批准'),
('rejected', '已拒绝'),
('completed', '已完成'),
)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending', verbose_name="状态")
加密字段示例:假设需要存储一个内部备注
_internal_notes_encrypted = models.TextField(blank=True, db_column='internal_notes')
使用一个固定的密钥(实际生产环境应从安全的环境变量获取)
_cipher_suite = Fernet.generate_key()
cipher_suite = Fernet(_cipher_suite)
@property
def internal_notes(self):
"""解密并返回备注"""
if self._internal_notes_encrypted:
try:
return self.cipher_suite.decrypt(self._internal_notes_encrypted.encode()).decode()
except Exception as e:
logger.error("decrypt_notes_failed", enrollment_id=self.id, error=str(e))
return ""
return ""
@internal_notes.setter
def internal_notes(self, value):
"""加密并存储备注"""
if value:
self._internal_notes_encrypted = self.cipher_suite.encrypt(value.encode()).decode()
else:
self._internal_notes_encrypted = ""
def save(self, args, kwargs):
"""重写save方法,记录操作日志"""
is_new = self.pk is None
super().save(args, kwargs)
action = 'created' if is_new else 'updated'
logger.info("enrollment_record_modified",
enrollment_id=self.id,
user_id=self.user.id,
course_id=self.course.id,
status=self.status,
action=action)
class Meta:
unique_together = ['user', 'course'] 防止重复报名
verbose_name = '报名记录'
verbose_name_plural = '报名记录'
生成并应用数据库迁移:
python manage.py makemigrations
python manage.py migrate
在`training/views.py`中创建核心视图。使用Django的基于类的视图和权限装饰器。

from django.contrib.auth.decorators import login_required, permission_required
from django.utils.decorators import method_decorator
from django.views.generic import ListView, CreateView, UpdateView
from django.urls import reverse_lazy
from django.shortcuts import get_object_or_404
from .models import TrainingCourse, Enrollment
import structlog
logger = structlog.get_logger()
@method_decorator(login_required, name='dispatch')
class CourseListView(ListView):
"""员工可见的课程列表"""
model = TrainingCourse
template_name = 'training/course_list.html'
context_object_name = 'courses'
def get_queryset(self):
只显示活跃的课程
return TrainingCourse.objects.filter(is_active=True)
@method_decorator(login_required, name='dispatch')
class EnrollmentCreateView(CreateView):
"""员工报名视图"""
model = Enrollment
fields = [] 自动关联用户和课程
template_name = 'training/enroll.html'
def form_valid(self, form):
course_id = self.kwargs.get('course_id')
course = get_object_or_404(TrainingCourse, id=course_id, is_active=True)
检查是否已报名
if Enrollment.objects.filter(user=self.request.user, course=course).exists():
form.add_error(None, '您已报名该课程')
return self.form_invalid(form)
检查名额
if course.enrollments.filter(status='approved').count() >= course.capacity:
form.add_error(None, '课程名额已满')
return self.form_invalid(form)
form.instance.user = self.request.user
form.instance.course = course
response = super().form_valid(form)
logger.info("enrollment_created", user_id=self.request.user.id, course_id=course_id)
return response
def get_success_url(self):
return reverse_lazy('course_list')
@method_decorator([login_required, permission_required('training.change_enrollment')], name='dispatch')
class EnrollmentReviewListView(ListView):
"""管理员审核列表"""
model = Enrollment
template_name = 'training/review_list.html'
context_object_name = 'pending_enrollments'
def get_queryset(self):
return Enrollment.objects.filter(status='pending').select_related('user', 'course')
创建对应的URL路由(`training/urls.py`):
from django.urls import path
from . import views
app_name = 'training'
urlpatterns = [
path('', views.CourseListView.as_view(), name='course_list'),
path('course//enroll/', views.EnrollmentCreateView.as_view(), name='enroll'),
path('admin/review/', views.EnrollmentReviewListView.as_view(), name='review_list'),
]
将其包含到主URL配置(`config/urls.py`)中:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('training/', include('training.urls')),
path('accounts/', include('django.contrib.auth.urls')), 启用认证视图
]
修改`config/settings.py`,添加以下关键安全设置:
从环境变量读取密钥,切勿硬编码
import os
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'your-development-secret-key-change-in-production')
DEBUG = os.environ.get('DJANGO_DEBUG', 'False') == 'True'
ALLOWED_HOSTS = ['your-server-ip', 'localhost', '127.0.0.1']
安全中间件与设置
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SESSION_COOKIE_SECURE = True 仅HTTPS
CSRF_COOKIE_SECURE = True 仅HTTPS
生产环境务必设置以下两项
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000 1年
日志配置
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'INFO',
'class': 'logging.FileHandler',
'filename': '/var/log/django/archive_training.log',
},
},
'loggers': {
'django': {
'handlers': ['file'],
'level': 'INFO',
'propagate': True,
},
},
}
创建日志目录并设置权限:
sudo mkdir -p /var/log/django
sudo chown -R $USER:$USER /var/log/django
首先测试Gunicorn能否运行应用:
cd ~/archive_training
gunicorn --bind 0.0.0.0:8000 config.wsgi:application
访问服务器IP:8000,确认应用运行正常后,创建Gunicorn系统服务文件:
sudo nano /etc/systemd/system/gunicorn_archive_training.service
写入以下内容:
[Unit]
Description=Gunicorn daemon for Archive Training
After=network.target
[Service]
User=your_username
Group=www-data
WorkingDirectory=/home/your_username/archive_training
Environment="PATH=/home/your_username/archive_training/venv/bin"
ExecStart=/home/your_username/archive_training/venv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/home/your_username/archive_training/config.sock config.wsgi:application
[Install]
WantedBy=multi-user.target
将文件中的`your_username`替换为你的实际用户名。然后启动并启用服务:
sudo systemctl start gunicorn_archive_training
sudo systemctl enable gunicorn_archive_training
sudo systemctl status gunicorn_archive_training 检查状态
配置Nginx作为反向代理。创建配置文件:
sudo nano /etc/nginx/sites-available/archive_training
写入以下配置:
server {
listen 80;
server_name your-server-ip; 或你的域名
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/your_username/archive_training;
}
location / {
include proxy_params;
proxy_pass http://unix:/home/your_username/archive_training/config.sock;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
再次替换`your_username`和`your-server-ip`。启用该站点并测试Nginx配置:
sudo ln -s /etc/nginx/sites-available/archive_training /etc/nginx/sites-enabled/
sudo nginx -t 测试配置语法
sudo systemctl restart nginx
收集静态文件:
python manage.py collectstatic
现在,通过浏览器访问你的服务器IP,即可看到部署完成的档案保密培训报名系统。
至此,一个具备基础安全防护、可投入使用的档案保密培训报名系统已部署完成。所有操作均基于开源组件,无需额外费用,后续可根据具体保密要求,进一步增加文件加密存储、动态口令等高级功能。