Django中如何隐藏仅由JavaScript触发显示的图片URL?
问题
我开发了一个Django应用,需求是用户点击指定id为PDDO的图片元素30次后,才将原图替换为另一张图片。当前实现通过img标签的data-over属性存储目标图片URL,但该URL可通过查看页面源码轻易获取。我尝试过如下JS方案但无效:
$("a[data-label='document.querySelector("#PDDO").dataset.over']").hide()
附当前核心实现代码:
var count = 0; document.addEventListener('DOMContentLoaded', function(){ document.querySelector("#PDDO").onclick = function(){ if(count > 30){ document.querySelector("#PDDO").src = document.querySelector("#PDDO").dataset.over }else{ count++; } }})
<img src="{% static 'database/images/original_image.png' %}" width="200", height="200", class="revolve" id="PDDO" data-over="{% static 'database/images/new_image.png' %}">
解决方案
方法1:后端存储计数并返回URL(最安全)
把点击计数逻辑移到后端,用Django的session存储用户点击次数,只有达标后才返回目标图片URL:
- 前端每次点击时发送AJAX请求到后端,更新计数
- 后端验证计数达标后,返回目标图片的静态URL
- 前端拿到URL后替换图片src
Django视图代码:
from django.http import JsonResponse from django.templatetags.static import static def update_click_count(request): if request.method != 'POST': return JsonResponse({'error': '无效请求'}, status=400) count = request.session.get('pddo_click_count', 0) count += 1 request.session['pddo_click_count'] = count response_data = {'count': count} if count >= 30: response_data['target_url'] = static('database/images/new_image.png') return JsonResponse(response_data)
前端JS代码:
document.addEventListener('DOMContentLoaded', function(){ const img = document.querySelector("#PDDO"); img.onclick = async function(){ const response = await fetch('/update-click-count/', { method: 'POST', headers: { 'X-CSRFToken': getCookie('csrftoken'), 'Content-Type': 'application/json' } }); const data = await response.json(); if (data.target_url) { img.src = data.target_url; } } }); // 获取CSRF Token的辅助函数 function getCookie(name) { let cookieValue = null; if (document.cookie && document.cookie !== '') { const cookies = document.cookie.split(';'); for (let i = 0; i < cookies.length; i++) { const cookie = cookies[i].trim(); if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; }
前端HTML(移除data-over属性):
<img src="{% static 'database/images/original_image.png' %}" width="200" height="200" class="revolve" id="PDDO">
方法2:前端加密存储URL(防普通用户)
如果不想依赖后端逻辑,可对目标URL进行简单加密后存在前端,计数达标再解密:
- 在Django模板中对目标URL做Base64加密
- 前端存储加密字符串,达标后解密替换src
自定义Django过滤器(在app的templatetags目录下创建base64.py):
from django import template import base64 register = template.Library() @register.filter def base64_encode(value): return base64.b64encode(value.encode('utf-8')).decode('utf-8')
前端HTML代码:
{% load static %} {% load base64 %} <img src="{% static 'database/images/original_image.png' %}" width="200" height="200" class="revolve" id="PDDO" data-over-encrypted="{% static 'database/images/new_image.png'|base64_encode %}">
前端JS代码:
var count = 0; document.addEventListener('DOMContentLoaded', function(){ const img = document.querySelector("#PDDO"); const encryptedUrl = img.dataset.overEncrypted; img.onclick = function(){ count++; if(count >= 30){ const decryptedUrl = atob(encryptedUrl); img.src = decryptedUrl; } } });
注意:此方法仅增加获取门槛,技术人员仍可通过JS代码解密,适合低安全要求场景。
方法3:后端直接返回图片内容(完全隐藏URL)
计数达标后,后端直接返回图片二进制内容,而非URL,彻底隐藏真实路径:
- 前端点击达标后,请求后端图片接口
- 后端验证计数后,返回图片文件
- 前端将img的src指向该接口
Django视图代码:
from django.http import HttpResponse from django.templatetags.static import static import os def get_target_image(request): count = request.session.get('pddo_click_count', 0) # 根据计数返回对应图片 if count >= 30: img_path = static('database/images/new_image.png').lstrip('/') else: img_path = static('database/images/original_image.png').lstrip('/') with open(img_path, 'rb') as f: return HttpResponse(f.read(), content_type='image/png')
前端JS代码(复用方法1的update_click_count接口):
document.addEventListener('DOMContentLoaded', function(){ const img = document.querySelector("#PDDO"); img.onclick = async function(){ const response = await fetch('/update-click-count/', { method: 'POST', headers: { 'X-CSRFToken': getCookie('csrftoken'), 'Content-Type': 'application/json' } }); const data = await response.json(); if (data.count >= 30) { img.src = '/get-target-image/'; } } }); // 复用getCookie函数 function getCookie(name) { let cookieValue = null; if (document.cookie && document.cookie !== '') { const cookies = document.cookie.split(';'); for (let i = 0; i < cookies.length; i++) { const cookie = cookies[i].trim(); if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; }
内容的提问来源于stack exchange,提问作者Rivered
相关产品推荐
相关产品推荐

