毕业设计基于Django的B2C线上电子产品销售平台设计与实现

摘 要

近几年来,网络信息技术日新月异,整个网络环境相较于前几年有了深刻的变革,这些网络信息相关的技术不仅让我们更加实质性地享受到互联网带给我们的便利、让互联网更加泛化和深化,同时也让我们和互联网相关的开发人员更加直观更加方便地去开发互联网应用。在此同时,电商网站也有了长足的发展,如今的电商已经不是只有淘宝京东几家独大,想要开网店必须去淘宝京东开店铺的局面也不复存在。技术的发展让电商网站的建立简单了起来。本文就结合当前较为主流的Python Web框架Django来搭建一个B2C线上电子产品销售平台的电商系统,该系统使用B/S架构和前后端分离的模式进行设计,后端使用Django Rest Framework向前端提供API,前端使用国内流行的Vue框架进行数据展示。本文将从多个维度去阐述和解释系统的各个模块和各种功能,在实现电商网站的基本需求的同时,还对电子产品销售相关的内容进行优化,以及对其他附加功能的实现。本文最终将实现一个B2C的电子产品销售平台,用户可以进行注册会员、浏览商品、购买商品和管理信息等的操作,管理员可以也可以轻松地管理商品信息、用户信息等等操作。

关键词: Python;Django;Django REST framework;电商平台;电子产品
B2C Online Electronics Sales Platform Design and Implementation Based on Django
Abstract
In recent years, the network information technology has been changing with each passing day, and the entire network environment has undergone profound changes compared to previous years. These network information-related technologies not only allow us to more substantively enjoy the convenience brought to us by the Internet, but also make the Internet more ubiquitous. Intensification and deepening also make it more intuitive and convenient for us and Internet-related developers to develop Internet applications. At the same time, the e-commerce website has also made considerable progress. Nowadays, e-commerce is not the only dominance of Taobao Jingdong, and there is no longer any need for Taobao Jingdong to open stores. The development of technology has made the establishment of e-commerce websites simple. This article combines the current mainstream Python Web framework Django to build a B2C online electronic product sales platform e-commerce system. This system uses the B/S architecture and the front-end and back-end separation mode to design, and the back-end uses Django Rest Framework to the front-end. Provide API, front-end use of domestic popular Vue framework for data display. This article will elaborate and explain various modules and various functions of the system from multiple dimensions. While realizing the basic requirements of the e-commerce website, it also optimizes the content related to electronic product sales, and implements other additional functions. This article will eventually implement a B2C electronic product sales platform where users can perform operations such as registering members, browsing products, purchasing goods, and managing information. Administrators can also easily manage product information, user information, and other operations.

Keywords: Python; Django; Django RSET framework; Electronic business platform; electronic product

目录

摘 要 I
目录 III
第一章 绪论 1
1.1 课题研究背景及意义 1
1.2 国内外研究现状 2
1.3 本文的主要工作 3
第二章 技术选型 4
2.1 Python 4
2.2 Django 5
2.3 MySQL 6
2.4 Django Rest Framework 7
2.5 Vue 8
第三章 系统分析 9
3.1 可行性分析 9
3.2 系统需求分析 10
3.2.1 业务需求分析 10
3.2.2 用户需求 11
3.2.3 功能需求分析 12
3.2.4 非功能需求分析 14
第四章 总体设计 16
4.1 总体功能模块设计 16
4.2 数据库设计 18
4.2.1 数据表结构 18
4.2.2 数据表字段 18
第五章 详细设计与实现 25
5.1 用户模块 25
5.2 商品模块 32
5.3 用户操作模块 41
5.4 交易模块 44
5.5 后台模块 51
5.6 其他模块 52
第六章 系统测试与部署 54
6.1 测试环境 54
6.2 系统测试 54
6.3 测试内容 55
6.4 测试结果 58
第七章 全文总结 59
参考文献 60
致谢 61
外文原文 62
中文翻译 69

第一章 绪论

1.1 课题研究背景及意义
随着互联网的不断普及,电子商务对于人们来说也越来越熟悉。相较于传统的经营模式,电子商务目前属于一种较为新型的商业运作模式,并且已经取得了巨大的发展,同时在我国电子商务也属于较为成熟的营销模式。相较于国外,我国的电子商务起步比较晚。不过这并不影响我国电子商务的迅速发展,因为目前我国电子商务拥有庞大的消费群体以及先进的互联网技术,这为电子商务的发展提供了主要基础和动力。随着移动互联网的发展,电子商务的发展更是快马加鞭。我国电商平台不仅有像淘宝、天猫、京东等这样C2C的平台,还有像小米商城、网易严选等这种B2C的平台,具体一些,还有针对特定类型商品的电商平台,像当当网这样的图书销售平台,面向女性客户的蘑菇街等。如此种类繁多的电商平台,也表明着我国电子商务产业已经趋于成熟,运作模式也较为完善。
电子商务相较于传统商务有很大的优势。首先,电子商务可以打破时空的限制,使人与人之间可以进行更为方便的沟通和交流,这样就为电子商务的核心——交易,创造了有利条件。其次,电子商务相较于传统商务无需实体店的粉饰,店面地址、仓库等硬性条件设施的要求门槛较低,可以为企业的营销大大降低成本,同时随着这些年物流行业的蓬勃发展,货物的配送也逐渐不是交易的瓶颈。最后,电子商务的受众面更为宽广,可以容纳全国,乃至全世界的顾客,增大了市场的范围和跨度,这为充分发挥市场资源的配置作用创造了绝佳的条件。同样电子商务也有一些不足,比如客户对于商品不能有像实体店一般的认知、因为技术原因导致的交易不安全或是用户信息泄露、商家对于用户的欺诈等行为难以从根本上杜绝。这些现象都是我国电子商务发展中亟待解决的问题。
如今电商网站的需求相当庞大,上至大型采购、零售企业,下至农村合作社、个人小商铺,他们对商品的销售和管理用传统的手段,充斥着大量低级而又重复的操作,这无疑在时间和空间上都会收到诸多的限制,财力和人力也有较大的开销。所以,解决这个问题,需要一套完整电商系统去经营管理我们繁多的商品,不仅会减少管理的成本,还会减少出错的概率,解决了管理上的痛点和难点。
基于上述的需求,本文使用Python的Web框架Django实现一套B2C线上电子产品销售平台设计与实现。这套系统采用时下流行的Python语言,和Python生态中较为重量级的Web框架Django,以及配套Django的REST框架Django REST framework,使用前后端分离的设计模式,来实现这样一个电商系统。该系统使用开源数据库MySQL,项目部署在Linux上的Nginx容器上。这套B2C线上电子产品销售平台,可以进行用户的相关操作,如注册登录、修改信息等,进行商品的相关操作,如添加商品、修改商品,可以进行订单管理、支付宝支付等功能。较为全面地满足商户对于商品销售和商品管理方面的需求。
1.2 国内外研究现状
随着经济和科技的不断发展,电商系统已经被在国内外进行了广泛的研究。在国外,尤其是欧美国家,电商系统起步较早,早期的Ebay、Amazon都是电子商务中元祖级的产品,也为今后的电子商务平台奠定了基础。可以说在技术层次上,国外的相关技术发展已经十分完备,其后台使用的技术也不尽相同,老牌电子商务平台常使用技术较为完备成熟PHP、Java等技术栈,或者混合多种技术进行维护,而新兴的电商网站则经常使用Python、Ruby这些在Web开发上逐渐绽放的技术栈进行开发。但数据显示,国外电商网站的发展远没有国内活跃,一方面中外的一些差异是导致国内用户更喜欢网上购物的原因,另一方面,国外的物流产业远没有国内发达,也是限制国外电子商务产业发展的一个重要原因。
而在国内,得益于国家的快速发展和政策的扶持,电子商务在各个方面都有着不可小觑的发展。尤其是物流业的发展和近期的稳定,让电子商务变得更加便捷,足不出户就能完成商品的交易。国内在电子商务平台普遍使用PHP、Java作为其技术栈,新型的电商平台追求较为完备和稳定技术也是如此,所以,总览国内各大电商平台,采用Python、Ruby等语言作为其后台基础语言的确是为少数。
无论国内国外,在PC端,电商平台都通常使用B/S(浏览器/服务器)架构,免去了需要安装客户端的麻烦,并且可以完备地实现系统的所有功能,技术较为成熟。在移动端B/S架构与C/S(客户端/服务器)共存,但对于B/S架构的支持往往是不完善的,商家也大力地去支持使用其对应的APP,可以方便的浏览商品、进行交易。
1.3 本文的主要工作
本论文分为以下几个章节对毕业设计基于Django的B2C线上电子产品销售平台(太理电子商城)进行论述,每个章节的具体内容如下:
第一章 绪论。主要针对国内外电子商务网站进心分析,阐述本课题背景和研究本课题的意义所在。在本章最后,对论文的结构进心简单的归纳。
第二章 技术选型。这章主要针对本项目中使用何种技术及为什么选用这种技术进行阐述。
第三章 系统分析。这章通过可行性分析和需求分析来获得项目的具体需求,以便接下来的总体设计。
第四章 总体设计。这章从较高的层面分析了项目中电商系统的结构,同时也对数据库进行了设计。
第五章 详细设计。对总体设计中划分出个各个模块进行详细的设计。这其中包括各个模块中较为重要的核心技术的介绍和使用,以及核心功能的展示和核心代码的解析。
第六章 系统测试和部署。这一章主要是对本系统的各项功能和非功能需求进行测试,以确保系统的鲁棒性。
第七章 全文总结。

第二章 技术选型

在本文中,是以现阶段网络较为流行的B/S开源框架Django为基础进行开发,而Django是以Python为基础的web框架,所以在开发过程中所使用的后台语言为Python。数据库采用市面较为流行的轻量的开源数据库MySQL,项目中会有缓存问题的解决,使用内存数据库Redis对缓存进行存储。本项目在设计时,使用市面上流行的REST风格API,与Django所配套的RESTful API为Django REST Framework,在项目中采用此框架。由于项目使用前后端分离的开发模式,前端使用国内最为流行的Vue框架进行开发,本文主要对后端进行实现,在前端部分不会进行太多解释。
2.1 Python
Python,是一种广泛使用的高级编程语言,属于通用型编程语言,由吉多·范罗苏姆创造,第一版发布于 1991 年。可以视之为一种改良(加入一些其他编程语言的优点,如面向对象)的 LISP。作为一种解释型语言,Python 的设计哲学强调代码的可读性和简洁的语法(尤其是使用空格缩进划分代码块,而非使用大括号或者关键词)。相比于 C++ 或 Java,Python 让开发者能够用更少的代码表达想法。不管是小型还是大型程序,该语言都试图让程序的结构清晰明了。
与 Scheme、Ruby、Perl、Tcl 等动态类型编程语言一样,Python 拥有动态类型系统和垃圾回收功能,能够自动管理内存使用,并且支持多种编程范式,包括面向对象、命令式、函数式和过程式编程。其本身拥有一个巨大而广泛的标准库。
Python 虚拟机本身几乎可以在所有的操作系统中运行。Python 的正式解释器CPython是用 C语言编写的、是一个由社区驱动的自由软件,目前由 Python软件基金会管理。[1]
Python拥有一个较为强大的标准库。Python语言的核心只包含数字、字符串、列表、字典、文件等常见类型和函数,而由Python标准库提供了系统管理、网络通信、文本处理、数据库接口、图形系统、XML处理等额外的功能。
Python同时用拥有强大扩展(第三方库),Python中的组件pip专门用来管理这些第三方扩展,这些扩展极大的简化开发者开发Python应用的过程,比如常用的pymysql用来连接Python和Mysql数据库、numpy用来进行高级复杂的数学运算等等。
Python语法简洁明了,作为一种动态语言,python拥有大多数动态语言的特点——语法简单、阅读方便,程序员可以使用最简单、最少量的代码去实现自己想要实现的功能。
Python当然不可避免的也有一些缺点。Python属于动态语言,动态语言相较于静态语言最大的不足就是执行速度较慢。C、Java这类静态语言会在代码执行前将其编译成为机器可以识别的二进制编码,这大大的加快了程序的执行速度,而动态语言则是一行一行地去执行代码,所以Python的执行速度是比较慢的。
第二个缺点就是Python对于代码的封装不是很好,开发者无法将Python代码进行加密发布,发布一个Python应用程序就相当于将其源代码发布出去,安全性较于静态语言较差。
目前Python的使用场景十分广泛,国内外很多大型企业都是用Python去构建自己的产品,如提供文件分享服务的DropBox、提供图书、唱片、电影等文化产品的资料数据库网站豆瓣网、国内知名问答网站知乎、范科技主题网站果壳等等。
2.2 Django
Django是一个开放源代码的Web应用框架,由Python写成。采用了MVT的软件设计模式,即模型Model,视图View和模板Template。Django的主要目标是使得开发复杂的、数据库驱动的网站变得简单。Django注重组件的重用性和“可插拔性”,敏捷开发和DRY法则(Don’t Repeat Yourself)。在Django中Python被普遍使用,甚至包括配置文件和数据模型[2]。
使用Django,可以在短时间内完成一个Web应用,免去了Web开发中的大部分麻烦,因此开发者可以专注于编写应用程序的逻辑实现,而无需重复地制造轮子,并且Django是开源的。
Django有以下特点:首先Fully loaded,Django包含了许多可用的常见Web开发任务的额外功能,Django负责了用户身份验证、内容管理、站点地图、RSS提要等许多功能,开箱即用。其次,Django具有较高的安全性,帮助开发人员避免了许多常见的安全错误,如SQL注入、跨站点脚本、跨站点请求伪造等,其用户认证系统则提供了一种安全的方式来管理账号和密码。再次,Django具有较高的可扩展性,使用Django快速灵活地扩展以满足最高流量需求的能力。最后,其繁多的功能收到了许多公司,组织和政府的青睐,他们使用Django来构建各种各样的东西——从内容管理系统到社交网络再到科学计算平台。
Django框架的核心包括:一个面向对象的映射器,用作数据模型(以Python类的形式定义)和关系性数据库间的媒介;一个基于正则表达式的URL分发器;一个视图系统,用于处理请求;以及一个模板系统。
核心框架中还包括:一个轻量级的、独立的Web服务器,用于开发和测试。
一个表单序列化及验证系统,用于HTML表单和适于数据库存储的数据之间的转换。一个缓存框架,并有几种缓存方式可供选择。中间件支持,允许对请求处理的各个阶段进行干涉。内置的分发系统允许应用程序中的组件采用预定义的信号进行相互间的通信。一个序列化系统,能够生成或读取采用XML或JSON表示的Django模型实例。一个用于扩展模板引擎的能力的系统[3]。
在Python中常见的Web框架除Django还有Tornado、Flask等,Tornado和Flask在Python中属于轻量及框架,适合中小型项目,著名的问答网站豆瓣网就是使用Tornado框架进行开发。在本次项目中,有许多功能模块比如用户模块使用Django框架可以省去许多复杂反复的编码操作,所以本次项目中使用Django框架而非Tornado或Python。
2.3 MySQL
MySQL是一个轻量级的关系数据库管理系统,MySQL在过去由于性能高、成本低、可靠性好,已经成为最流行的开源数据库,因此被广泛地应用在Internet上的中小型网站中。随着MySQL的不断成熟,它也逐渐用于更多大规模网站和应用,比如维基百科、Google和Facebook等网站。非常流行的开源软件组合LAMP中的“M”指的就是MySQL。
MySQL提供两种不同的版本:开源MySQL社区服务器和专有的企业服务器,MySQL企业服务器区别于一系列作为服务器插件安装的专有扩展,但是它们共享版本编号系统,并且由相同的代码库构建。
MySQL的创始人麦克尔·维德纽斯以MySQL为基础,成立分支计划MariaDB。而原先一些使用MySQL的开源软件逐渐转向MariaDB或其它的数据库。例如维基百科已于2013年正式宣布将从MySQL迁移到MariaDB数据库。
Mysql有以下特点:使用C、C++编写,在各个平台编译,可移植性强。其使用的多线程是完全多线程,也支持多处理器。MySQL会对查询语句进行优化,在很大程度上会提高查询性能。它也支持多种协议的连接,如TCP/IP、ODBC、JDBC等,支持多国语言、支持多种存储引擎。MySQL使用标准的SQL语句进行数据库操作,上手简单。
在本次项目中,涉及的数据量、数据复杂程度都较小,使用庞大冗杂且收费的Oracle数据库实属大材小用,而在Python生态中,对SqlServer的支持不是很好,而Django默认使用的SQLite则又过于简单,对于本项目中的某些功能实现起来也不是很方便,所以在本项目中使用轻量又实用的Mysql数据库是最佳选择。
在项目的开发过程中,会使用Mysql数据库进行开发,而在实际的部署中,则使用MariaDB数据库,两者在使用上基本没有太大差异,数据类型也可以互通。
2.4 Django Rest Framework
Django REST Framework(以下简称DRF)是一个用于构建Web API的功能强大且灵活的工具包。它提供了一个Web项目使用REST风格API的实现,它有以下的几个特点:
强大的身份验证功能,身份验证策略包括OAuth1a和OAuth2,配合其他Django插件使用,更能实现自动登录验证等功能。
支持ORM和非ORM数据源的序列化。Django中的ORM功能已经十分强大,让开发者能够免去编写SQL语句这些繁复的工作,甚至建表等工作也帮开发者胜任。而DRF中对ORM和非ORM数据源的序列化更是让此功能如虎添翼,对于一些自定义的数据结构、富文本等也有更好的支持。
若不需要更强大的功能,只需使用常规的基于功能的视图即可完全自定义,并且实现起来要比单纯使用Django框架会更加简单。
DRF拥有广泛的文档和良好的社区支持,在使用DRF过程中遇到问题,绝大多数都可以在其丰富的文档中找到解决方法。同样,其生成文档的能力也是数一数二,对于前后端分离的项目,后端向前端提供的文档DRF可以自动生成,并附有样例演示和测试功能,十分方便。
DRF由国际公认的公司使用和信任,包括Mozilla,Red Hat,Heroku和Eventbrite,可以说是一个十分成熟的Web框架。
在本项目中,使用REST风格的url进行数据的请求、插入、修改等操作,并且要与Django搭配使用,DRF是最为合适且成熟的选择。
2.5 Vue
Vue是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
在本次项目中,前端并不是项目中的主要任务,所以在这里不再赘述。选择Vue,是因为其强大的功能和丰富的中文文档支持。Vue由华人主持开发,有丰富的中文支持,在项目中是由Vue脚手架创建单页面项目,打包供Django使用。

第三章 系统分析

3.1 可行性分析
可行性研究的目的就是用最小的代价在尽可能短的时间里确定问题是否能解决。本章将从以下四个方面去阐述该项目的可行性。
从经济可行性的角度分析,该项目的实施不涉及物力成本,只涉及人力的成本。若该项目是市场上的某个商业项目,确定项目是否值得投资开发,即要分析在整个软件生存周期中所花费的代价和效益之间的度量。本课题属于毕业设计,不涉及成本和效益,默认可行。
从技术可行性角度讲,如今要实现一个电商网站,技术已经不是问题,本项目中将使用开源免费的Python生态中的多个组件去完成这样一个电商网站,使用Django去实现网站基础的搭建、使用DRF去实现网站向前端所提供的各个接口、使用Vue实现前端页面的制作,期间设计的第三方登录、第三方支付都可以使用第三方开放平台所提供的API来实现,在项目部署时,本项目同样使用开源免费的Nginx容器。以上所提到的各种技术都已相对成熟,并且在行业内也属于领先水平,所以在技术上,是完全行得通的。
从操作可行性上分析,本项目开发阶段是在Windows环境下实施的,在这个环境下各种操作都非常方便,各种开发工具一应俱全,对于编写代码也十分便利,是十分友好的开发环境。项目编码结束后将会部署在Linux环境下,这个环境对于Python的支持十分到位,同时其灵活性和稳定性也相较于Windows更高,操作也十分便捷。所以在操作可行性上,是可行的。
最后从法律可行性上讲,本项目并不涉及与国家安全相关的一些信息,也不会危害社会。在项目的安全性得到保障的条件下,也不会泄露用户的个人信息。本项目属于B2C的电商网站,所出售的产品都是商家自己的产品,这也免去了很多C2C电商所产生的法律问题。在法律上,项目是可行的。
综上所述,本项目在经济可行性、技术可行性、操作可行性和法律可行性上都行得通,是值得去实施的一个项目。
3.2 系统需求分析
软件需求分析是软件开发期的第一个阶段,是软件生存周期最重要的一步,是关系到软件开发成败的关键步骤。它在问题定义和可行性研究阶段之后进行。它的基本任务是准确地回答"系统必须做什么?"这个问题。虽然在可行性研究阶段粗略了解了用户的需求,至还提出了一些可行的方案,但是可行性研究的基本目的是最小的代价在尽可能短的时间内确定问题是否存在可行的解法,因此许多细节都被忽略了,一个微小的错漏都可能导致误解或铸成系统的大错,在纠正时付出巨大的代价。因而可行性研究并不能代替需求分析,它实际上并没有准确地回答"系统必须做什么"这个关键问题。
需求分析是整个软件生命周期中非常重要的阶段,决定着软件项目的成败,在这个阶段本章将从业务需求、用户需求、功能需求和非功能需求这四个方面去阐述项目的需求问题。
3.2.1 业务需求分析
业务需求主要是面向投资人的需求,通过客户访谈的方式可以获取项目的业务需求。客户访谈是一个直接与客户交流的过程,既可以了解高层用户对软件的要求,也可以直接听取用户的呼声。
通过带入甲方角色来分析项目的业务需求。
甲方需要这样一个平台,在这个平台上,作为直接向用户出售商品商户,可以轻松地管理商品,这里面包括对商品的分类管理、首页头图投放等问题。在此之上,甲方也需要方便地管理用户和订单,若用户对于账户或订单有一些变更,作为管理者的甲方管理员可以轻松的进行管理。还有其最基本的功能,要完成一个或多个商品的交易流程,这个过程一定要是安全的。
业务需求分析是客户或机构组织层面的需求分析。由于这一些人并不是专业的技术人员,所以我们在与他们进行沟通是要注意引导和提取他们对系统业务需求。
项目的系统目标是构建一个B/S架构的电子设备线上商城,管理员可以进行用户的权限管理、信息管理,还可以进行交易管理和订单管理,也可以进行商品的添加、删除、修改等操作。用户可以注册登录商城账号,浏览商品,并对商品进行过滤筛选,完成一个完整的交易。实现以上功能即满足基本业务需求。
3.2.2 用户需求
用户需求是在软件设计和开发的过程中以用户为核心,进心调查与分析,发掘用户的具体要求。在本系统中,有以下两种重要角色,用户和管理员。作为用户,主要任务是访问网站进行商品购买和其他操作,而作为管理员,主要是对于数据库中的商品、订单、用户进行管理操作。项目中将系统划分为以下几个模块,这也是Django提倡开发者这样做的,以模块的方式划分职责,将系统划分为商品模块、交易模块、用户操作模块、用户模块和后台模块。
表3-1 用户模块
模块 功能 描述
登录 注册 注册账号
登录 登录商城
退出 注销账号登录状态
验证码 验证码 发送注册验证码
信息管理 查看个人信息 查看个人信息
修改个人信息 修改个人信息
修改密码 修改密码

表3-2 商品模块
模块 功能 描述
商品展示 全部展示 展示出所有商品
商品过滤 过滤出指定商品
轮播图投放 将指定商品放入轮播图
商品管理 添加商品 添加商品
修改商品 修改商品
删除商品 删除商品
分类管理 添加分类 添加分类
删除分类 删除分类
热词展示 展示热词 展示出热门搜索关键词

表3-3 交易模块
模块 功能 描述
购物车 加入购物车 将商品加入购物车
从购物车删除 将商品从购物车删除
修改购物车 修改购物车中的商品
订单管理 列出所有订单 显示出所有订单
新增订单 新增订单
删除订单 删除订单
支付 支付宝支付 支付宝支付

表3-4 用户操作模块
模块 功能 描述
收藏 展示收藏列表 展示收藏列表
收藏商品 收藏商品
留言 展示用户留言 展示用户留言
添加留言 添加留言
删除留言 删除留言
收货地管理 获取收货地址 展示全部收货地址
添加收货地址 添加收货地址
更新收货地址 修改收货地址
删除收货地址 删除收货地址

3.2.3 功能需求分析
功能需求是把具体的用户需求,变成软件的功能要求。规定开发人员必须在产品中实现的软件功能,用户利用这些功能来完成任务,满足业务需求。功能需求是基于业务需求和用户需求进行需求分析的结果,为满足业务需求和用户需求提出的一组产品功能列表,功能需求构成了一个完整的产品抽象模型,是产品设计团队进行产品设计和产品研发团队进行产品研发的基础。
表3-5 后台模块功能需求
功能名称 描述
个人信息管理 管理员对用于信息的修改,包括创建新用户,修改用户邮箱、姓名、性别、出生日期等操作,删除用户
订单管理 对于用户交易所产生的订单进行管理,包括修改订单收件人姓名、手机和收货地址
商品管理 管理员可以对商品进行管理,包括添加商品、删除商品、修改商品信息、修改商品剩余库存。同时,可以将商品设置为首页轮播图投放,分类区域广告的操作。
用户操作管理 管理员对用户的收藏、留言、预存收货地址进行修改

表3-6 用户接口功能需求
功能名称 描述
注册、登录 用户可以在注册界面注册成为网站会员,并通过账号密码登录商城,同样也可以注销登陆登录状态,也可以通过选择记住登录实现自动登录。
个人信息管理 用户可以自行修改个人信息内容。
收货地址管理 用户可以新增收货地址、删除已存在的收货地址,也可以对已有的收货地址进行修改。
订单管理 用户在交易过程中会生成交易订单,用户可以浏览自己的全部订单,对未付款的订单进行付款,也可以删除订单。
收藏管理 用户在登录状态下可以对商品进行收藏操作,也可以对已经收藏的商品取消收藏,在收藏列表可以看到用户所有收藏的商品
商品列表 用户可以查看商品的列表,也可以通过商品类别、价格区间、销量等条件进行过滤显示,也可以通过多种条件对商品列表进行排序。
商品详情 用户可以查看商品的详细信息,并在详情页进行购物车操作
支付 用户通过第三方支付工具对订单进行俯瞰
第三方登录 用户可以使用第三方账号如微博、微信、QQ进行商城登录,同时,也可以将第三方账户绑定至商城已有用户之上
3.2.4 非功能需求分析
(1) 性能需求
对于系统的响应时间作以下要求
表3-7 时间特性要求
时间特性 时间特性要求
响应时间 3秒内
更新处理时间 5秒内
数据转换与传输时间 3秒内
运行时间 全天候

对于软件的适应性,有以下需求:系统可以架设在Linux系统之上,拥有较强的扩展性,可以方便的进行其他模块的增加。对于用户而言,商城的网站必须可以正常地运行在ie8及以上、Chrome、Firefox等主流浏览器之上。

(2)质量属性
对于项目的质量要有以下需求:
正确性: 确保网站的正常访问,不会出现数据泄露、遗失、错误,或者乱码等问题;
可靠性: 确保系统出现异常或崩溃的状况的概率小于百分之三;
安全性: 要确保用户数据的安全,不会通过SQL注入、跨站访问伪造等方式获取或修改用户信息,对于恶意攻击或恶意爬取网站数据的情况,也能及时发现并做出响应的处理。
可移植性: 系统可以运行在多种发行版本的Linux、Unix系统之上。
鲁棒性: 网站可以接受较高的并行访问量,对于本次项目,网站应可以接受100人同时访问。并且网站可以连续运行6个月以上。

(3)接口需求
对于硬件,项目有以下需求:
处理器要求: Pentium II以上
运行环境: Python 3.0以上版本
内存要求: 1G RAM以上

对于软件接口有以下要求:
操作系统: Linux的各发行版本
数据库: MariaDB
开发工具:后端使用Pycharm进行开发,前端使用WebStorm进行开发,使 用FireFox进行调试

(4)其他需求
网站可以部署在Linux系统之上,Linux系统可以是CentOS 6.0以上版本、Ubuntu 12.04 以上版本或其他Linux发行版本。
网站需要部署在Nginx容器之上。
数据库支持MySQL5.0以上版本,或MariaDB。
浏览器支持ie8以上版本或FireFox、Chrome等主流浏览器。

第四章 总体设计

4.1 总体功能模块设计
Django使用MTV模型进行编程,Django的MTV模式本质上和MVC是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同。M 代表模型(Model),负责业务对象和数据库的关系映射(ORM);T 代表模板 (Template),负责如何把页面展示给用户(html);V 代表视图(View),负责业务逻辑,并在适当时候调用Model和Template。
除了以上三层之外,还需要一个URL分发器,它的作用是将一个个URL的页面请求分发给不同的View处理,View再调用相应的Model和Template,MTV的响应模式如下所示:

在这里插入图片描述

图4-1 Django MTV响应模式

在本项目中,配合使用DRF,使用REST风格的API使接口更加通俗易懂且易于管理维护。
在本项目中,主要由四个模块组成,这四个模块分别是用户模块、用户操作模块、交易模块和商品模块,这四个模块相互协作,完成网站的主体功能,具体主体功能模块设计图如图4-2:
在这里插入图片描述

图4-2 功能模块设计图

系统的主要参与人员有游客、会员用户和管理员,游客和会员用户有相互重叠的部分,但这里为了区别其功能,分别对待,图4-3是电子产品销售系统的用例图:

在这里插入图片描述

图4-3 系统用例图
4.2 数据库设计
4.2.1 数据表结构
数据库模型描述了在数据库中结构化和操纵数据的方法,模型的结构部分规定了数据如何被描述(例如树、表等)。数据模型从抽象层次上描述了系统的静态特征、动态行为和约束条件,为数据库系统的信息表示与操作提供了一个抽象的框架。
E-R图也称实体-联系图,提供了表示实体类型、属性和联系的方法,用来描述现实世界的概念模型。本项目E-R图如图4-2:
在这里插入图片描述

图4-2 E-R图
4.2.2 数据表字段
(1) user_userProfile表,该表用来记录线上商城系统中用户的相关信息,包括管理员和普通会员

表 4-1 用户表
字段名 数据类型 长度 可否为空 键引用 字段含义 备注
id int 11 否 主键 账号id 唯一
password varchar 128 否 账号密码 加密
last_login datetime 6 是 最后一次登录时间
is_superuser tinyint 1 否 是否为超级用户
username varchar 150 否 用户名
is_stuff tinyint 1 否 是否为管理员
is_active tinyint 1 否 账号是否激活
date_joined datetime 6 否 注册时间
name varchar 30 是 姓名
birthday date 是 出生日期
gender varchar 6 是 性别
mobile varchar 11 是 手机号码
email varchar 100 是 邮箱

(2) user_verifycode表,用户模块中用于用户注册的表,用来验证用户的注册验证码是否正确。
表4-2 短信验证码表
字段名 数据类型 长度 可否为空 键引用 字段含义 备注
id int 11 否 主键 验证码编号 唯一
code varchar 10 否 验证码
mobile varchar 11 否 手机号
add_time datetime 6 否 添加时间

(3) goods_goodsCategory表,商品模块中用来表示商品类别信息,通过自连接实现类别间的一种层级关系,该表与自己关联。
(4) goods_goods表,商品模块中用来记录商品详细信息的表,通过此表,可以管理系统中的商品详细信息,该表与商品类别表相关联。

表4-3 商品类别表
字段名 数据类型 长度 可否为空 键引用 字段含义 备注
id int 11 否 主键 类别编号 唯一
name varchar 30 否 类别名
code varchar 30 否 类别code 唯一
desc longtext 否 类别描述
category_type int 11 否 类目级别
is_tab tinyint 1 否 是否导航
add_time datetime 6 否 添加时间
parent_category_id int 11 是 外键 父类别id

表4-4 商品表
字段名 数据类型 长度 可否为空 键引用 字段含义 备注
id int 11 否 主键 商品id 唯一
goods_sn varchar 50 否 商品唯一货号
name varchar 100 否 商品名
click_num int 11 否 点击数
sold_num int 11 否 销售量
fav_num int 11 否 收藏数
goods_num int 11 否 库存数
market_price int 11 否 市场价格
shop_price double 否 本店价格
goods_brief double 否 商品简短描述
goods_desc longtext 否 商品详细描述 富文本
ship_free longtext 1 否 是否免运费
goods_front_image varchar 100 是 封面图
is_new tinyint 1 否 是否新品
is_hot tinyint 1 否 是否热销
add_time datetime 6 否 添加时间
category_id int 11 否 外键 商品类别id
(5) goods_goodsImage表,商品模块商品展示图表,是在商品详情页对商品多张轮播图的存储位置的定位。该表与商品信息表相关联。
表4-5商品轮播图表
字段名 数据类型 长度 可否为空 键引用 字段含义 备注
id int 11 否 主键 轮播图id 唯一
image varchar 100 是 图片位置
add_time datetime 6 否 添加事件
goods_id int 11 否 外键 对应商品id

(6) goods_banner表,商品模块中关于首页轮播图的信息的表,通过后台系统可以添加、删除、修改首页轮播图。
表4-6 首页轮播商品表
字段名 数据类型 长度 可否为空 键引用 字段含义 备注
id int 11 否 主键 id 唯一
image varchar 100 否 图片地址
index int 11 否 轮播顺序
add_time datetime 6 否 添加时间
good_id int 11 否 外键 对应商品id

(7) goods_goodsbrand表,商品模块中记录着品牌信息的表。
表4-7 品牌表
字段名 数据类型 长度 可否为空 键引用 字段含义 备注
id int 11 否 主键 id 唯一
name varchar 30 否 品牌名
desc longtext 否 品牌描述
image varchar 200 否 图片地址
add_time datetime 6 否 添加时间
category_id int 11 是 外键 商品类目id
(8) goods_indexad表,商品模块中,与类别广告投放相关的表,首页中会按照类别分成多个区块,每个区块有一个广告区域,这个表负责管理这个区域。
表4-8 类别广告表
字段名 数据类型 长度 可否为空 键引用 字段含义 备注
id int 11 否 主键 id 唯一
category_id int 11 否 外键 商品类目id
goods_id int 11 否 外键 商品id

(9) trade_shoppingcart表,交易模块中用来记录用户购物车信息的表,用来管理用户单个商品购买数量。
表4-9 购物车表
字段名 数据类型 长度 可否为空 键引用 字段含义 备注
id int 11 否 主键 id 唯一
nums int 11 否 购买数量
add_time datetime 6 否 添加时间
goods_id int 11 否 外键 购买商品
user_id int 11 否 外键 购买用户

(10) trade_orderinfo表,交易模块中保存订单信息的表,用户购买商品生成订单,创建记录,第三方支付回调会修改订单状态。该表与用户信息表相关联。
表4-10 订单表
字段名 数据类型 长度 可否为空 键引用 字段含义 备注
id int 11 否 主键 id 唯一
order_sn varchar 30 是 订单号
trade_no varchar 100 是 交易号
pay_status varchar 30 否 订单状态
post_script varchar 200 否 订单留言
order_mount double 否 订单金额
pay_time datetime 6 是 支付时间
address varchar 100 否 收货地址
续表4-10
字段名 数据类型 长度 可否为空 键引用 字段含义 备注
signer_name varchar 20 否 签收人姓名
signer_mobile varchar 11 否 签收人手机
add_time datetime 6 否 添加时间
user_id int 11 否 外键 对应用户

(11) trade_ordergoods表,交易模块中记录订单中具体有哪些商品和商品数量的表,管理员可以修改订单详情以满足客户的需求。该表与商品信息表、订单表相关联。
表4-11 订单详情表
字段名 数据类型 长度 可否为空 键引用 字段含义 备注
id int 11 否 主键 id 唯一
goods_num int 11 否 商品数量
add_time datetime 6 否 添加时间
goods_id int 11 否 外键 对应商品
order_id int 11 否 外键 对应订单

(12) user_operation_userfav表,用户操作模块记录用户收藏商品的表,用户可以在自己的收藏管理中管理自己已经收藏的商品,也可以在对应的商品详情页将商品添加至收藏中或从收藏中删除,以上操作主要是对这张表的操作。该表与商品表、用户信息表相关联。
表4-12 用户收藏表
字段名 数据类型 长度 可否为空 键引用 字段含义 备注
id int 11 否 主键 id 唯一
add_time datetime 6 否 添加时间
goods_id int 11 否 对应商品
user_id int 11 否 对应用户

(13) user_operation_userleavingmessage表,用户操作模块中与用户留言相关的表,用户在个人管理页面可以对管理员进行留言。
表4-13用户留言表
字段名 数据类型 长度 可否为空 键引用 字段含义 备注
id int 11 否 主键 id 唯一
message_type int 11 否 留言类型
subject varchar 100 否 主题 1 留言,2 投诉, 3 询问,4 售后,5 求购
message longtext 否 留言内容
file varchar 100 否 上传的文件地址
add_time datetime 6 否 添加时间
user_id int 11 否 外键 对应用户

(14) user_operation_useraddress表,用户操作模块中管理用户配货地址的表,用户可以在个人中心添加、删除、修改配送地址,在提交订单时,选择合适的配送地址。
表4-14用户收货地址表
字段名 数据类型 长度 可否为空 键引用 字段含义 备注
id int 11 否 主键 id 唯一
privince varchar 100 否 省份
city varchar 100 否 城市
district varchar 100 否 区域
address varchar 100 否 详细地址
signer_name varchar 100 否 签收人
signer_mobile varchar 11 否 电话
add_time datetime 6 否 添加时间
user_id int 11 否 外键 对应用户

第五章 详细设计与实现

详细设计是继概要设计后的一个独立软件研制阶段,借助合适的图表等方式配合语言描述,对软件单元进行详细的过程描述,从而在实现阶段可以直接翻译成用某种程序设计语言书写的程序。
本章讲详细地对上一章节中划分出的模块进行设计。
项目的目录结构如图5-1所示:
在这里插入图片描述

图5-1 项目目录结构
5.1 用户模块
用户模块是线上电子销售系统极为重要的模块,好的设计能极大地提高用户体验,同时用户模块对于安全性地要求也比较高,否则会造成用户信息泄露甚至密码泄露等风险。
用户模块是对不同用户的不同权限的划分,在本系统中存在以下4中角色一是超级管理员,拥有所有权限,只在系统开发调试时使用,正常情况下不开启,二是普通管理员,普通管理员根据不同管理员的身份,其权限也有不同的划分,三是普通用户,可以对个人信息进行管理,进行商品交易,最后是游客用户,这类用户无需登录,只可以进行浏览商品列表、商品详情等无需登录的操作。
用户模块中,注册登录功能是以后其他功能的基石,很多功能要基于用户的登录状态。对用超级管理员和普通管理员来讲,其注册登录入口与普通用户不同,
这种用户只能通过拥有创建用户权限的管理员在后台创建,而且他们也有自己独立的登录入口和管理界面。可以说项目中使用的后台系统是一套和线上商城系统共享同一数据库的独立系统,在本项目中作为一个模块使用。
(1) 用户注册功能
图5-2为普通用户注册的流程图:
在这里插入图片描述

图5-2 用户注册流程图
在这其中,用户提交内容的表单验证在前端和后端都会进行,前端进行一次过滤,前端通过的表单将会在后端进行再次验证,这样做是为了防止在前端有人恶意修改代码,保证了系统的安全性,这种解决方案也是效率较高的一种实现。
用于注册可以使用手机注册,手机注册可以由第三方服务提供的API来实现,这里可以使用云片网提供的验证码发送功能来实现注册的功能,在实际部署上线的时候会接入云片网短信发送。
用户注册还有另外一种实现方案,使用邮箱注册。使用邮箱注册实现起来也很方便,在用户注册的时候,会向用户注册所使用的邮箱发送一封邮件,邮件中包含验证码,同时数据库中也保存着这个验证码。通过这样方式实现的注册,在成本上要比手机注册小,手机注册使用第三方服务需要付费,而邮箱注册只需开发者使用邮箱的SMTP服务或者自己搭建邮箱服务器去实现即可。
图5-3是前端用户注册的入口:
在这里插入图片描述

图5-3 用户注册
使用Django框架,实现用户注册十分简单方便,可以说Django已经帮开发者完成了这个工作,只需开发者进行简单的装配即可。用户注册view的核心代码如下,UserViewset用来实现用户注册,用户获取功能、通过重写get_permissions函数,动态的加载permission_classes。
def get_permissions(self):
if self.action == “retrieve”:
return [permissions.IsAuthenticated()]
elif self.action == “create”:
return []
return []
重写get_object方法,在url中随意请求一个,都会返回当前用户。
def get_object(self):
return self.request.user
def perform_create(self, serializer):
return serializer.save()
(2)用户登录功能
图5-4为普通用户登录的流程图:
在这里插入图片描述

图5-4 用户登录流程图
用户登录分两种情况,一种是普通的登录,一种是自动登录。
对于初次访问项目的页面,或者浏览器清除过cookie信息的用户来说,他们在访问本电商系统网站的时候,需要使用账号密码登录到商城系统中去,系统将用户输入的账号密码通过表单传输到后端,后端将密码通过特殊加密算法后和数据库内容进行比对,进而判断是否可以让用户登录到系统。这个加密过程是Django的user模块已经帮开发者实现好的,开发者在完成user模块的时候,继承了Django为开发者提供的user模块,这样的实现更加方便,也是对Django的代码的复用。
在完成登录后,服务端将会向浏览器发送一个json串,用来保存用户的登录状态,实现这个功能的技术是JWT。
JSON Web Token(JWT)是一个开放式标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间以JSON对象安全传输信息。这些信息可以通过数字签名进行验证和信任。可以使用秘密(使用HMAC算法)或使用RSA的公钥/私钥对对JWT进行签名。
在身份鉴定的实现中,传统方法是在服务端存储一个session,给客户端返回一个cookie,而使用JWT之后,当用户使用它的认证信息登陆系统之后,会返回给用户一个JWT,用户只需要本地保存该token(通常使用local storage,也可以使用cookie)即可。当用户希望访问一个受保护的路由或者资源的时候,通常应该在Authorization头部使用Bearer模式添加JWT,其内容看起来是下面这样:Authorization: Bearer 。
因为用户的状态在服务端的内存中是不存储的,所以这是一种无状态的认证机制。服务端的保护路由将会检查请求头Authorization中的JWT信息,如果合法,则允许用户的行为。由于JWT是自包含的,因此减少了需要查询数据库的需要。JWT的这些特性使得开发者可以完全依赖其无状态的特性提供数据API服务,甚至是创建一个下载流服务。因为JWT并不使用Cookie的,所以你可以使用任何域名提供你的API服务而不需要担心跨域资源共享问题(CORS)。
为什么要使用JWT? 相比XML格式,JSON更加简洁,编码之后更小,这使得JWT比SAML更加简洁,更加适合在HTML和HTTP环境中传递。在安全性方面,SWT只能够使用HMAC算法和共享的对称秘钥进行签名,而JWT和SAML token则可以使用X.509认证的公私秘钥对进行签名。与简单的JSON相比,XML和XML数字签名会引入复杂的安全漏洞。因为JSON可以直接映射为对象,在大多数编程语言中都提供了JSON解析器,而XML则没有这么自然的文档-对象映射关系,这就使得使用JWT比SAML更方便。
在Django中开发者要实现JWT功能其实相当简单,djangorestframework-jwt是DRF为开发者提供实现JWT认证的插件,在环境中安装这个插件后,只需在setting进行配置即可使用。
在这里插入图片描述

图5-5 用户登录
用户在登录时,即可以使用账号进行登录,也可以使用手机进行登录,具体实现的核心代码如下,实现自定义用户验证。
class CustomBackend(ModelBackend):
def authenticate(self, username=None, password=None, **kwargs):
try:
user = User.objects.get(Q(username=username) | Q(mobile=username))
if user.check_password(password):
return user
except Exception as e:
return None
(3) 用户个人信息管理
在这里插入图片描述

图5-6 个人信息管理
在会员中心的用户信息界面,可以直观地查看和修改个人信息。这些信息由特定的Serializer进行序列化然后展示在页面上。用户的手机号码无法修改,如果需要修改,后台具有特定权限的管理员可以完成这个任务,将在以后的后台模块再进行阐述。
以下为个人信息展示和修改功能的Serializer:
class UserDetailSerializer(serializers.ModelSerializer):
“”"
用户详情序列化类
“”"
class Meta:
model = User
fields = (“name”, “gender”, “birthday”, “email”, “mobile”)
本系统只对User模型中,name、gender、birthday、email、mobile这些字段进行序列化。在写View的过程中,将用户注册和用户信息展示写在了同一个View中,因为用户注册是对用户的创建create,而用户信息展示是对用户的获取get。在REST风格的编程中,这两个请求的url是相同的,但是其请求的方法是不同,针对这个区别,本系统重写了View中get_serializer_class函数,使其可以动态加载Serializer,具体代码如下:
def get_serializer_class(self):
if self.action == “retrieve”:
return UserDetailSerializer
elif self.action == “create”:
return UserRegSerializer
return UserDetailSerializer
(4)身份验证
身份验证是将传入请求与一组识别凭证(例如请求来自的用户或其签名的令牌)相关联的机制。然后,权限和限制策略可以使用这些凭据来确定请求是否应该被允许。REST框架提供了许多开箱即用的身份验证方案,还允许开发者实施自定义方案。
身份验证始终在视图的开始处运行,在执行权限和限制检查之前以及在允许其他任何代码继续执行之前运行。request.user属性通常会设置为contrib.auth包的User类的一个实例。request.auth属性用于任何其他身份验证信息,例如,它可以用来表示请求已签名的身份验证令牌。
当未经身份验证的请求被拒绝时,有两种不同的错误代码可能是合适的。
HTTP 401未经授权。
HTTP 403权限被拒绝。
HTTP 401响应必须始终包含WWW-Authenticate标头,该标头指示客户端如何进行身份验证。HTTP 403响应不包含WWW-Authenticate标头。
将使用哪种响应取决于认证方案。尽管可能正在使用多种认证方案,但只能使用一种方案来确定响应的类型。在确定响应类型时使用视图上设置的第一个认证类。
注意,当请求可以成功进行身份验证时,仍然会被拒绝执行请求的权限,在这种情况下,将始终使用403权限拒绝响应,而不管身份验证方案如何。

Django REST Framework在几种相关情况下返回状态码403:
当没有所需的权限级别时(例如,当DEFAULT_PERMISSION_CLASSES为(‘rest_framework.permissions.IsAuthenticated’)时,将未经身份验证的用户作为API请求)。
当执行不安全的请求类型(POST,PUT,PATCH或DELETE - 一个应该有副作用的请求)时,您正在使用rest_framework.authentication.SessionAuthentication,并且您未将CSRFToken包含在requeset中。当执行不安全的请求类型时,包含的CSRFToken不再有效。
以上的有关403状态的返回,是开发者解决对应问题的解决思路。
5.2 商品模块
在商品模块中,主要有商品列表、商品详情、商品广告这几个主要功能。但其展示不单单是在这对应的页面上,在商城的主页上,也可能有其对应的展示。
商品模块也是本系统的核心之一,商品模块也运用到了DRF中的许多核心技术,如序列化、过滤器等,在这一小节将对本系统对于这些功能的运用进行解释。
(1)序列化
序列化器允许将诸如查询集和模型实例之类的复杂数据转换为本地Python数据类型,然后可以将它们轻松地呈现为JSON,XML或其他内容类型。序列化器还提供反序列化,在首次验证传入数据之后,可以将分析的数据转换回复杂类型。
REST框架中的序列化程序与Django的Form和ModelForm类非常相似。DRF提供了一个Serializer类,它为您提供了一种强大的通用方法来控制响应的输出,以及一个ModelSerializer类,它为创建处理模型实例和查询集的序列化程序提供了有用的快捷方式。
通常你会想要序列化器类紧密地映射到Django模型定义。ModelSerializer类提供了一个快捷方式,可让开发者自动创建一个Serializer类,其中的字段与Model字段对应。
ListSerializer类提供了一次序列化和验证多个对象的行为。您通常不需要直接使用ListSerializer,而应该在实例化序列化程序时简单地传递many = True。当一个序列化器被实例化并且many = True被传递时,一个ListSerializer实例将被创建。序列化程序类成为父级ListSerializer的子级以下参数也可以传递给ListSerializer字段或传递了many = True的序列化程序:allow_empty。默认情况下为True,但如果要禁止将空列表作为有效输入,则可将其设置为False。
BaseSerializer类,可用于轻松支持可选的序列化和反序列化样式。由于此类提供与Serializer类相同的接口,因此您可以将它与现有的基于泛型类的视图完全一样用于常规Serializer或ModelSerializer。唯一不同的是,BaseSerializer类不会在可浏览的API中生成HTML表单。这是因为它们返回的数据不包含所有的字段信息,这些字段信息将允许将每个字段呈现为合适的HTML输入。
在本项目中,大量使用了序列化,不仅是商品模块,基本每个模块都在使用序列化这个功能。在本项目的结构目录中,每个模块都有一个serializers.py的源代码文件,这个文件进行着这个模块有关字段序列化的内容。
在这里插入图片描述

图5-7 商品类别
值得注意的是,在对商品类别进行序列化时,使用了一些特殊的技巧,在数据库设计的时候,系统将商品类别设置了一个外键,这个外键也是自己本身这张表。这样隐性地将分类分成了三个级别。如上图所示,手机通讯是一级类别,手机、手机配件是二级类别,小米手机、华为手机、苹果手机是三级类别。其具体的核心序列化代码如下,在序列化goods字段时,会执行下面的get_ad_goods方法。
class IndexCategorySerializer(serializers.ModelSerializer):
brands = BrandSerializer(many=True)
goods = serializers.SerializerMethodField()
sub_cat = CategorySerializer2(many=True)
ad_goods = serializers.SerializerMethodField()

Serializer fields处理原始值和内部数据类型之间的转换。他们还处理验证输入值,以及从其父对象中检索和设置值。DRF自带了许多Serializer fields去处理不同类型的字段,RegexField对正则表达式序列化、EmailField对Email序列化如此等等。
关系序列化:关系字段用于表示模型关系。它们可以应用于ForeignKey,ManyToManyField和OneToOneField关系,以及反向关系以及GenericForeignKey等自定义关系。检查关系。使用ModelSerializer类时,会为您自动生成序列化程序字段和关系。检查这些自动生成的字段可以成为确定如何定制关系风格的有用工具。
在Serializer中嵌套Serializer的时候,会出现url没有域名的情况,这时需要在嵌套的serializer加上context={‘request’: self.context[‘request’]}的参数。
def get_ad_goods(self, obj):
goods_json = {}
ad_goods = IndexAd.objects.filter(category_id=obj.id, )
if ad_goods:
good_ins = ad_goods[0].goods
goods_json = GoodsSerializer(good_ins, many=False, context={‘request’: self.context[‘request’]}).data
return goods_json

(2)View
REST框架提供了一个APIView类,它是Django的View类的子类。APIView类以下列方式与常规View类不同:
传递给处理程序方法的请求将是REST框架的Request实例,而不是Django的HttpRequest实例。处理程序方法可能会返回REST框架的响应,而不是Django的HttpResponse。该视图将管理内容协商并在响应中设置正确的渲染器。任何APIException异常都会被捕获并介入适当的响应。传入的请求将被认证,并且在将请求分派给处理程序方法之前将运行适当的许可和/或限制检查。
像往常一样,使用APIView类与使用常规View类非常相似,传入的请求被分派到适当的处理程序方法,如.get()或.post()。此外,可以在控制API策略的各个方面的类上设置许多属性。

基于类的视图的一个主要优点是它们允许你组合可重复使用的行为。REST框架通过提供大量预构建视图来提供常用模式,从而充分利用了这一点。
REST框架提供的通用视图允许您快速构建紧密映射到数据库模型的API视图。如果通用视图不适合您的API需求,则可以下拉使用常规APIView类,或者重用通用视图使用的mixins和基类来组成自己的可重用通用视图集。
mixin类提供用于提供基本视图行为的操作。请注意,mixin类提供了操作方法,而不是直接定义处理程序方法,如ge()和post()。这允许更灵活的行为组合。
ListModelMixin提供实现列出查询集的list(request,* args,** kwargs)方法。如果查询集已填充,则返回200 OK响应,并将queryset的序列化表示形式作为响应的主体。响应数据可以可选地分页。
CreateModelMixin提供.create方法,实现创建和保存新模型实例。如果创建了一个对象,则会返回一个201 Created响应,并将该对象的序列化表示形式作为响应的主体。如果该表示包含名为url的键,则响应的位置标题将填充该值。如果为创建对象提供的请求数据无效,则将返回400错误请求响应,并将错误细节作为响应的主体。
RetrieveModelMixin 提供.retrieve方法,该方法实现在响应中返回现有的模型实例。如果可以获取对象,则返回200 OK响应,并将对象的序列化表示作为响应的主体。否则,它将返回一个404 Not Found。
UpdateModelMixin 提供.update方法,实现更新和保存现有模型实例。还提供了一个.partial_update方法,它与更新方法类似,只是更新的所有字段都是可选的。这允许支持HTTP PATCH请求。如果更新对象,则返回200 OK响应,并将对象的序列化表示形式作为响应的主体。如果提供的用于更新对象的请求数据无效,则将返回400错误请求响应,并将错误细节作为响应的主体。
DestroyModelMixin 提供一个.destroy方法,实现现有模型实例的删除。如果一个对象被删除,则返回一个204无内容响应,否则它将返回一个404 Not Found。
定制通用视图,通常你会想使用现有的通用视图,但使用一些稍微定制的行为。如果您发现自己在多个地方重复使用了一些自定义行为,则可能需要将行为重构为普通类,然后根据需要将其应用于任何视图或视图集。
在这里插入图片描述

图5-8 各种mixin和各种View的关系
PUT作为create。在版本3.0之前,REST框架将对待的PUT作为更新或创建操作进行混合,具体取决于对象是否已经存在。允许PUT作为创建操作是有问题的,因为它必然暴露关于对象存在或不存在的信息。透明地允许重新创建之前删除的实例也不是一个比仅仅返回404响应更好的默认行为。两种样式“PUT as 404”和“PUT as create”在不同情况下都可以使用,但从3.0版开始,现在使用404行为作为默认值,因为它更简单,更明显。如果需要泛型的PUT-as-create行为,您可能希望将AllowPUTAsCreateMixin类包含在您的视图中。
(3)viewSet
Django REST框架允许您将一组相关视图的逻辑组合到一个名为ViewSet的类中。在其他框架中,您可能会发现概念上类似的实现,名为“Resources”或“Controllers”。ViewSet类只是一种基于类的View,它不提供诸如.get()或.post()之类的方法处理程序,而是提供诸如.list()和.create()之类的操作。ViewSet的方法处理程序仅使用.as_view()方法绑定到最终化视图时的相应操作。通常,不是在urlconf中的视图中明确注册视图,而是使用路由器类注册视图集,这会自动为您确定urlconf。
使用ViewSet类比使用View类有两个主要优点。重复的逻辑可以合并成一个类。在上面的例子中,用户只需要指定一次查询集,它将在多个视图中使用。通过使用路由器,而不再需要处理自己的URL配置。
这两者都有一个折衷。使用常规视图和URL conf更加明确,并给你更多的控制权。如果您希望快速启动并运行ViewSets,或者您拥有大型API并且希望始终实施一致的URL配置,ViewSets会非常有用。
ViewSet:ViewSet类继承自APIView。您可以使用任何标准属性(如permission_classes,authentication_classes)来控制视图上的API策略。ViewSet类不提供任何操作的实现。为了使用ViewSet类,您将覆盖该类并明确定义动作实现。
GenericViewSet:GenericViewSet类继承自GenericAPIView,并提供默认的get_object,get_queryset方法和其他通用视图基础行为,但默认不包含任何操作。为了使用GenericViewSet类,您将覆盖该类并混合所需的mixin类,或明确定义操作实现。
ModelViewSet:ModelViewSet类继承自GenericAPIView,并通过混合各种mixin类的行为来包含各种操作的实现。ModelViewSet类提供的操作是list(),retrieve(),create(),update(),partial_update()和destroy()。

(4)filtering
REST框架的通用列表视图的默认行为是返回模型管理器的整个查询集。通常您会希望您的API限制查询集返回的项目。
筛选GenericAPIView子类的任何视图的queryset的最简单方法是覆盖.get_queryset()方法。重写此方法允许开发者以多种不同方式自定义视图返回的查询集。
DjangoObjectPermissionsFilter旨在与django-guardian软件包一起使用,并添加了自定义“视图”权限。过滤器将确保查询集仅返回用户具有适当查看权限的对象。
如果您使用的是DjangoObjectPermissionsFilter,那么您可能还需要添加适当的对象权限类,以确保用户只有在具有适当对象权限的情况下才能对实例进行操作。做到这一点的最简单方法是继承DjangoObjectPermissions并为perms_map属性添加“视图”权限。

(5)商品列表页
本项目在商品模块大量的使用了ViewSet和Mixin,通过继承其对应的类来实现相应的功能。
在这里插入图片描述

图5-9 商品列表页
商品列表页通过不同的商品类别过滤出所要展示的商品,其中,通过filter将所有结果集中的所需商品按照要求过滤出来,filter的核心代码如下:
class GoodsFilter(django_filters.rest_framework.FilterSet):
pricemin = django_filters.NumberFilter(name=“shop_price”, lookup_expr=‘gte’)
pricemax = django_filters.NumberFilter(name=“shop_price”, lookup_expr=‘lte’)
top_category = django_filters.NumberFilter(method=‘top_category_filter’)

def top_category_filter(self, queryset, name, value):
    return queryset.filter(Q(category_id=value) | Q(category__parent_category_id=value) | Q(category__parent_category__parent_category_id=value))

可以从图中看出,开发者可以对结果集进心过滤,上栏的价格可以对商品的价格区间进心过滤,当然也可以自定义价格过滤。同样,左栏的手机品牌是通过商品类别进行过滤。
在有特别多的商品的时候,不可避免的是对商品进行分页,不论开发者是使用传统分页的样式,还是要使用瀑布流的样式。和分页相关的核心代码如下:
class GoodsPagination(PageNumberPagination):
page_size = 8
page_size_query_param = ‘page_size’
page_query_param = “page”
max_page_size = 100
在商品列表的view中使用这个类作为其分页的方法即可完成分页功能。REST框架包含对可定制分页样式的支持。这允许您修改多大的结果集被分成单独的数据页面。
分页API可以支持:作为响应内容的一部分提供的分页链接。响应标题中包含的分页链接,如Content-Range或Link。内置的样式当前都使用作为响应内容的一部分的链接。使用可浏览的API时,此样式更易于访问。
分页仅在您使用通用视图或视图集时自动执行。如果您使用的是常规APIView,则需要自己调用分页API以确保返回分页响应。示例请参阅mixins.ListModelMixin和generics.GenericAPIView类的源代码。
可以通过将分页类设置为无,关闭分页。

在商品列表页本系统也实现了对结果集的排序功能,在图5-8中可以看到本系统可以通过销量或者价格进行升序或者降序排列商品,这些属于GenericViewSet的基本功能。
以下是商品列表页view的核心源代码,使用SearchFilter、OrderingFilter实现了搜索和排序功能,同时还设置了查询的字段和对哪些字段排序。
serializer_class = GoodsSerializer
pagination_class = GoodsPagination
filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter)
filter_class = GoodsFilter
search_fields = (‘name’, ‘goods_desc’, ‘goods_brief’)
ordering_fields = (‘sold_num’, ‘shop_price’)

以下的方法实现了访问量的自增,重写retrieve方法,进入详情页面点击数自加1。
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
instance.click_num += 1
instance.save()
serializer = self.get_serializer(instance)
return Response(serializer.data)

(6)商品详情页
商品详情页作为一个单独的页面承接了商品到交易的过渡,商品的详情页承载了单个商品的全部详细信息,页承载了用户的部分操作功能。
在这里插入图片描述

图5-10 商品详情页
如图5-10所示,商品详情页展示了商品名、商品简短描述、是否承配送费用、市场价、店内价格、销量、库存等商品基本信息,还包括左边商品图片轮播信息,在下面还有有关商品详细信息的富文本。
有关富文本的编辑与展示,本系统采用百度的开源富文本编辑插件UEditor。UEditor是由百度web前端研发部开发所见即所得富文本web编辑器,具有轻量,可定制,注重用户体验等特点,开源基于MIT协议,允许自由使用和修改代码
在这里插入图片描述

图5-11 UEditor富文本编辑器
从pip中安装的UEditor与python3.6有许多不兼容的地方,会导致很多功能无法使用,本系统将开源的与Django配套的UEditor进行改造作为一个模块加入到了Extra_app中,解决了其中一些不兼容的问题。在ORM中,本系统也有与UEditor富文本应的序列化字段,只需直接使用即可。

(7)首页的商品
首页也是一个商品列表的载体,他承载着更为丰富的内容,他是很多功能的入口点,如登录注册、个人中心、商品列表、商品详情等,同样也是很多广告的重要投放点,首页的合理布局会影响整个电商平台的访问量甚至是负载。
在这里插入图片描述

图5-12 首页
商品分类很直观的体现在首页,本项目首页参考了小米商城的布局,最上面的导航条可以自定义定制,可以选择任意级别任意类型的类别显示在导航条上,而左面的导航栏则显示了所有的分类,中间部分是新品的展示,在设计数据库表的时候,本系统设计了一个字段为是否是新品,新品将会在首页进行展示,就是此处。再往下是对一级分类的专区,左边显示其包含的二级分类和品牌信息,右边则是具体的商品。当然还有其他细节的部分,如购物车,注册登录入口、个人中心入口这里不再赘述。
5.3 用户操作模块
用户操作模块主要用来管理用户的收货地址信息、收藏信息和留言信息。
(1)用户收货地址管理
用户收货地址是在用户提交订单的时候提供的用户收货地址,这其中包括用户收件人,省份地区信息、详细地址、收件人姓名、收件人联系方式等字段。
在这里插入图片描述

图5-13 收货地址管理
用户可以修改已经存在的收货地址信息,也可以添加新的收货地址或删除已有的收货地址,这些收货地址将在提交订单的时候以选择框的形式出现在订单提交页面。
相关收货地址的核心代码如下,其中list: 获取收货地址、create:添加收货地址、update:更新收货地址、delete:删除收货地址,因为涉及到增删改查,可以使用ModelViewSet。

class AddressViewset(viewsets.ModelViewSet):
serializer_class = AddressSerializer
permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
def get_queryset(self):
return UserAddress.objects.filter(user=self.request.user)

(2)留言信息管理
留言主要用于和后台管理员进行联系,如需要手动修改订单信息等。留言只能由管理员在后台查看。
用户留言管理模块的核心代码如下,其中List:获取用户留言、create:添加留言、delete:删除留言:
class LeavingMessageViewset(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet):
serializer_class = LeavingMessageSerializer
permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
在这里插入图片描述

图5-14 用户留言

(3)用户收藏
本系统在数据库中记录着用户对于商品的收藏信息,用户在商品详情页面可以方便的对商品进行收藏或对已经收藏的商品进行取消收藏,用户也可以在个人中心对自己收藏的商品进行简单的管理。
在这里插入图片描述

图5-15 商品详情页的收藏
在这里插入图片描述

图5-16 个人中心的收藏管理
本系统实现用户收藏功能可以借助Django的Signals信号量功能。Django 自带了一个信号调度程序允许receiver函数在某个动作出现时候去获取通知。当开发者需要其代码执行某些时间的同时发生些其他时间的时候,信号非常有用。同样,还可以创建一个自定义的信号,让别人在某个事件发生的时候可以获得通知。
Django自带的models提供了几个信号,它们位于django.db.models.signals。pre_save:调用model的save()方法前发送信号、post_save:调用model的save()方法后发送信号、pre_delete:调用model活着QuerySets的delete()方法前发送信号、
post_delete:同理,调用delete()后发送信号、m2m_changed:当一个模型上的ManyToManyField字段被改变的时候发送信号。
收藏有关信号量的核心代码如下:
通过信号量,完成商品收藏数+1的功能
@receiver(post_save, sender=UserFav)
def create_user_fav(sender, instance=None, created=False, **kwargs):
if created:
goods = instance.goods
goods.fav_num += 1
goods.save()
@receiver(post_delete, sender=UserFav)
def delete_user_fav(sender, instance=None, created=False, **kwargs):
goods = instance.goods
goods.fav_num -= 1
goods.save()

有关收藏的View的核心代码如下,list:获取用户收藏列表,retrieve:判断某个商品是否被收藏,create:收藏商品。其中lookup_field是设置是以以哪个字段为url,这里设置为goods_id,意思为/userfavs/goods_id/。
lookup_field = “goods_id”
def get_queryset(self):
return UserFav.objects.filter(user=self.request.user)
def get_serializer_class(self):
if self.action == “list”:
return UserFavDetailSerializer
elif self.action == “create”:
return UserFavSerializer
return UserFavSerializer
5.4 交易模块
交易模块也是本系统电商项目的核心之一,它主要承担着生成订单、支付等重要的功能,本系统在设计时,一定要考虑其安全性。在支付方面本系统采用接入都三方支付支付宝来确保其安全性和可靠性。
(1)购物车
在用户浏览商品的时候,不仅可以把喜欢的商品加入收藏,也可以直接将其加入到购物车进行购买。在传统的网站设计中,如JavaEE开发中可能会使用session机制将其保存在一个会话中,而在前后端分离的项目中,虽然也是同样的原理,但这样的任务全权交给了前端去做,而不是在后端进行。后端只需将购物车信息存入数据库即可。
在这里插入图片描述

图5-16商品加入购物车
用户不仅可以将商品加入购物车,在页面中,右上角存在一个呼出购物车的模态框,在这里用户可以直观地查看购物车中的商品信息和商品个数。系统在设计购物车数据库时,将购物车的每个商品设计成一条记录,这样的数据表可以容纳更多的信息,如购买商品的数量,也可以从购物车中直接转去结算页面。
在这里插入图片描述

图5-17购物车
当然用户在加入购物车时还有很多细节需要处理,如加入购物车的商品的数量不能小于1不能大于其库存数,还有对于已经在购物车存在的商品再次加入购物车就是增加其数量而不是重新覆盖等问题。
在serializer中,从self.context[“request”]中取出当前request。如果已经存在记录,则增加其数量;如果未存在记录,则创建记录,以下为购物车Serializer的核心代码:
def create(self, validated_data):
if existed:
existed = existed[0]
existed.nums += nums
existed.save()
else:
existed = ShoppingCart.objects.create(**validated_data)
return existed
serializers.Serializer并没有重写其父类的update方法,会抛出异常。要想使用该方法,必须重写,但删除无需重写delete方法,update方法修改商品数量,下面的instance是ShoppingCart的实例。
def update(self, instance, validated_data):
instance.nums = validated_data[“nums”]
instance.save()
return instance
(2)订单
对于进行结算操作,系统会直接生成一个订单,在数据库设计中,系统使用两张表来记录订单,一张时trade_OrderInfo表用来记录订单的一些信息,另一张是trade_OrderGoods表用来记录订单里面的商品信息。当刚刚生成好订单时,订单的状态时待支付的,当用户完成支付,接受到支付宝的回调之后,系统会把订单的状态变为已支付状态。
在这里插入图片描述

图5-18确认支付信息
如图5-18所示,用户在核实订单信息,选填好配送地址、支付方式和留言点击去结算就会生成一个状态为待支付的订单。
用户也可以在个人中心对自己的订单进行一些简单的管理。在这里不仅可以查看用户的全部历史订单,也可以对部分敏感订单进行删除。
在这里插入图片描述

图5-19订单管理
订单功能的部分核心代码涉及到支付宝回调,将会在下一个小节展示,以下是订单功能的其他核心代码:
class OrderViewset(mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.CreateModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet):
permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
serializer_class = OrderSerializer
def get_serializer_class(self):
if self.action == “retrieve”:
return OrderDetailSerializer
return OrderSerializer
以上的代码是对于订单浏览的用户认证设置,这里使用JWT认证,并设置了其使用的serializer。

(3)支付宝对接
在本项目中,使用接入支付宝来完成支付功能。支付宝为开发者提供了沙箱环境进行开发阶段的调试,本系统在此也是用沙箱环境进行开发,不涉及真实的金钱的交易。
在用户点击去结算的时候,会生成一个未支付的订单,同时也会跳转到支付宝为用户提供的支付页面。系统使用蚂蚁金服开发平台为开发人员的提供的专用的支付宝沙箱版APP即可进行付款交易。交易完成后,支付宝将会向系统的服务器发起回调,以证明交易顺利完成,在接收到会掉之后本系统会将订单的状态设置成已付款状态,并转向到本系统网站的首页。以上就是完成支付的一个大致的流程。
在这里插入图片描述

图5-20支付宝支付
在接入支付宝时,开发者需要进行一些配置和操作来符合支付对于商家的规范。

支付宝相关配置

private_key_path = os.path.join(BASE_DIR, ‘apps/trade/keys/private_2048.txt’)
ali_pub_key_path = os.path.join(BASE_DIR, ‘apps/trade/keys/alipay_key_2048.txt’)
ali_app_id = “2016080700191217”
app_notify_url = “http://39.108.208.63:8000/alipay/return/”
return_url = “http://39.108.208.63:8000/alipay/return/”
这其中private_key_path和ali_pub_key_path这两个路径所对应的文件是本系统使用支付提供的工具对开发者支付宝账号对应的应用所生成的公钥的私钥,支付宝使用RSA2的非对称加密方式来确保交易的安全性。ali_app_id 是支付宝为开发者提供的沙箱应用的appid,app_notify_url是支付宝的异步回调地址,return_url是同步回调地址。
图5-21为支付宝支付的调用时序图,该图是蚂蚁金服开发者平台向开发者提供的的一个用来展示一个完整支付流程的时序图。从中我们可以看到支付是否成功并不与同步回调通知为主,而是以异步回调为主,这样的设计也显而易见,对于一些长时间的订单,用户仍然可以进行支付,这个过程是安全的,我们不会因为这样的过程而导致多次支付,或者付款后而系统还显示未付款状态。
在蚂蚁金服开放平台提供了JAVA、PHP和.NET三个语言版本的SDK,封装了签名&验签、HTTP接口请求等基础功能。本系统中使用Github上的开源的支付宝Python解决方案。
在这里插入图片描述

图5-21 支付时序图
在我们的系统中,使用支付宝APP进行支付过程中会对图5-21的时序图中的1、4、5、7过程有直观的显示,如果使用支付宝网页直接支付,对2、3过程也会有相应的显示。
电脑网站支付结果异步通知,对于PC网站支付的交易,在用户支付完成之后,支付宝会根据API中商户传入的notify_url,通过POST请求的形式将支付结果作为参数通知到商户系统。
商户系统请求支付宝接口alipay.trade.page.pay,支付宝对商户请求参数进行校验,而后重定向至用户登录页面。用户确认支付后,支付宝get请求returnUrl(本系统入参传入),返回同步返回参数。交易成功后,支付宝post请求notifyUrl(本系统入参传入),返回异步通知参数。若由于网络等问题异步通知没有到达,商户可自行调用alipay.trade.query接口进行查询,根据查询接口获取交易以及支付信息(系统也可以直接调用查询接口,不需要依赖异步通知)。
系统接收到异步通知以后,必须通过验签(验证通知中的sign参数)来确保支付通知是由支付宝发送的。详细验签规则参考异步通知验签。
接收到异步通知并验签通过后,一定要检查通知内容,包括通知中的app_id、out_trade_no、total_amount是否与请求中的一致,并根据trade_status进行后续业务处理。
异步通知中有一些重要参数,系统需要在url中体现,并且使用加密算法加密。
表5-1异步通知公共参数
参数 参数名称 类型 必填 描述
notify_time 通知时间 Date 是 通知的发送时间。格式为yyyy-MM-dd HH:mm:ss
notify_type 通知类型 String(64) 是 通知的类型
notify_id 通知校验ID String(128) 是 通知校验ID
charset 编码格式 String(10) 是 编码格式,如utf-8、gbk、gb2312等
version 接口版本 String(3) 是 调用的接口版本,固定为:1.0
sign_type 签名类型 String(10) 是 签名算法类型,目前支持RSA2和RSA,推荐使用RSA2
sign 签名 String(256) 是 请参考异步返回结果的验签

表5-2异步通知业务参数
参数 参数名称 类型 必填 描述
trade_no 支付宝交易号 String(64) 是 支付宝交易凭证号
app_id 开发者的app_id String(32) 是 支付宝分配给开发者的应用Id
out_trade_no 商户订单号 String(64) 是 原支付请求的商户订单号

服务器异步通知有以下页面特性:1. 必须保证服务器异步通知页面(notify_url)上无任何字符,如空格、HTML标签、开发系统自带抛出的异常提示信息等; 2. 支付宝是用POST方式发送通知信息,因此该页面中获取参数的方式,如:request.Form(“out_trade_no”)、$_POST[‘out_trade_no’];3. 支付宝主动发起通知,该方式才会被启用; 4. 只有在支付宝的交易管理中存在该笔交易,且发生了交易状态的改变,支付宝才会通过该方式发起服务器通知(即时到账交易状态为“等待买家付款”的状态默认是不会发送通知的);5. 服务器间的交互,不像页面跳转同步通知可以在页面上显示出来,这种交互方式是不可见的;6. 第一次交易状态改变(即时到账中此时交易状态是交易完成)时,不仅会返回同步处理结果,而且服务器异步通知页面也会收到支付宝发来的处理结果通知;7. 程序执行完后必须打印输出“success”(不包含引号)。如果商户反馈给支付宝的字符不是success这7个字符,支付宝服务器会不断重发通知,直到超过24小时22分钟。一般情况下,25小时以内完成8次通知(通知的间隔频率一般是:4m,10m,10m,1h,2h,6h,15h);8. 程序执行完成后,该页面不能执行页面跳转。如果执行页面跳转,支付宝会收不到success字符,会被支付宝服务器判定为该页面程序运行出现异常,而重发处理结果通知;9. cookies、session等在此页面会失效,即无法获取这些数据;10. 该方式的调试与运行必须在服务器上,即互联网上能访问;11. 该方式的作用主要防止订单丢失,即页面跳转同步通知没有处理订单更新,它则去处理;12. 当商户收到服务器异步通知并打印出success时,服务器异步通知参数notify_id才会失效。也就是说在支付宝发送同一条异步通知时(包含商户并未成功打印出success导致支付宝重发数次通知),服务器异步通知参数notify_id是不变的。
以下为支付宝支付Serializer的核心代码:
def get_alipay_url(self, obj):
alipay = AliPay(
appid=“2016080700191217”,
app_notify_url=“http://39.108.208.63:8000/alipay/return/”,
app_private_key_path=private_key_path,
alipay_public_key_path=ali_pub_key_path,
debug=True,
return_url=“http://39.108.208.63:8000/alipay/return/” )
url = alipay.direct_pay(
subject=obj.order_sn,
out_trade_no=obj.order_sn, total_amount=obj.order_mount,)
re_url = “https://openapi.alipaydev.com/gateway.do?{data}”.format(data=url)
return re_url
上面一段代码是设置支付模块的一些参数,这些模块对接了支付宝的一些接口,包括同步回调地址、异步回调地址、支付宝appid等等,对于介入支付宝,这些参数是必不可少的。
下面的函数的作用是用来生成订单号。订单编号格式如下当前时间+userid+随机数。
def generate_order_sn(self):
random_ins = Random()
order_sn = “{time_str}{userid}{ranstr}”.format(time_str=time.strftime(“%Y%m%d%H%M%S”) userid=self.context[“request”].user.id, ranstr=random_ins.randint(10, 99))
return order_sn
5.5 后台模块
后台模块是专门为管理员设立的模块,方便管理员对各种信息如用户信息、商品信息、订单信息等进行管理。后台模块也有自己的一套权限管理机制,Django为开发者提供了admin模块,可以方便快速的进行后台模块的开发。而开源项目xadmin则是Django admin的升华,可以更加方便地进行管理后台。
以商品的管理为例,系统在商品的后台管理界面中,可以清楚明了的看到系统中所有商品的一个列表,并且在上方有一个过滤器和一个搜索框,管理员可以通过各种条件对全部商品进行过滤和搜索。同样新增商品和删除商品以及修改商品的操作也很方便。
在这里插入图片描述

图5-22太理电子商城后台
图5-23是后台中对商品管理的过滤器展示,管理员可以通过设置过滤器参数来筛选想要看到的商品。
在这里插入图片描述

图5-23商品后台
因为系统使用了xadmin,它作为一个独立的模块或者说app存在,它可以让开发者使用很少量的代码,甚至说是配置就完成以上功能。
以下是配置商品后台的部分核心代码,主要配置了要对哪些字段进心哪些操作:
class GoodsAdmin(object):
list_display = [“name”, “click_num”, “sold_num”, “fav_num”, “goods_num”, “market_price”,“shop_price”, “goods_brief”, “goods_desc”, “is_new”, “is_hot”, “add_time”]
search_fields = [‘name’, ]
list_editable = [“is_hot”, ]
list_filter = [“name”, “click_num”, “sold_num”, “fav_num”, “goods_num”, “market_price”,“shop_price”, “is_new”, “is_hot”, “add_time”, “category__name”]
style_fields = {“goods_desc”: “ueditor”}

这些代码配置了系统的商品后台中,每个商品在列表页面要显示哪些字段、有哪些过滤器,开发者可以通过哪些条件去限制显示的数量、甚至是系统的xadmin后台的标题、logo、底部版权标注的一些信息都可以在这里设定。
其他模块的配置和商品后台的配置大同小异,在这里不再赘述。
5.6 其他模块
其他模块主要是对本项目系统进行的一些优化。在系统中使用redis来代替传统的缓存,以加速用户的访问。经过测试,相较于传统的缓存机制,用户在再次访问页面的速度上可以加快近10倍的速度。
django-redis 基于 BSD 许可, 是一个使 Django 支持 Redis cache/session 后端的全功能组件。为何要用 django-redis ?它支持本地化的 redis-py URL 符号连接字符串,它可扩展客户端、解析器、序列器,默认客户端主/从支持,具有完善的测试,而且它已在一些项目的生产环境中作为 cache 和 session 使用,支持永不超时设置,支持原生进入 redis 客户端/连接池,高可配置 ( 例如仿真缓存的异常行为 ),支持多版本的Python。
在使用pip安装django-redis模块并将其配置到项目中后,只需在setting中进行如下配置,即可进入工作状态。(前提是以及安装好redis或者是搭建好redis集群)。

配置redis缓存

CACHES = {
“default”: {
“BACKEND”: “django_redis.cache.RedisCache”,
“LOCATION”: “redis://127.0.0.1:6379/3”,
“OPTIONS”: {
“CLIENT_CLASS”: “django_redis.client.DefaultClient”,
}}}
从图-24可以明显的看出使用redis作为缓存和不适用的区别
在这里插入图片描述

图5-24使用redis作为缓存的速度提升

第六章 系统测试与部署

软件测试是软件在正式投入使用或者交付前,必须要经过软件生命周期的阶段。具体来讲软件的系统测试需要根据软件需求文档、软件规格说明书、软件详细设计文档,对软件进行多轮测试,以期检查软件开发结果是否符合当初的设计和需求,同时软件测试工作也是对程序开发人员工作的验收。测试做的主要目标是找出实际运行的系统与需求的系统之间存在何种差异,帮助程序开发人员优化系统结构,提升代码质量,真正的提高软件产品的稳定性、安全性、可用性、易用性。
6.1 测试环境
使用以下环境进心测试:
服务端操作系统:Ubuntu 14.04 32位(阿里云)
客户端操作系统:Windows 10
数据库:MySQL 5.7
浏览器:Firefox 56.0b9 (64位)
网络:全部可以连通外部网络
6.2 系统测试
系统测试通常有两种主要的测试方法,它们分别是白盒测试和黑盒测试。本文所采用的测试方法也是这两种主流的软件测试。
白盒测试,也叫结构测试,它是一种面向程序的测试方法。主要的测试思路是:将程序看成一个透明的盒子,可以看见程序的运行流程和数据流方向。这种测试方式是需要测试者按照程序流程设计数据,使用断点调试的手段,查看数据流是否正确,验证返回值是否正确。
黑盒测试,也叫功能测试,它是面向用户的一种测试方法。主要的测试思路是:将程序看成一个看不见内部的沙盒,用户只能查看输入数据和输出数据。这种测试方法往往被用来测试软件程序是否实现了软件设计说明书上的特定功能,而且输入和输出的结果是否和设计规范里面一致。
本文采用白盒测试方法根据每个功能的实现选取设计测试用例,采用黑盒测试方法根据程序功能模块的划分选取测试用例。
6.3 测试内容
以下是需要完成的测试内容。
6.3.1 用户模块是否可以正常使用
(1) 用户注册的测试
(2) 用户登录的测试
(3) 用户修改个人信息的测试
(4) 用户登录状态保持的测试
表6-1 用户模块测试用例
序号 测试内容 输入内容 期望结果 备注
1 注册 13566668888
testpasswd 注册成功 账号首次注册
2 注册 13566668888
Testpasswd2 用户已存在 账号已被注册
3 注册 test
test 输入错误
4 登录 13566668888
testpasswd 登陆成功
5 登录 13566668888
testpass 密码错误 账号密码不匹配
6 修改个人信息 修改信息后提交 信息被修改

6.3.2 商品模块是否可以正常使用
(1) 商品浏览的测试
(2) 商品搜索的测试
(3) 商品排序的测试
(4) 商品过滤的测试
表6-2 商品模块测试用例
序号 测试内容 输入内容 期望结果
1 浏览商品 点击商品分类 显示出类别商品列表
2 商品过滤 点击价格区间 显示相应价格区间商品
3 商品排序 点击按价格排序 商品列表按价格排序
4 商品搜索 搜索框输入“华为” 显示所有名称包含华为的商品

6.3.3 用户操作模块是否可以正常使用
(1) 用户添加收货地址的测试
(2) 用户修改收货地址的测试
(3) 用户删除收货地址的测试
(4) 用户添加收藏的测试
(5) 用户取消收藏的测试
(6) 用户添加留言的测试
表6-3 用户操作模块模块测试用例
序号 测试内容 输入内容 期望结果
1 添加收货地址 输入信息后点击添加按钮 新地址被添加
2 修改收货地址 修改已有的收货地址点击确定 地址被修改
3 删除收货地址 点击已有收货地址删除按钮 地被删除
4 收藏 点击收藏 按钮变为已收藏,个人中心中记录对应商品
5 取消收藏 点击已收藏 按钮变为收藏,个人中心删除对应商品
6 添加留言 输入留言后点击提交 后台查看到该留言

6.3.4 交易模块是否可以正常使用
(1) 加入购物车的测试
(2) 从购物车删除的测试
(3) 修改购物车中商品数量的测试
(4) 创建订单的测试
(5) 删除订单的测试
(6) 浏览全部订单的测试
(7) 支付成功的测试
(8) 未完成支付的测试
表6-4 交易模块测试用例
序号 测试内容 输入内容 期望结果
1 加入购物车 点击加入购物车 购物车中加入该商品
2 从购物车删除 点击单品后的红叉 购物车中该商品被删除
3 创建订单 点击去结算 转入支付页面,订单创建
4 删除订单 点击订单后的删除 订单被删除
5 浏览订单 点击订单详情 显示全部订单
6 支付成功 进行付款 订单状态变为已完成
7 支付失败 不进行支付 订单状态为待支付

6.3.5 后台模块是否可以正常使用
(1) 管理员添加管理员的测试
(2) 管理员修改用户信息的测试
(3) 管理员删除用户的测试
(4) 管理员添加商品的测试
(5) 管理员修改商品信息的测试
(6) 管理员删除商品的测试
(7) 管理员通过过滤器过滤商品的测试
(8) 管理员对增加商品类别的测试
(9) 管理员查看用户留言的测试
表6-5 后台模块测试用例
序号 测试内容 输入内容 期望结果
1 添加商品 输入信息,点击添加 商品被添加并显示在列表
2 查看留言 点击留言面板 显示全部留言
3 增加商品类别 输入类别信息,点击添加 类别信息被添加
4 删除用户 点击用户后的删除按钮 用户被删除
6.4 测试结果
经测试系统的稳定性和健壮性比较好,可以连续安全运行很长时间,系统的各项功能符合设计的预期。本系统较为突出的优点是用户界面友好,简单,美观,使用起来简单方便,使用者普遍满意度比较高。实际使用过程中个功能模块都获取到了准确的数据,各项网页输入的数据均有验证输入格式,并且如果用户输入格式有问题系统会比较友好的输出错误提示信息。
对于系统的一些非性能性要求,测试时也进行了验证。B/S结构的系统框架,用户所使用的设备的性能没有特别要求,甚至于手机端也可以通过手机浏览器访问系统。浏览器兼容方面,系统需要兼容ie8及其以上版本浏览器和最新的Google Chrome, Firefox等主流浏览器。由于本系统是基于功Vue和Django应用程序而开发,所以系统具有较强适应能力,可以为用户新的需求单独设计新的功Django App,以此来达到满足用户新需求,达到较强的可扩展性,可以适应一些开发计划的变化。
总体来说基本上完成了设计说明书上的设计要求,系统各项功能正常使用,在另一些关乎用户体验的方面需要完善一下。

第七章 全文总结

本文实现了一个基于Django的电子产品销售平台,Django使用Python作为基础语言,是Web开发中的后起之秀。本文首先介绍了电商网站在国内外的现状,电商网站在我国和国外已经是一个比较成熟的技术,但国外更为激进,更善于使用较为新的技术去省去建站复杂的工序,而国内则追求稳定,相对使用更为成熟的技术。
紧接着,本文对电子产品销售平台项目的技术选型进行了分析,本系统使用Django和配套Django使用的REST框架Django REST framework,分析了本系统为什么使用这样的框架进行开发。本系统使用前后端分离的开发模式,前端使用Vue进行开发,后端使用Django和DRF,在项目两方面完成时通过API使两端无缝连接。本系统使用MySQL这样一个轻量级数据库作为项目的数据库,使用Redis这样一个开源分布式内存数据库作为缓存数据库,提升了整个系统的性能。
接下来,本文进行了项目的可行性分析和需求分析,确定了本项目这个电子产品销售平台具体需要实现哪些广泛意义上的功能和性能需求。
在完成需求分析之后,进入了总体设计阶段。在这个阶段本文划分了项目的各个模块以及各个模块的职责所在,从宏观上将项目定型。本文还对数据库进行了初步设计,在接下来的详细设计中,如果有哪些不合适或者需要修改的地方,还需要进行进一步修改。
然后进入了项目的详细设计阶段,本文对总体设计阶段的几个模块——用户模块、商品模块、用户操作模块、交易模块、后台模块和其他模块进行了详细的设计,详细设计中包含着对模块各个功能的实现,以及这个功能的逻辑实现。本文对其中将为重要的功能进行了详细的剖析,比如用户验证的JWT以及交易时使用的支付宝支付功能等。在这个阶段,本文在商品模块中详细地对DRF中的View、ViewSet、Mixin、Serializers、Filter以及Siginals进行了分析。
最后进行了项目测试和部署,系统使用黑盒测试和白盒测试对项目的各项功能进行测试,以确保各项功能的正常运行。
本项目还有许多不够完善的地方,希望在以后的学习和工作中能够学习到更多知识,对该项目的一些不完善的地方进行补充。

参考文献

[1] 肖旻,陈行.基于Python语言编程特点及应用之探讨[J].电脑知识与技术,2014,10(34):8177-8178.
[2] 王冉阳.基于Django和Python的Web开发[J].电脑编程技巧与维护,2009(02):56-58.
[3] 李延. 基于REST架构的web服务技术研究[D].武汉理工大学,2013.
[4] 贺海梁,袁玉宇.基于REST的面向资源Web应用架构参考模型[J].软件,2012,33(11):59-63.
[5] 陈磊.REST的真谛[J].软件世界,2007(17):38-39.
[6] 魏成坤,刘向东,石兆军.基于OAuth2.0的认证授权技术研究[J].信息网络安全,2016(09):6-11.
[7] 刘镝,张智江,张尼.基于国内开放平台的Oauth认证框架研究[J].信息通信技术,2011,5(06):43-46.
[8] 范展源,罗福强.JWT认证技术及其在WEB中的应用[J].数字技术与应用,2016(02):114.
[9] 王丹,赵文兵,丁治明.Web应用常见注入式安全漏洞检测关键技术综述[J].北京工业大学学报,2016,42(12):62-72.
[10] RESTful Web API中文版[M]. 电子工业出版社 , 理查德森, 2014
[11] 郭栋,王伟,曾国荪.一种基于微服务架构的新型云件PaaS平台[J].信息网络安全,2015(11):15-20.
[12] GOGOE VIDJINNAGNI PAUL. 基于JSON Web签名的Web安全研究[D].武汉理工大学,2015.
[13] Lorne D. Booker,Nick Bontis,Alexander Serenko. Evidence‐Based Management and Academic Research Relevance[J]. Knowledge and Process Management,2012,19(3).
[14] Krishnan S. Anand,Ravi Aron. Group Buying on the Web: A Comparison of Price-Discovery Mechanisms[J]. Management Science,2003,49(11).
[15] Ahmed E. Hassan,Richard C. Holt. A lightweight approach for migrating web frameworks[J]. Information and Software Technology,2004,47(8).

致谢

转眼,大学本科的生活就要结束了,这四年来的学习和生活让我受益良多,经过将近半年的努力,我的大学本科毕业论文终于将要完成。这半年来,我获得了很多人的无私关怀与帮助。对此,我想向他们表示我诚挚的谢意。
首先,我要感谢我的论文负责老师相洁老师和范烨老师,范烨老师治学态度严谨求实,特别敬业。相洁老师在收集资料、论文选题,亦或是系统实现、论文撰写阶段,他都给予我莫大的关注和鼓励,给予了我系统框架方面的指导和系统设计实现技术方面的帮助。
其次,我还要感谢一直关心我和支持我的家人和朋友,没有他们在背后默默支持,我也无法顺利完成学业。特别是我的家人,给予了我很大的物质支持和精神支持,我在这里再一次向他们表示感谢。
最后我还要感谢我同组的同学,在我遇到有不懂的难题时,我的同门师兄弟及时的给予了我莫大的帮助。他们的鼓励和支持是我不断挑战自己,不断进步的动力,在这里我表示再一次的感谢。

外文原文
Authentication and Authorization of End User in Microservice Architecture
Abstract. As the market and business continues to expand; the traditional single monolithic architecture is facing more and more challenges. The development of cloud computing and container technology promote microservice architecture became more popular. While the low coupling, fine granularity, scalability, flexibility and independence of the microservice architecture bring convenience, the inherent complexity of the distributed system make the security of microservice architecture important and difficult. This paper aims to study the authentication and authorization of the end user under the microservice architecture. By comparing with the traditional measures and researching on existing technology, this paper put forward a set of authentication and authorization strategies suitable for microservice architecture, such as distributed session, SSO solutions, client-side JSON web token and JWT + API Gateway, and summarize the advantages and disadvantages of each method.

  1. Introduction
    For any mature system, a complete authentication and authorization of end user module is essential. It protects the boundaries of the system. what is the difference between authentication and authorization? Authentication is the process of verifying who you are. When you log into a PC with a user name and password you are authenticating. Authorization is the process of verifying that you have access to something. For example, some resources only allow the administrator to view but not the ordinary users.

1.1 Auth in Monolithic Application
In a traditional monolithic architecture project, User’s requests are handled within a single process in the backend. Put a filter in the system boundary to verify the identity and access, and determine the response or distribution of the request. As HTTP is a stateless protocol, it is usually based on the session that the server generates for the client to manage the user’s status.

Here is the session control process:

  1. The client provides authentication credentials.
  2. The server valid the credentials :Re-certification if verification failed,and Generate session if verification successful.
  3. The client requests the resource with session
  4. The server gets session of the user by session and response resource if the user has access
    The advantages of the session-based system are simple easy to implement and exposed less entrance to the system. But there are many challenges as well. The server needs to save the session in memory, which may cause high memory usage and reduce performance. On the other hand, the authentication feature and other system’s features mix together, high coupled results in reducing scalability and flexibility of the system. As the traffic increases, the system needs to deploy multiple nodes to load balance. Share a session in multiple nodes is a problem. Besides, the session-based system uses cookie at most time, so it should deal with some cookie-based attacks from client.

1.2 Distributed Session Management
As the feature of the system becomes more complex and the number of user increases, application deployed in single machine is unable to handle the pressure. From a single node to a cluster, the multiple nodes must share a session when they use session-based auth. There are some solutions about distributed like sticky session, session replication, session centralized management and so on.
Sticky session ensures that all the subsequent request will be send to the server who handled the first request corresponding to that request. Session replication means each server saves session data, and synchronizes through the network. So it will be affected by the network situation. Centralized management adds a specific server to manage session. Every service request session from the session server.
In any case, the distributed session is complex to design and difficult to maintain.

1.3 Token-Based Auth
The token-based authentication system allows users to enter their username and password in order to obtain a token which allows them to fetch a specific resource without using their username and password. Once their token has been obtained, the user can offer the token which offers access to a specific resource for a time period to the remote site.
By using token, there is no need to keep a session store; the token is a self-contained entity that conveys all the user information. In addition, the token is stateless so it’s easy to deal with server side scalability. The token is adapted to different client like browser and mobile. In the meantime, it can effectively avoid CORS and CSRF attack.

Figure 1 Token-based auth
在这里插入图片描述

  1. Auth Challenges in Microservice
    2.1 What is Microservice Architecture
    While there is no standard formal definition of micro service. In the words of Fowler & Lewis, a micro service is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. We like microservice for a bunch of reasons.Composing functionality,Self-contained services“Bounded context”,Containers,Kubernetes, Mesos,Not rely on HTTP sessions.
    2.2 Auth Service in Microservice
    There is a simple way to deal with the authentication and authorization in microservice that is to imitate the way of monolithic structure. Each service uses own database or shared database that stores credential data and implements own function to verify the the user independently. This way is easy to understand but has several deficiencies. First, when joining a new service in the system every time, we must re-implement the auth function for the new service. Second, when we use a single shared user database, it will have single point of failure problem. Also, this way is against to the single responsibility principle of micro service.
    In order to improve the above architecture, and to adapt to the microservice design principles. The auth service focuses on the authentication and authorization of the user. Other services authenticate the user’s identity and authority by interacting with the auth service. As a result, each service is focused on its own business, while improving the scalability and loosely coupled of the system.
    2.3 What do We Want
    Security: Security means that users are secured identified. The sharing of information within microservice could be a concern depending on how we actually deploy the system.
    Stateless services: We want stateless services with multiple instances in running because we are deploying in the cloud and anything can fail at any time.
    No single point of failure: As mentioned in the previous section, shared databases solution has the problem of SPOF.
    No inherent bottlenecks, Ability to log out, Integration with microservices, Simple to implement.

  2. Using Distribute Session
    In the first part we mentioned the traditional session-based authentication and authorization method and distributed session management. It is well known that microservice is distributed architecture. This section explains how to implement the distribution session solution in micro services.
    User requests access for some services, service redirects to auth server if there are no sessionid in the request. Auth requires the user to provide credentials, generates session for the user and store it in session store, then return sessionid to user. User re-requests access for services with sessionid. Services query session store with sessionid to get user’s status and make an appropriate response.
    Distribution session solution is feasible but is little complicated to implement as mentioned in first part. The next will introduce several token-based solutions about authentication and authorization in microservice.

  3. Using JSON Web Token
    4.1. JSON Web Token(JWT)
    JSON Web Token (JWT)is an information transfer protocol based on an open standard (RFC 7519) that consists of Header, Payload and Signature.

  4. Header: The header typically consists of two parts, one of them is token type, namely JWT, and another one is hashing algorithm used, such as HMAC SHA256 or RSA. The format of JSON
    header is as follow:
    {“alg”: “HS256”,
    “typ”: “JWT”}
    Then, this JSON is Base64Url encoded to form the first part of the JWT.

  5. Payload: Second part of token which consists of claim. Claim is the statement about an entity
    (usually users) and supplementary metadata.
    {“exp”: 1494428560,
    “name”: “John Doe”,
    “authorities”: [
    “ROLE_ADMIN”,
    “VIEW_RESOURCE”]
    …}
    The payload is then Base64Url encoded to form the second part of the JSON Web Token.

  6. Signature: This section is to signature the Header and Payload sections according to the algorithm specified in the Header section to prevent modification maliciously.
    For example, if you want to use the HMAC SHA256 algorithm, the signature will be created in the following way:
    HMACSHA256(
    base64UrlEncode(header) + “.” +
    base64UrlEncode(payload),
    secret)

4.2. Client-Side JWT Solution
When the user sends request to the resources server, the resource server redirect to auth server if there is no token or token is expired. User sends credentials to auth server. Auth server valid the credentials and generate JWT token for the user which contains user’s authority, expired time and so on. User re- requests resource server with JWT token this time. Resource server decodes the token and verifies user permission. Then make an appropriate response. Due to the signature, JWT can validation itself rather than request auth server. So this will reduce auth server pressure and chatty traffic. Another benefit is that system is easy to expand for the stateless of the token.
It’s a good solution so far. But how to logout. We know that JWT contains an expire time. Before the expire time, the token is always valid. When we want logout, the simple way is to remove the token from client side. But this is not safe, because the server side has not been aware. Another way is periodically check with auth service in the server side. This will cause additional network traffic.

  1. Using JWT+API Gateway
    In the microservice architecture, we can found there is an API gateway in the boundary of the system. An API gateway provides a single, unified API entry point across one or more internal APIs.
    The most important thing about logout is to invalidate the token on server side. As every request will go through API gateway, so we can use it to solve the logout problem. The key step is that API gateway translates JWT to an opaque token.
    Most steps of JWT+API gateway solution are the same as the solution in V. The only difference is that the gateway has transferred the token. Auth server generate a JSON web token for the user and deliver it to gateway. Gateway transfer the token to an opaque token that only itself can resolve. Then store the relationship of the opaque token and origin token in a token store and send the opaque token to client. When the gateway receives an opaque token from client, transfer it to the origin token, then forward the origin token to the resource service. With that transfer step, we can remove the token relationship in the API gateway token store, So the client need to re-request a token. This is a brilliant solution for the logout issue.
    By comparing with the traditional monolithic architecture, this paper explores several solutions of authentication and authorization in microservice architecture. Distributed session solution is similar to the traditional auth way but its complex to implement and maintain. SSO server will cause SPOF and traffic problem. Using API gateway to improve the client-side JWT solution can solve the logout problem. So there is no absolute right solution but only suitable options for your system.

中文翻译
微服务体系结构中最终用户的认证和授权

摘要. 随着市场和业务的不断扩大, 传统的单一整体架构面临着越来越多的挑战。云计算和容器技术的发展促进了微服务架构变得越来越流行。尽管微服务体系结构的低耦合,细粒度,可伸缩性,灵活性和独立性带来了方便,但分布式系统的固有复杂性使得微服务体系结构的安全性变得重要和困难。本文旨在研究在微服务架构下最终用户的认证和授权。通过与传统方法的比较和现有技术的研究,提出了一套适用于微服务体系结构的认证和授权策略,如分布式会话,SSO解决方案,客户端JSON Web Token和JWT + API Gateway,并总结每种方法的优缺点。

  1. 介绍
    对于任何成熟的系统,最终用户模块的完整认证和授权至关重要。它保护系统的边界。认证和授权有什么区别? 验证是验证你是谁的过程。当您使用用户名和密码登录到PC时,您正在进行身份验证。授权是验证您有权访问某个内容的过程。例如,一些资源只允许管理员查看,而不是普通用户。
    1.1 单片应用程序中的验证
    在传统的单一体系结构项目中,用户的请求在后端的单个进程中处理。在系统边界放置一个过滤器来验证身份和访问权限,并确定请求的响应或分发。由于HTTP是无状态协议,它通常基于服务器为客户端生成的会话来管理用户的状态。
    这是会话控制过程:
  2. 客户端提供认证凭证。
  3. 服务器验证凭据:验证失败时重新认证;如果验证成功,则生成会话。
  4. 客户端通过会话请求资源
  5. 如果用户有权访问,则服务器通过会话和响应资源获取用户会话
    基于会话的系统的优点很容易实现,并且对系统的入口更少。但也有许多挑战。服务器需要将会话保存在内存中,这可能会导致较高的内存使用量并降低性能。另一方面,身份验证功能和其他系统的功能混合在一起,耦合度高,降低了系统的可扩展性和灵活性。随着流量的增加,系统需要部署多个节点才能实现负载均衡。在多个节点中共享会话是一个问题。此外,基于会话的系统大多数时候都使用cookie,所以它应该处理来自客户端的一些基于cookie的攻击。

1.2 分布式会话管理
随着系统功能变得越来越复杂,用户数量增加,部署在单机上的应用程序无法应对压力。从单个节点到群集,多个节点在使用基于会话的身份验证时必须共享一个会话。关于分布式粘滞会话,会话复制,会话集中管理等方面存在一些解决方案。
粘滞会话确保所有后续请求都将发送给处理与该请求相对应的第一个请求的服务器。会话复制意味着每个服务器都保存会话数据,并通过网络进行同步。所以它会受到网络情况的影响。集中管理添加了一个特定的服务器来管理会话。来自会话服务器的每个服务请求会话。
无论如何,分布式会话设计复杂且难以维护。

1.3 Token-Based Auth
基于令牌的认证系统允许用户输入他们的用户名和密码,以获得令牌,使他们能够在不使用用户名和密码的情况下获取特定资源。一旦获得他们的令牌,用户可以提供令牌访问特定资源一段时间到远程站点。
通过使用令牌,不需要保留会话存储; 令牌是传递所有用户信息的独立实体。另外,令牌是无状态的,所以处理服务器端可扩展性很容易。令牌适用于浏览器和移动设备等不同的客户端。同时,它可以有效避免CORS和CSRF攻击。

在这里插入图片描述

图1 Token-based 认证

  1. 微服务中的身份验证挑战
    2.1 什么是微服务体系结构
    虽然没有标准的微服务正式定义。用Fowler&Lewis的话来说,微服务是一种将单个应用程序作为一套小型服务开发的方法,每个小应用程序都在自己的进程中运行,并与轻量级机制(通常是HTTP资源API)进行通信。我们喜欢微服务出于一系列原因有撰写功能、独立服务、“有界的上下文”、独立缩放、独立部署、本地化故障、倾向无状态、不依赖于HTTP会话。
    2.2微服务中的身份验证服务
    有一种简单的方法来处理微服务中的认证和授权,即模仿单片结构的方式。每个服务使用自己的数据库或共享数据库来存储凭证数据并实现自己的功能来独立验证用户。这种方式很容易理解,但有几个缺陷。首先,每次在系统中加入新服务时,都必须重新实现新服务的身份验证功能。其次,当我们使用单个共享用户数据库时,它将具有单点故障问题。而且,这种方式违背了微服务的单一责任原则。
    为了改进上述体系结构并适应微服务设计原则,我们在系统中放置了一个单独的auth服务, auth服务侧重于用户的身份验证和授权。其他服务通过与auth服务交互来验证用户的身份和权限。因此,每项服务都专注于自己的业务,同时提高系统的可扩展性和松耦合性。

2.3 我们想要什么
安全:安全意味着用户有安全的身份。根据我们实际部署系统的方式,微服务中的信息共享可能是一个问题。
无状态服务:我们希望运行多个实例的无状态服务,因为我们正在部署云,任何事情都可能随时失败。
没有单点故障:如前一节所述,共享数据库解决方案存在SPOF问题。
没有固有的瓶颈、能够注销、与微服务集成、易于实施。

  1. 使用分发会话
    在第一部分中,我们提到了传统的基于会话的认证和授权方法以及分布式会话管理。众所周知,微服务是分布式体系结构。本节介绍如何在微服务中实施分发会话解决方案。

用户请求访问某些服务,如果请求中没有sessionid,服务将重定向到auth服务器。Auth要求用户提供凭证,为用户生成会话并将其存储在会话存储中,然后将sessionid返回给用户。用户使用sessionid重新请求访问服务。使用sessionid查询会话存储以获取用户状态并进行适当的响应。
如第一部分所述,分发会话解决方案是可行的,但实现起来并不复杂。接下来将介绍几种基于令牌的解决方案,包括微服务中的认证和授权。

  1. 使用 JSON Web Token
    4.1. JSON Web Token(JWT)
    JSON Web Token(JWT)是基于开放标准(RFC 7519)的信息传输协议,由标头,有效载荷和签名组成。

  2. 头部:头部通常由两部分组成,其中一部分是令牌类型,即JWT,另一部分是使用的哈希算法,例如HMAC SHA256或RSA。JSON的格式
    头部格式如下:
    { “alg”: “HS256”,
    “typ”: “JWT”}
    然后,这个JSON被Base64Url编码,形成JWT的第一部分。

  3. 有效负载:由索赔组成的令牌的第二部分。声明是关于实体的声明
    (通常是用户)和补充元数据。
    { “exp”: 1494428560,
    “name”: “John Doe”,
    “authorities”: [
    “ROLE_ADMIN”,
    “VIEW_RESOURCE”]
    …}
    然后将有效载荷Base64Url进行编码以形成JSON Web令牌的第二部分。

  4. 签名:本节将根据标题部分中指定的算法对Header和Payload部分进行签名,以防止恶意修改。
    例如,如果您想使用HMAC SHA256算法,则签名将按以下方式创建:
    HMACSHA256(
    base64UrlEncode(header) + “.” +
    base64UrlEncode(payload),
    secret)

4.2. 客户端JWT解决方案
当用户向资源服务器发送请求时,如果没有令牌或令牌过期,资源服务器将重定向到auth服务器。用户将凭证发送给auth服务器。Auth服务器验证凭证并为包含用户权限,过期时间等的用户生成JWT令牌。用户此次用JWT令牌重新请求资源服务器。资源服务器解码令牌并验证用户权限。然后做出适当的回应。由于签名,JWT可以验证自己,而不是请求auth服务器。所以这将减少auth服务器压力和流量。另一个好处是系统很容易扩展为令牌无状态。
迄今为止这是一个很好的解决方案。但是如何注销。我们知道JWT包含到期时间。在到期之前,令牌总是有效的。当我们想要注销时,最简单的方法就是从客户端删除令牌。但这不是安全的,因为服务器端并不知道。

  1. 使用JWT + API网关
    在微服务体系结构中,我们可以发现系统边界存在一个API网关。API网关跨一个或多个内部API提供单一的统一API入口点。
    注销最重要的是使服务器端的令牌无效。由于每个请求都会经过API网关,因此我们可以使用它来解决注销问题。关键的一步是API网关将JWT转换为不透明的令牌。
    JWT + API网关解决方案的大多数步骤与V中的解决方案相同。唯一的区别是网关已传输令牌。Auth服务器为用户生成一个JSON Web令牌并将其传递给网关。网关将令牌转移到只有自己可以解决的不透明令牌。然后将不透明令牌和原始令牌的关系存储在令牌存储中,并将不透明令牌发送给客户端。当网关收到来自客户端的不透明令牌时,将其转移给原始令牌,然后将原始令牌转发给资源服务。通过该转移步骤,我们可以删除API网关令牌存储中的令牌关系,因此客户端需要重新请求令牌。这是注销问题的绝妙解决方案。
    通过与传统的单片架构进行比较,本文探讨了微服务架构中的几种认证和授权解决方案。分布式会话解决方案与传统的认证方式类似,但其实现和维护的复杂性较高。SSO服务器将导致SPOF和流量问题。使用API网关改进客户端JWT解决方案可以解决注销问题。所以没有绝对正确的解决方案,但只有适合您的系统的选项。
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值