淘宝城购物中心,上海seo优化,视频网站的建设目标,四川住房和城乡建设九大员网站作者#xff1a;Hubery 时间#xff1a;2018.10.31 接上文#xff1a;接上文#xff1a;Django2 Web 实战02-用户注册登录退出 视频是一种可视化媒介#xff0c;因此视频数据库至少应该存储图像。让用户上传文件是个很大的隐患#xff0c;因此接下来会讨论这俩话题#…作者Hubery 时间2018.10.31 接上文接上文Django2 Web 实战02-用户注册登录退出 视频是一种可视化媒介因此视频数据库至少应该存储图像。让用户上传文件是个很大的隐患因此接下来会讨论这俩话题文件上传安全隐患。 新增一个文件上传函数让用户给movie上传图片检查OWASP列举的前10项安全隐患我们会检查文件上传的安全隐患。可以看下Django帮我们做了什么以及什么地方我们应该做出谨慎的决策。 1. 文件上传 这里我们会创建一个model展示和管理要上传到网站上的文件然后创建一个form和视图来验证和处理上传过程。 1.1 准备文件上传配置项 开始着手文件上传之前我们需要知道文件上传取决于一系列的设置且这些设置在开发环境和生产环境上是不同的。这些设置会影响文件的存储方式和访问方式。 Django有两套文件配置STATIC_* 和MEDIA_*。 Static文件是我们项目的一部分比如(CSS,JS)。 Media文件是用户上传到我们系统中的文件。Media文件不应被信任切不能执行。 我们将会在settings.py文件中设置这两个地方 MEDIA_URL /uploaded
MEDIA_ROOT os.path.join(BASE_DIR, ../media_root)
复制代码MEDIA_URL, 是用来给上传的文件服务的URL。 开发环境中这个值无关紧要同样不会与我们视图中的URL冲突。 生产环境中上传的文件应该给一个与我们工程中任何app不同的域URL同时还不能是子域。 用户的浏览器被欺骗执行它从同一域(或子域)中请求来的文件因为我们的app将信任该与我们用户cookie(包括session ID)相同的文件。 所有浏览器的默认策略是同源策略(Same Origin Policy)。 MEDIA_ROOT是Django保存代码目录的路径。 我们应该确保该目录不在我们的工程代码目录下这样就不会意外的将该目录加入版本控制范围或者意外的授予该目录文件一些特定的权限如执行。 在生产环境中还有其他的配置项需要配置如限制请求body等这些会在后续的部分讨论。 接下来创建media_root目录 命令行至与我们的项目最外层目录平级 mkdir media_root
ls
复制代码 1.2 创建MovieImage模型 MovieImage模型用一个新的字段ImageField来存储文件同时也会验证该文件是否是图片。尽管ImageField会验证该字段但仅仅靠阻止那些制造恶意文件的用户是不够的(但会帮助意外点击.zip文件的用户而不是.png的用户)。 Django用Pillow库来做验证所以先添加Pillow库到环境中 pip install Pillow
复制代码默认在命令行中直接pip install Pillow安装的是最新版本; 另外提供一种更优雅的命令行安装方式 touch requirements.dev.txt //创建文件
vi requirements.dev.txt // 编辑文件
// 输入版本号 Pillow4.4.0 然后保存
pip install -r requirements.dev.txt // 执行py库安装
复制代码接下来开始创建model core/models.py def movie_directory_path_with_uuid(instance, filename):return {}/{}.format(instance.movie_id, uuid4())class MovieImage(models.Model):image models.ImageField(upload_tomovie_directory_path_with_uuid)uploaded models.DateTimeField(auto_now_addTrue)movie models.ForeignKey(Movie, on_deletemodels.CASCADE)user models.ForeignKey(settings.AUTH_PASSWORD_VALIDATORS,on_deletemodels.CASCADE)
复制代码ImageField是FileField的一个特殊字段用Pillow来确认一个文件是否是图片。ImageField和FileField使用Django的文件存储API来工作(提供了一种读取文件的方式)同时可以进行文件的读写。 Django自带了FileSystemStorage实现了存储API将文件数据存储到本地文件系统上。这对开发来说足够了但后续我们会考虑替代方案。 我们用ImageField的upload_to参数来指定一个方法用来生成上传文件的名字。我们不希望用户可以在我们的系统中指定文件的名字因为他们可能会滥用一些用户信任的名字从而使我们难堪。鉴于此我们使用一个函数将指定的movie的所有图片存储在同一目录中同时用uuid4为每个文件生成一个通用的名字(这也避免了名字冲突和处理文件之间的相互覆盖问题)。 我们同时会记录是谁上传的文件这样如果我们发现一个坏的文件相当于提供了一种如何找到其他坏文件的线索。 模型创建完更新数据库 python manage.py makemigrations core
复制代码有了模型就可以创建其他部分如表单和视图。 1.3 创建和使用MovieImageForm MovieImageForm和之前的VoteForm相似它会隐藏和禁用模型所需的movie和user字段这很难取得客户的信任。 编辑core/forms.py # 添加文件上传form
class MovieImageForm(forms.ModelForm):movie forms.ModelChoiceField(widgetforms.HiddenInput,querysetMovie.objects.all(),disabledTrue,)user forms.ModelChoiceField(widgetforms.HiddenInput,querysetget_user_model().objects.all(),disabledTrue,)class Meta:model MovieImagefields (image, user, movie)
复制代码表单ModelForm中我们没有重写MovieImage的image字段因为ModelForm会自动提供一正确的文件选择框input typefile。 现在我们在视图MovieDetail中使用这个表单, core/views.py # movie详情 视图
class MovieDetail(DetailView):queryset Movie.objects.all_with_related_persons_and_score()def get_context_data(self, **kwargs):ctx super().get_context_data(**kwargs)# 配置图片上传表单ctx[image_form] self.movie_image_form()# 其他 略# 添加图片上传表单def movie_image_form(self):if self.request.user.is_authenticated:return MovieImageForm()return None
复制代码这里的上传代码比较简单只能上传新图片没有其他操作一只提供一个空表单。然而通过这种方式我们不能显示错误信息。实践中丢失error信息不是很好的做法。 1.4 更新模版movie_detail.html显示和上传图片 我们需要对movie_detail.html模版进行两次更新。 需要更新main模版的block新增一个图片列表。需要更新sidebar模版的block包含我们新建的上传表单。编辑core/templates/core/movie_detail.html {% extends base.html %}{% block title %}{{ object.title }} - {{ block.super }}
{% endblock %}{% block main %}h1{{ object }}/h1p classlead{{ object.plot }}/p{# 展示电影图片列表 #}div classcolh1{{ object }}/h1p classlead {{ object.plot }}/p/divul{% for i in object.movieimage_set.all %}li classlist-inline-itemimg src{{ i.image.url }}/li{% endfor %}/ulp由 {{ object.director }} 执导。/p
{% endblock %}{% block sidebar %}{# 电影排名部分 #}div这个电影排名span classbadge badge-primary{{ object.get_rating_display }}/span/divdivh2该片得分{{ object.score|default_if_none:TBD-暂无得分 }}/h2/div{# 文件上传部分 #}{% if image_form %}divh2上传新图片/h2form methodpostenctypemultipart/form-dataaction{% url core:MovieImageUpload movie_idobject.id %}{% csrf_token %}{{ image_form.as_p }}pbutton classbut btn-primary上传/button/p/form/div{% endif %}{# 投票部分 #}{% if vote_form %}form methodpost action{{ vote_form_url }}{% csrf_token %}{{ vote_form.as_p }}button classbtn btn-primary投票/button/form{% else %}p 先登录再给此电影投票/p{% endif %}
{% endblock %}
复制代码更新movie_detail.html的main和sidebar部分。 main block中用image字段的url属性返回MEDIA_URL中设置的URL再与计算的名字相拼接然后我们可以通过tag找到正确的图片。 sidebar block中form tag中一定要引入enctype属性以便可以让上传的文件与请求的属性相关联。 模版升级完成可以开始创建保存上传文件的视图了MovieImageUpload。 1.5 创建MovieImageUpload视图 编辑core/views.py文件 # 创建图片上传视图
class MovieImageUpload(LoginRequiredMixin, CreateView):form_class MovieImageFormdef get_initial(self):initial super().get_initial()initial[user] self.request.user.idinitial[movie] self.kwargs[movie_id]return initialdef render_to_response(self, context, **response_kwargs):movie_id self.kwargs[movie_id]movie_detail_url reverse(core:MovieDetail,kwargs{pk: movie_id})return redirect(tomovie_detail_url)def get_success_url(self):movie_id self.kwargs[movie_id]movie_detail_url reverse(core:MovieDetail, kwargs{pk: movie_id})return movie_detail_url
复制代码 视图再一次做了验证和保存模型的所有工作。我们从请求的user属性中获取user.id属性从URL中获取movie ID当MovieImageForm的user和movie字段不可用时(忽略请求body体中的参数值)将user和movie ID当作初始参数传给form。 Django的ImageField会对文件改名和存储。 1.6 将请求关联到视图和文件上 将文件上传视图MovieImageUpload关联到URLConf中。 编辑core/urls.py from django.conf.urls import url
from django.urls import pathfrom core import viewsapp_name coreurlpatterns [# 省略其他路径# 配置path(movie/int:movie_id/image/upload,views.MovieImageUpload.as_view(),nameMovieImageUpload),
]
复制代码像往常一样我们添加一个path()函数确保传入一个movie_id参数。 现在Django就知道如何找到我们新增的文件上传视图只是它还不知道如何对外提供这个上传的文件。 在开发环境中为了对外提供该上传的文件更新下urls.py文件 MyMovie/urls.py from django.conf import settings
from django.conf.urls.static import staticfrom django.contrib import admin
from django.urls import path, includeimport core.urls
import user.urlsMEDIA_FILE_PATHS static(settings.MEDIA_URL,document_rootsettings.MEDIA_ROOT)urlpatterns [path(admin/, admin.site.urls),path(user/, include(user.urls, namespaceuser)),path(, include(core.urls, namespacecore)),
] MEDIA_FILE_PATHS
复制代码Django提供了static()函数返回一个包含单路径对象的列表该对象将以字符串MEDIA_URL开头的任何请求路由到document_root中的文件。 开发环境中这给我们提供了一种上传图片文件的方式。这种方式不适合生产环境如果settings.DEBUG是Falsestatic()函数将返回一个空列表。 天星技术团QQ557247785。