You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django框架中实现CAC智能卡用户身份认证?

Django 框架下 CAC 智能卡认证落地实现方案

CAC 认证的核心是基于 TLS 双向认证(mTLS)实现,浏览器弹出证书选择提示的逻辑发生在 TLS 握手阶段,由前置 Web 服务器触发,Django 本身不直接处理 TLS 层交互,全流程实现分三层配置:

一、前置 Web 服务器配置(触发证书提交提示的核心)

这一步是实现访问时自动弹出CAC证书选择窗口的关键,必须用Nginx/Apache这类支持完整mTLS配置的Web服务器做反向代理,不要用Django自带的runserver承载生产流量,runserver不支持完整双向认证能力。

  • 提前准备好签发CAC证书的根CA、中间CA证书,合并为一个CA证书链文件cac_ca_bundle.crt,Web服务器将用这个文件校验客户端提交证书的合法性。
  • Nginx 参考配置:
server {
    listen 443 ssl;
    server_name your-service-domain.com;

    # 常规服务端HTTPS证书配置
    ssl_certificate /path/to/your/server_public.crt;
    ssl_certificate_key /path/to/your/server_private.key;

    # CAC双向认证核心配置
    ssl_client_certificate /path/to/cac_ca_bundle.crt;
    ssl_verify_depth 3; # CAC证书通常为2-3级链,深度设3足够覆盖
    ssl_verify_client optional; # 全局设为可选,仅在认证路径强制开启,避免影响普通页面访问

    # 将证书校验结果、证书内容透传给后端Django
    proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
    proxy_set_header X-SSL-Client-Cert $ssl_client_escaped_cert;
    proxy_set_header X-Forwarded-Proto $scheme;

    # 普通业务路径反向代理
    location / {
        proxy_pass http://127.0.0.1:8000; # Django服务监听地址
    }

    # CAC登录专属路径,强制要求客户端提交证书
    location /auth/cac-login {
        ssl_verify_client on;
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
        proxy_set_header X-SSL-Client-Cert $ssl_client_escaped_cert;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

配置生效后,用户访问/auth/cac-login路径时,浏览器会自动读取系统中存储的可用客户端证书(包括智能卡通过PKCS11驱动映射到系统的CAC证书),弹出选择提示,无需前端做额外开发。如果使用Apache,逻辑完全一致,核心是开启SSLVerifyClient require配置,将证书信息通过请求头透传给后端即可。

二、Python侧X509证书解析实现

不需要手动调用OpenSSL命令行解析证书,直接用cryptography库即可完成稳定的x509格式解析,比命令行调用性能更好、安全性更高。

  • 安装依赖:
pip install cryptography django
  • 在Django配置文件settings.py中添加安全配置,防止外部请求伪造证书头:
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
# *部署时必须配置防火墙限制Django端口仅允许本地Nginx访问*,避免外部直接请求伪造认证头
CAC_CA_CHAIN_PATH = "/path/to/cac_ca_bundle.crt"
  • 编写证书解析工具函数,可放在项目utils/cac.py路径下:
import urllib.parse
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.x509.oid import NameOID

def parse_cac_cert(request):
    # 先校验Web服务器返回的证书验证结果
    verify_status = request.META.get("HTTP_X_SSL_CLIENT_VERIFY")
    if verify_status != "SUCCESS":
        return None, "客户端证书未通过CA校验"
    # 读取透传的证书内容,解码为PEM格式
    cert_encoded = request.META.get("HTTP_X_SSL_CLIENT_CERT")
    if not cert_encoded:
        return None, "未获取到客户端提交的证书"
    try:
        cert_pem = urllib.parse.unquote(cert_encoded).encode()
        cert = x509.load_pem_x509_certificate(cert_pem, default_backend())
        # 提取CAC证书中的用户标识,CAC卡的用户唯一EDIPI号一般存在USER_ID字段
        user_info = {}
        for attr in cert.subject:
            if attr.oid == NameOID.COMMON_NAME:
                user_info["full_name"] = attr.value
            if attr.oid == NameOID.USER_ID:
                # USER_ID格式通常为 xxx.EDIPI@域名,提取纯EDIPI作为用户唯一键
                user_info["edipi"] = attr.value.split("@")[0].split(".")[-1]
        # 额外校验证书有效期
        user_info["expire_at"] = cert.not_valid_after
        return user_info, None
    except Exception as e:
        return None, f"证书解析失败: {str(e)}"

三、Django侧认证逻辑

编写CAC登录视图,完成证书校验后的用户登录逻辑:

from django.contrib.auth import login
from django.http import HttpResponseForbidden, HttpResponseRedirect
from .models import User # 替换为你项目的用户模型
from .utils.cac import parse_cac_cert

def cac_login(request):
    user_info, err = parse_cac_cert(request)
    if err:
        return HttpResponseForbidden(f"CAC认证失败:{err}")
    # 以EDIPI为唯一标识匹配本地用户,可根据业务需求调整匹配逻辑
    try:
        user = User.objects.get(edipi=user_info["edipi"])
    except User.DoesNotExist:
        # 首次登录自动创建用户的逻辑,可按需开启
        user = User.objects.create_user(
            username=user_info["edipi"],
            edipi=user_info["edipi"],
            last_name=user_info.get("full_name", "")
        )
    # 写入session完成登录
    login(request, user, backend="django.contrib.auth.backends.ModelBackend")
    return HttpResponseRedirect("/system/dashboard/")

在urls.py中添加路由映射到上述视图即可完成全流程对接。

四、落地注意事项

  • 服务端不需要集成pkcs11相关组件,pkcs11是客户端侧读取智能卡证书用的驱动,只需要在用户终端安装对应驱动(OpenSC、ActivClient等)确保浏览器能读取卡内证书即可,服务端仅需处理浏览器提交的标准x509证书。
  • 生产环境建议额外增加CRL/OCSP证书吊销校验逻辑,避免已挂失的CAC卡通过认证。
  • 本地调试阶段如果不想部署Nginx,可以用支持mTLS的ASGI/WSGI服务器临时调试,但不建议用于生产环境。

内容的提问来源于stack exchange,提问作者jpk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 12:27:25