基于Django的多租户项目实现用户自定义域名访问公知文章方案咨询
实现企业自定义域名访问公开文章的方案
一、核心思路
通过自定义域名与企业ID绑定+服务端请求识别+域名所有权验证的流程,将企业自定义域名的请求映射到对应企业的公开文章页面,同时适配Heroku+Cloudflare的部署环境。
二、Django后端数据模型调整
给企业模型新增与自定义域名相关的字段,用于存储域名信息和验证状态:
from django.db import models import secrets class Company(models.Model): name = models.CharField(max_length=255) # 原有字段(如slug、成员信息等)... custom_domain = models.CharField(max_length=255, unique=True, blank=True, null=True) domain_verified = models.BooleanField(default=False) # 标记域名是否验证通过 verification_token = models.CharField(max_length=64, default=secrets.token_urlsafe) # 验证令牌
三、域名所有权验证流程
- 企业用户提交自定义域名后,后端自动生成唯一
verification_token - 提供两种验证方式供用户选择:
- DNS TXT记录验证:要求用户在Cloudflare域名解析中添加TXT记录,内容为
knowledgebase-verification=<verification_token> - HTML文件验证:要求用户在自定义域名根目录上传
knowledgebase-verify.html,文件内容包含verification_token
- DNS TXT记录验证:要求用户在Cloudflare域名解析中添加TXT记录,内容为
- 后端实现验证接口,支持手动/定时触发验证:
- DNS验证:用
dns.resolver库查询目标域名的TXT记录,匹配令牌 - HTML验证:发送GET请求到
http://<custom_domain>/knowledgebase-verify.html,检查返回内容是否包含令牌
- DNS验证:用
- 验证通过后,将
domain_verified设为True
四、后端请求路由处理(DRF)
1. 自定义中间件识别域名
添加中间件获取请求的Host头,匹配已验证的企业自定义域名,将企业ID注入请求:
from django.http import Http404 from .models import Company class CustomDomainMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): host = request.get_host().split(':')[0] # 去除端口号 try: company = Company.objects.get(custom_domain=host, domain_verified=True) request.company_id = company.id # 将企业ID存入请求对象 except Company.DoesNotExist: pass # 非自定义域名,走原有路由逻辑 response = self.get_response(request) return response
在settings.py中注册该中间件:
MIDDLEWARE = [ # 其他中间件(如CommonMiddleware)... 'your_app.middleware.CustomDomainMiddleware', ]
2. 公开文章视图适配
修改原有公开文章视图,优先使用请求中的company_id返回对应内容:
from rest_framework.views import APIView from rest_framework.response import Response from .models import Article from .serializers import ArticleSerializer class PublicArticlesView(APIView): def get(self, request, company_slug=None): # 优先使用自定义域名对应的企业ID company_id = getattr(request, 'company_id', None) if company_id: articles = Article.objects.filter(company_id=company_id, is_public=True) else: # 原有路由逻辑:通过slug获取企业 company = Company.objects.get(slug=company_slug) articles = Article.objects.filter(company=company, is_public=True) serializer = ArticleSerializer(articles, many=True) return Response(serializer.data)
五、React前端适配
1. 路由与页面渲染调整
在前端入口处先检测当前域名是否为企业自定义域名,再渲染对应页面:
async function checkCustomDomain() { try { const res = await fetch('/api/check-custom-domain/'); const data = await res.json(); if (data.is_custom_domain && data.company_id) { // 渲染公开文章列表组件 render(<PublicArticlesList companyId={data.company_id} />, document.getElementById('root')); } else { // 渲染原有主应用路由 render(<App />, document.getElementById('root')); } } catch (err) { render(<App />, document.getElementById('root')); } } checkCustomDomain();
注:后端需实现/api/check-custom-domain/接口,返回当前域名是否为自定义域名及对应企业ID。
2. 静态资源路径处理
确保所有静态资源使用绝对路径,或在package.json中配置homepage为动态获取的当前域名,避免自定义域名下资源加载错误。
六、Heroku+Cloudflare部署配置
1. Heroku配置
- 后端应用:在Heroku后台
Settings > Domains中添加允许的自定义域名(或使用通配符域名*.your-main-domain.com,需配合Cloudflare使用),要求用户将自定义域名的CNAME记录指向你的Heroku应用域名(如your-app.herokuapp.com) - 前端应用:若前后端分离部署,同样在前端Heroku应用中配置允许的自定义域名,或改用Cloudflare Pages部署前端,更灵活支持多域名映射
2. Cloudflare配置
- 引导企业用户将域名的DNS解析服务商切换为Cloudflare
- 用户添加CNAME记录,将自定义域名指向你的Heroku应用域名,并开启Proxy status(橙色云朵),由Cloudflare代理请求
- 可选:添加页面规则,将自定义域名的请求重写为对应企业的公开文章路由(如
company1.com/*重写为your-main-domain.com/articles/public/company-1/*),但这种方式需手动维护,不如后端中间件灵活
七、可选方案:拆分公开文章为独立项目
若原有项目耦合度高,可将公开文章模块拆分为独立的React+Django项目:
- 独立项目仅处理公开文章展示和自定义域名映射逻辑
- 原有项目专注于私有文章、用户管理等核心功能
- 数据通过API同步,或共享数据库(需严格控制数据权限)
- 部署时独立项目可直接绑定多个自定义域名,配置更简洁
内容的提问来源于stack exchange,提问作者Kevin Otieno
相关产品推荐
相关产品推荐

