十七、首页和全局404、500页面配置
1、首页配置
首页页面轮播课程需要在课程的model中添加is_banner字段,说明是否是轮播课程:
1 class Course(models.Model): 2 """课程""" 3 DEGREE_CHOICES = ( 4 ('cj', '初级'), 5 ('zj', '中级'), 6 ('gj', '高级') 7 ) 8 9 name = models.CharField('课程名', max_length=50) 10 desc = models.CharField('课程描述', max_length=300) 11 detail = models.TextField('课程详情') 12 degree = models.CharField('课程难度', choices=DEGREE_CHOICES, max_length=2) 13 learn_times = models.IntegerField('学习时长(分钟数)', default=0) 14 students = models.IntegerField('学习人数', default=0) 15 fav_nums = models.IntegerField('收藏人数', default=0) 16 click_nums = models.IntegerField('点击数', default=0) 17 image = models.ImageField('封面图', upload_to='courses/%Y/%m', max_length=100) 18 course_org = models.ForeignKey(CourseOrg, verbose_name='所属机构', on_delete=models.CASCADE, null=True, blank=True) 19 category = models.CharField('课程类别', max_length=20, default='') 20 tag = models.CharField('标签', max_length=10, default='') 21 teacher = models.ForeignKey(Teacher, verbose_name='机构讲师', on_delete=models.CASCADE, null=True, blank=True) 22 courseneed_know = models.CharField('课程须知', max_length=300, default='') 23 teacher_tellyou = models.CharField('老师告诉你', max_length=300, default='') 24 is_banner = models.BooleanField('是否轮播', default=False) 25 add_time = models.DateTimeField('添加时间', default=datetime.now) 26 27 class Meta: 28 verbose_name = '课程' 29 verbose_name_plural = verbose_name 30 31 # 获取章节数 32 def get_zj_nums(self): 33 return self.lesson_set.all().count() 34 35 # 获取学习用户 36 def get_learn_users(self): 37 return self.usercourse_set.all()[:5] 38 39 # 获取章节 40 def get_course_lesson(self): 41 return self.lesson_set.all() 42 43 def __str__(self): 44 return self.name
机构显示需要机构标签,在机构的model中添加tag字段:
1 class CourseOrg(models.Model): 2 """课程机构""" 3 CATEGORY_CHOICES = ( 4 ('pxjg', '培训机构'), 5 ('gx', '高校'), 6 ('gr', '个人') 7 ) 8 name = models.CharField('机构名称', max_length=50) 9 category = models.CharField('机构类别', max_length=20, choices=CATEGORY_CHOICES, default='pxjg') 10 desc = models.TextField('机构描述') 11 students = models.IntegerField('学习人数', default=0) 12 course_nums = models.IntegerField('课程数', default=0) 13 click_nums = models.IntegerField('点击数', default=0) 14 fav_nums = models.IntegerField('收藏数', default=0) 15 image = models.ImageField('封面图', upload_to='org/%Y/%m', max_length=100) 16 address = models.CharField('地址', max_length=150) 17 city = models.ForeignKey(CityDict, verbose_name='所在城市', on_delete=models.CASCADE) 18 tag = models.CharField('机构标签', max_length=10, default='全国知名') 19 add_time = models.DateTimeField('添加时间', default=datetime.now) 20 21 class Meta: 22 verbose_name = '课程机构' 23 verbose_name_plural = verbose_name 24 25 # 获取教师数量 26 def get_teacher_nums(self): 27 return self.teacher_set.all().count() 28 29 def __str__(self): 30 return self.name
迁移数据库。
首页的数据都是需要从后台获取的,先编写首页的接口:
1 class IndexView(View): 2 """首页""" 3 def get(self, request): 4 # 轮播图 5 all_banners = Banner.objects.all().order_by('index') 6 7 # 课程 8 courses = Course.objects.filter(is_banner=False)[:6] 9 # 轮播课程 10 banner_courses = Course.objects.filter(is_banner=True)[:3] 11 12 # 机构 13 orgs = CourseOrg.objects.all()[:15] 14 return render(request, 'index.html', { 15 'all_banners': all_banners, 16 'courses': courses, 17 'banner_courses': banner_courses, 18 'orgs': orgs 19 })
然后修改首页的url:
1 urlpatterns = [ 2 path('', IndexView.as_view(), name='index'), # 首页 3 ]
修改前端首页页面中显示数据的代码:
1 {% extends 'base.html' %} 2 3 {% load staticfiles %} 4 5 {% block title %} 6 知能网 - 首页 7 {% endblock %} 8 9 {% block content %} 10 <div class="banner"> 11 <div class="wp"> 12 <div class="fl"> 13 <div class="imgslide"> 14 <ul class="imgs"> 15 {# 遍历显示轮播图#} 16 {% for banner in all_banners %} 17 <li> 18 <a href="{{ banner.url }}"> 19 <img width="1200" height="478" src="{{ MEDIA_URL }}{{ banner.image }}" /> 20 a> 21 li> 22 {% endfor %} 23 ul> 24 div> 25 <div class="unslider-arrow prev">div> 26 <div class="unslider-arrow next">div> 27 div> 28 29 div> 30 31 32 div> 33 34 35 <section> 36 <div class="wp"> 37 <ul class="feature"> 38 <li class="feature1"> 39 <img class="pic" src="/static/images/feature1.png"/> 40 <p class="center">专业权威p> 41 li> 42 <li class="feature2"> 43 <img class="pic" src="/static/images/feature2.png"/> 44 <p class="center">课程最新p> 45 li> 46 <li class="feature3"> 47 <img class="pic" src="/static/images/feature3.png"/> 48 <p class="center">名师授课p> 49 li> 50 <li class="feature4"> 51 <img class="pic" src="/static/images/feature4.png"/> 52 <p class="center">数据真实p> 53 li> 54 ul> 55 div> 56 section> 57 58 59 <section> 60 <div class="module"> 61 <div class="wp"> 62 <h1>公开课程h1> 63 <div class="module1 eachmod"> 64 <div class="module1_1 left"> 65 <img width="228" height="614" src="/static/images/module1_1.jpg"/> 66 <p class="fisrt_word">名师授课<br/>专业权威p> 67 <a class="more" href="{% url 'course:course_list' %}">查看更多课程 >a> 68 div> 69 <div class="right group_list"> 70 <div class="module1_2 box"> 71 <div class="imgslide2"> 72 <ul class="imgs"> 73 {# 遍历显示轮播课程#} 74 {% for banner_course in banner_courses %} 75 <li> 76 <a href="{% url 'course:course_detail' banner_course.id %}"> 77 <img width="470" height="300" src="{{ MEDIA_URL }}{{ banner_course.image }}" /> 78 a> 79 li> 80 {% endfor %} 81 ul> 82 div> 83 <div class="unslider-arrow2 prev">div> 84 <div class="unslider-arrow2 next">div> 85 div> 86 {# 遍历显示课程#} 87 {% for course in courses %} 88 <div class="module1_{{ forloop.counter|add:2 }} box"> 89 <a href="{% url 'course:course_detail' course.id %}"> 90 <img width="233" height="190" src="{{ MEDIA_URL }}{{ course.image }}"/> 91 a> 92 <div class="des"> 93 <a href="{% url 'course:course_detail' course.id %}"> 94 <h2 title="{{ course.name }}">{{ course.name }}h2> 95 a> 96 <span class="fl">难度:<i class="key">{{ course.get_degree_display }}i>span> 97 <span class="fr">学习人数:{{ course.students }}span> 98 div> 99 <div class="bottom"> 100 <span class="fl" title="慕课网">{{ course.course_org.name }}span> 101 <span class="star fr">{{ course.fav_nums }}span> 102 div> 103 div> 104 {% endfor %} 105 div> 106 div> 107 div> 108 div> 109 section> 110 <section> 111 <div class="module greybg"> 112 <div class="wp"> 113 <h1>课程机构h1> 114 <div class="module3 eachmod"> 115 <div class="module3_1 left"> 116 <img width="228" height="463" src="/static/images/module3_1.jpg"/> 117 <p class="fisrt_word">名校来袭<br/>权威认证p> 118 <a class="more" href="{% url 'org:org_list' %}">查看更多机构 >a> 119 div> 120 <div class="right"> 121 <ul> 122 {# 遍历显示机构#} 123 {% for org in orgs %} 124 <li class="{% if forloop.counter|divisibleby:5 %}five{% endif %}"> 125 <a href="{% url 'org:org_home' org.id %}"> 126 <div class="company"> 127 <img width="184" height="100" src="{{ MEDIA_URL }}{{ org.image }}"/> 128 <div class="score"> 129 <div class="circle"> 130 <h2>{{ org.tag }}h2> 131 div> 132 div> 133 div> 134 <p><span class="key" title="{{ org.name }}">{{ org.name }}span>p> 135 a> 136 li> 137 {% endfor %} 138 ul> 139 div> 140 div> 141 div> 142 div> 143 section> 144 {% endblock %} 145 146 {% block custom_js %} 147 <script type="text/javascript" src="/static/js/index.js">script> 148 {% endblock %}
我们在登出然后登陆之后发现轮播图的数据不见了,需要修改登录接口中render首页的代码:
1 class LoginView(View): 2 """登录""" 3 def get(self, request): 4 return render(request, 'login.html') 5 6 def post(self, request): 7 # form实例化 8 login_form = LoginForm(request.POST) 9 if login_form.is_valid(): 10 # form验证通过,获取用户提交的用户名和密码 11 user_name = request.POST.get('username', None) 12 pass_word = request.POST.get('password', None) 13 14 # 通过django的authenticate方法获取user对象,也就是验证用户是否存在 15 user = authenticate(username=user_name, password=pass_word) 16 17 if user is not None: 18 if user.is_active: 19 # 只有激活用户才能登录 20 # 验证通过,通过django的login方法去登录,然后返回首页 21 login(request, user) 22 return HttpResponseRedirect(reverse('index')) 23 else: 24 # 验证不通过,返回登录页面,并将错误信息返回去显示 25 return render(request, 'login.html', {'msg': '用户名或密码错误', 'login_form': login_form}) 26 else: 27 # 验证不通过,返回登录页面,并将错误信息返回去显示 28 return render(request, 'login.html', {'msg': '用户名或密码错误', 'login_form': login_form}) 29 else: 30 return render(request, 'login.html', {'login_form': login_form})
2、404、500页面配置
404、500页面,必须在setting.py中设置DEBUG = False:
1 DEBUG = False 2 3 ALLOWED_HOSTS = ['*'] 4 5 # 静态文件 6 STATIC_URL = '/static/' 7 STATIC_ROOT = os.path.join(BASE_DIR, 'static')
在设置DEBUG = False后,django就不会代管你的静态文件,所以要在urls.py文件中设置url处理静态文件的url:
1 urlpatterns = [ 2 re_path(r'^static/(?P.*) ', serve, {"document_root": STATIC_ROOT}), # 静态文件显示 3 ]
然后将404、500页面的html文件放到templates下即可配置完成。
十八、常见的web攻击与防护
1、sql注入攻击与防护
1.1 sql注入攻击的危害
- 非法读取,篡改,删除数据库中的数据
- 通过修改数据库来修改网页上的内容
- 盗取用户的各类敏感信息
- 注入木马等
1.2 SQL注入攻击原理
比如下面一段代码:
1 class LoginUnsafeView(View): 2 def get(self, request): 3 return render(request, "login.html", {}) 4 def post(self, request): 5 user_name = request.POST.get("username", "") 6 pass_word = request.POST.get("password", "") 7 8 import MySQLdb 9 conn = MySQLdb.connect(host='127.0.0.1', user='root', passwd='root', db='mxonline', charset='utf8') 10 cursor = conn.cursor() 11 sql_select = "select * from users_userprofile where email='{0}' and password='{1}'".format(user_name, pass_word) 12 13 result = cursor.execute(sql_select) 14 for row in cursor.fetchall(): 15 # 查询到用户 16 pass 17 print 'test'
用户在登录的时候用户名如果输入 ' OR 1=1, 密码随便输入,SQL语句就变成了这样:
select * from users_userprofile where email='' OR 1=1' and password='123',email='' or 1=1这条件就直接为True了,不用去执行后面的and语句了,那么是不是直接就将用户的信息拿到了呢,所以这种写法是非常危险的。
那么如何去防护呢?可以尽量不去使用SQL原生语句去获取数据,可以通过django的orm去查询数据,在登录的使用使用django的login函数,这个函数是安全的。
2、xss攻击与防护
2.1 xss跨站脚本攻击的危害
- 控制受害者机器向其他网站发起攻击,注入木马
- 盗取各类用户账号
- 非法转账等
2.2 xss攻击原理
黑客将一段带有js代码的url发送给用户,用户点击这个url后发送请求到服务器,服务器返回数据给受害者,然后浏览器执行这段js脚本将用户的cookie信息发送给黑客,然后黑客拿着用户的cookie伪装成用户向服务器发送请求,这时黑客就可以进行任何攻击或者其他操作。
2.3 xss防护
- 对用户输入的地方和变量都检查长度和对<>;'等字符做一些过滤
- 不要在cookie中泄露用户的隐私数据,如用户名和密码
- 将cookie和系统ip绑定来降低cookie泄露后的危险
- 尽量采用POST方法而非GET提交表单
3、csrf攻击与防护
2.1 csrf攻击的危害
- 以用户的名义去发送邮件
- 盗取账号
- 购买商品
- 虚拟货币转账
2.2 csrf攻击的原理
用户登录受信任的A服务器,服务器返回cookie,用户之后的每次请求都会带上cookie去访问A服务器,但是用户在同时的时候没有登出或者关闭A服务器的页面,直接去访问危险的B服务器,然后B服务器要求用户去访问A的一个url,这时信息就会泄露。
2.3 csrf攻击的防护
在每个form中加上{{ csrf_token }}即可