一、昨日回顾
1. 知识回顾
mvc的三大组件是什么?
M:model 模型
V:view 视图
C:Controller 控制器
mvc的三大组件分别代表什么意思?
M:专门用来操作数据表的
V:专门用来展示视图模板的
C:专门用来处理业务逻辑,比如说什么时候该调用模型操作数据,什么时候该调用视图展示模板,都是由控制器里来处理。
2. 昨日反馈
二、知识路径
web项目开发流程介绍
需求分析
功能分析
ER图设计
表设计
项目部署
项目实现
实现后台用户管理系统
封装验证码工具类
==目标:能够设计项目所需的表、能够部署自主框架、能够实现后台用户管理系统==
三、今日课程内容:博客项目
1. web项目开发流程介绍
1)制定计划:博客项目(blog31) 开发周期:4天
2)需求分析:功能分析 ER图设计
3)软件设计:项目环境 表设计
4)程序编写
5)软件测试
6)运行维护
2. 需求分析
功能分析
- 后台用户管理系统;
- 后台分类管理系统;
- 后台博文管理系统;
- 评论管理系统;
- 后台登陆和七天免登录;
- 前台首页;
- 前台注册和登陆功能;
- 前台博客文章详情页;
ER图设计
E-R图也称实体-联系图(Entity Relationship Diagram),提供了表示实体类型、属性和联系的方法,用来描述现实世界的概念模型。它是描述现实世界概念结构模型的有效方法。是表示概念模型的一种方式,用矩形表示实体型,矩形框内写明实体名;用椭圆表示实体的属性,并用无向边将其与相应的实体型连接起来;用菱形表示实体型之间的联系,在菱形框内写明联系名,并用无向边分别与有关实体型连接起来,同时在无向边旁标上联系的类型(1:1,1:n或m:n)。
专业的ER图工具:Viso和Power Designer
==实体== ==》表:矩形表示
==属性== ==》表字段:椭圆
==关系== ==》表与表的关系:菱形(1:1表示1对1关系;1:n表示1对多关系;m:n表示多对多关系)
博客项目的ER关系图如下:
3. 表设计
1 | drop database blog31; |
4. 项目部署
将code/mvc目录改名为blog31,
下图这个mvc目录:
改名为blog31:
配置一个基于域名虚拟主机,域名为www.blog31.com
在httpd-vhosts.conf文件中增加如下图所示的虚拟主机配置项:
修改hosts文件,
重启apache,测试访问效果:
访问效果为:
能够看到上图,则说明部署成功。
5. 项目实现
实现后台用户管理系统
实现列表页
将code/templates/admin目录中如下图所示的目录复制一份,
转移拷贝到code/blog31/app/admin/view目录中,
在blog31/app/admin/controller目录中创建一个名为UserController.class.php的文件,
在blog31/app/admin/controller/UserController.class.php中构建如下代码,展示三个页面,
测试访问后台用户管理系统列表页
进一步做调整,将后台模板使用到的资源文件全部转移进项目中,
在blog31/public目录中创建一个名为admin的目录,专门保存后台的资源文件,
复制code/templates/admin/libs目录下的所有文件,如下图所示,
转移拷贝到blog31/public/admin目录中,
调整blog31/app/admin/view/User/userIndex.html中引入资源文件的路径,
在blog31/conf/conf.php中增加本网站域名配置项,
然后在blog31/app/admin/view/User/userIndex.html使用配置项URL的值来引入配置文件,
上图中替换的是所有图片文件引入路径,截取不全,实际替换的内容以blog31/app/admin/view/User/userIndex.html中的为准。
再次测试访问后台新闻管理系统列表页,
在blog31/app/model目录中创建UserModel.class.php文件,
内容如下:
调整blog31/conf/conf.php中PDO配置项的默认选择的数据库名,
在blog31/app/admin/controller/UserController.class.php中showIndex方法里渲染模板之前调用模型查询数据,
然后在blog31/app/admin/view/User/userIndex.html中回显查询得到的用户数据,
测试访问后台用户管理系统列表页:
实现添加页
访问后台用户管理系统添加页,
调整blog31/app/admin/view/User/userAdd.html中引入资源文件的路径,将”../libs”全部替换为”{C(‘URL’)}/public/admin”,如下图所示,代码截取不全,以实际文件中的代码为准,
调整blog31/app/admin/view/User/userAdd.html中的form表单域各项的值,
在blog31/app/admin/controller/UserController.class.php中创建一个处理添加用户的方法,adh方法,
测试添加用户使用效果:
在添加表单页面构建数据,点击提交,
提交后的效果:效果为添加成功,说明功能实现OK。
实现编辑页
访问后台新闻管理系统编辑页:
修改blog31/app/admin/view/User/userIndex.html中编辑按钮的链接:
调整blog31/app/admin/view/User/userEdit.html中引入资源文件的路径,将”../libs”全部替换为”{C(‘URL’)}/public/admin”,如下图所示,代码截取不全,以实际文件中的代码为准,
调整blog31/app/admin/view/User/userEdit.html中form表单里的代码,
在blog31/app/admin/controller/UserController.class.php中创建updh方法,代码如下:
测试使用效果:
点击进入编辑也,如下图所示:
修改数据,
点击更新,
跳回到更新页面:
密码也是被改变了的,如下图所示:
说明编辑功能实现OK。
6. 全天总结
项目中的建表部分:
如果某字段是需要保存时间,一般在PHP的项目中给int数据类型;
如果某字段需要保存状态类型的值,使用tinyint会更加好;
如果某字段保存的数据,我们可以预期的很准确,比如说手机号码,通常就是11位,但是我们在项目中一般会采取宁大不小的原则,给11位以上的值,比如给varchar(15);
在构建字段的时候,我们一般都会给字段设置default默认值,如果不设置默认值,则今后这个字段无论是否需要添加数据,都必须指定上值。
项目开发部分:
我们使用框架进行开发,一般程序开发人员从模板入手,先尝试展示出模板页面的效果,然后再考虑是否需要调用模型操作数据库的问题,所以我们通常在MVC框架中,1)首先会将模板页面转移进项目目录;2)然后会创建控制器类,构建方法调用视图展示模板页面;3)调试模板页面展示的效果;4)以上都成功之后,如果没有模型则需要创建模型,如果有模型则直接去考虑调用模型操作数据;
四、昨日回顾
1. 知识回顾
建表注意事项:
如果在项目中要保存一个时间的数据,则我们一般选择使用int类型来保存;
如果在项目中要保存一个状态或类型这样的数据时,我们一般选择使用tinyint类型来保存;
如果在项目中能够明确要保存的数据需要占多少个字节时,我们一般采用的做法是宁大不小,比如,保存手机号码一般是11位,但是我们通常会选择给varchar(15)
如果在项目中构建字段时,通常我们会给字段指定一个默认值,一方面方便程序的默认操作,比如注册时,如果给用户类型指定了默认值为0表示普通会员,则构建注册功能的代码就无需给这个字段设置值,MYSQL会直接给这个字段用上默认值;另外一方面可以避免在添加数据操作时,如果没给这个字段指定值,将会报错的情况。
在项目代码开发中的基本流程是:
1)从模板文件入手,创建相关的控制器类和该类中的相应方法,渲染模板展示到浏览器;
2)在方法中渲染模板之前调用模型操作数据,比如我们之前开发的后台用户管理系统列表页,在渲染列表页模板之前,我们需要调用模型把列表页需要展示的数据查询出来;如果没有模型,则需要创建模型,如果有,则直接使用;
3)在方法中构建逻辑处理代码,生成相关功能;
2. 昨日反馈
五、知识路径
六、今日课程内容:博客项目
1. 封装验证码工具类
在blog31/plugins目录下创建CaptchaTool.class.php的文件,
构建的代码如下,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
namespace plugins;//创建一个 全局空间 下的 plugins空间
class CaptchaTool{
private $_w;//画布的宽度
private $_h;//画布的高度
private $_img;//画布资源
public function __construct($w=200, $h=80){
#初始化参数
$this->_w = $w;
$this->_h = $h;
$this->_img = imagecreatetruecolor($this->_w, $this->_h);//创建画布
#填充背景色
$color = $this->color();//获得随机色
imagefill($this->_img, 0, 0, $color);//填充背景色
#写字
$randStr = $this->randStr();//构建4个随机字
$color = $this->color();//获得随机色
$fontPath = PUBLIC_PATH . '/fonts/font1.ttf';//字体文件所在路径
$bx = $this->_w/4;//左下角起点x坐标
$by = $this->_h*3/4;//左下角起点y坐标
$fontSize = $this->_h *37/80;//字体大小,高度尺寸的37/80
imagettftext($this->_img, $fontSize, 0, $bx, $by, $color, $fontPath, $randStr);//写字
#设置干扰元素
//设置干扰点
$this->setPoint(180);
//设置干扰线
$this->setLine(8);
}
#画干扰线
private function setLine($num){
for($i=0; $i<$num; $i++ ){ //画$num个点
$color = $this->color();//获得随机色
$bx = mt_rand(0, $this->_w/2);//起点x坐标
$by = mt_rand(0, $this->_h);//起点y坐标
$ex = mt_rand($this->_w/2, $this->_w);//终点x坐标
$ey = mt_rand(0, $this->_h);//终点y坐标
imageline($this->_img, $bx, $by, $ex, $ey, $color);//画点
}
}
#画干扰点
private function setPoint($num){
for($i=0; $i<$num; $i++ ){ //画$num个点
$color = $this->color();//获得随机色
$bx = mt_rand(0, $this->_w);//起点x坐标
$by = mt_rand(0, $this->_h);//起点y坐标
$ex = mt_rand($bx-2, $bx+2);//终点x坐标
$ey = mt_rand($by-2, $by+2);//终点y坐标
imageline($this->_img, $bx, $by, $ex, $ey, $color);//画点
}
}
#构建随机字
private function randStr($num=4){
$mixedArr = array_merge(range('a', 'z'), range('A', 'Z'), range(0, 9));//构建随机字采集库
$str = '';
for($i=0; $i<$num; $i++ ){ //采集$num次
$key = mt_rand(0, count($mixedArr)-1);//取得当前随机字符对应的元素下标
$str .= $mixedArr[$key];//从字库中取得下标为$key的元素值拼接给$str
}
return $str;
}
#分配颜色
private function color($r='', $g='', $b=''){
//初始化三原色
$r = ($r==='') ? mt_rand(0, 255) : $r;//红
$g = ($g==='') ? mt_rand(0, 255) : $g;//绿
$b = ($b==='') ? mt_rand(0, 255) : $b;//蓝
//分配颜色
return imagecolorallocate($this->_img, $r, $g, $b);
}
#输出图像的方法
public function output(){
header('Content-type:image/jpeg');//指定一个响应协议项
imagejpeg($this->_img);//直接将图像输出到浏览器
}
}在blog31/public下创建fonts目录,
将F:\home\class\day15\source\font1.ttf复制转移到blog31/public/fonts目录中,
在blog31/conf/define.php中定义一个目录常量,
在blog31/core/App.class.php中的autoload方法中实现对工具类的自动加载,
测试使用工具类,
在blog31/app/admin/controller/UserController.class.php中构建测试方法test,代码如下:
访问test方法,
验证码制作成功。
2. 实现后台分类管理系统
在blog31/app/admin/controller中创建名为CatController.class.php文件,专门用于操作后台分类管理系统各程序,
代码如下:
实现列表页
在blog31/app/admin/controller/CatController.class.php中创建名为showList的方法,渲染后台分类列表页模板,
通过http://www.blog31.com/index.php?p=admin&m=cat&a=showIndex访问后台分类管理系统列表页,发现页面虽然能够展示,但是样式乱了,
于是进一步调整blog31/app/admin/view/Category/categoryIndex.html中引入资源文件的路径,将”../libs”全部替换为”{C(‘URL’)}/public/admin”,如下图所示,代码截取不全,以实际文件中的代码为准,
在blog31/app/model目录下创建CatModel.class.php的文件,
代码如下:
在blog31/app/admin/controller/CatController.class.php文件showList方法中构建如下代码,
调整模板文件回显查询的数据blog31/app/admin/view/Category/categoryIndex.html:
访问后台分类管理系统列表页:
我们进一步调整程序,将所有分类数据进行整理,
实现无限级递归分类
在blog31/app/admin/controller/CatController.class.php文件中创建两个方法,代码和作用如下图所示:
然后在blog31/app/admin/controller/CatController.class.php文件中的showList方法里调整获得所有分类数据的代码,如下图所示将蓝框中的代码改为红框中的代码:
调整模板页面中的代码,在输出分类名称之前还需要输出缩进符,
测试最终效果:
访问后台分类管理系统列表页
实现添加页
在blog31/app/controller/CatController.class.php中创建名为showAd的方法,渲染后台分类添加页模板,
通过http://www.blog31.com/index.php?p=admin&m=cat&a=showAd访问后台分类管理系统添加页,发现页面虽然能够展示,但是样式乱了,
于是进一步调整blog31/app/admin/view/Category/categoryAdd.html中引入资源文件的路径,将”../libs”全部替换为”{C(‘URL’)}/public/admin”,如下图所示,代码截取不全,以实际文件中的代码为准,
实现编辑页
调整blog31/app/admin/view/Category/categoryIndex.html中的编辑按钮链接,
在blog31/app/admin/controller/CatController.class.php中创建名为showUpd的方法,渲染后台分类编辑页模板,
通过从后台分类列表页点击编辑按钮访问后台分类管理系统编辑页,发现页面虽然能够展示,但是样式乱了,
于是进一步调整blog31/app/admin/view/Category/categoryEdit.html中引入资源文件的路径,将”../libs”全部替换为”{C(‘URL’)}/public/admin”,如下图所示,代码截取不全,以实际文件中的代码为准,
在blog31/app/admin/controller/CatController.class.php中的showUpd方法里构建相应的程序功能代码,如下图所示:
调整blog31/app/admin/view/Category/categoryEdit.html中需要回显数据的部分及表单域的相关属性值,
在blog31/app/admin/controller/CatController.class.php构建updh方法,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45//编辑处理方法
public function updh(){
//接收表单提交的数据
$id = $_GET['id'];
$name = trim($_POST['name']);//新的分类名称
$old_name = trim($_POST['old_name']);//老的分类名称
$parent_id = trim($_POST['parent_id']);//新的父级id
$old_parent_id = trim($_POST['old_parent_id']);//老的父级id
//检查是否有数据真的被修改了
$target = [];
if( $name!=$old_name && $name!='' ){//如果新的名称不等于老的名称并且新的名称不为空,则需要修改name值
$target[] = "name='{$name}'";
}
if( $parent_id!=$old_parent_id ){//如果新的父级id不等于老的父级id
$target[] = "parent_id={$parent_id}";
}
if( !empty($target) ){//如果$target不为空,说明有数据需要被更新
$strTarget = implode(', ', $target);// $strTarget="name='xxxx', parent_id=xxxx";
$sql = "update bg_category set {$strTarget} where id={$id}";
//调用模型执行更新操作
$model = \core\App::single('\model\CatModel');
$re = $model->setData($sql);
if( $re ){//更新成功
echo '嘿嘿嘿,运气不错,更新成功咯~';
}else{//更新失败
echo '哈哈哈,你更新失败了!';
}
}else{//否则给出提示没有数据被更新
echo '您当前还没有更新数据哟~';
}
//2秒之后跳转回编辑页
$url = C('URL') . '/index.php?p=admin&m=cat&a=showUpd&id='.$id;
header('Refresh:2; url='.$url);
exit;
}测试使用效果,效果OK,功能成功。
实现删除功能
3. 实现后台博文管理系统
在blog31/app/admin/controller中创建名为ArticleController.class.php文件,专门用于操作后台博文管理系统各程序,
代码如下:
实现添加页
在blog31/app/controller/ArticleController.class.php中创建名为showAd的方法,渲染后台博文添加页模板,
通过http://www.blog31.com/index.php?p=admin&m=article&a=showAd访问后台博文管理系统添加页,发现页面虽然能够展示,但是样式乱了,
于是进一步调整blog31/app/admin/view/Article/articleAdd.html中引入资源文件的路径,将”../libs”全部替换为”{C(‘URL’)}/public/admin”,如下图所示,代码截取不全,以实际文件中的代码为准,
调整无限级递归分类方法的归属
将blog31/app/admin/controller/CatController.class.php文件中如下图所示的方法剪切下来,
将剪切下来的代码全部粘贴到blog31/app/model/CatModel.class.php的类中,
再调整blog31/app/model/CatModel.class.php内的代码,如下图所示将蓝框中的代码改为红框中的代码:
将blog31/app/admin/controller/CatController.class.php中的showUpd和showList方法调用无限级递归分类方法的方式进行调整,如下图中将蓝框部分改为红框部分:
在blog31/app/controller/ArticleController.class.php中showAd方法里添加如下代码:
调整blog31/app/admin/view/Article/articleAdd.html中的表单各成员代码:
在blog31/app/model下创建ArticleModel.class.php的文件,如下图所示:
在blog31/app/controller/ArticleController.class.php中添加adh方法,处理添加功能,
测试使用效果:
在添加页构建如下内容,
点击添加按钮,效果为:
说明添加功能OK。
实现列表页
在blog31/app/admin/controller/ArticleController.class.php中创建名为showIndex的方法,渲染后台博文列表页模板,
通过http://www.blog31.com/index.php?p=admin&m=article&a=showIndex访问后台博文管理系统列表页,发现页面虽然能够展示,但是样式乱了,
于是进一步调整blog31/app/admin/view/Article/articleIndex.html中引入资源文件的路径,将”../libs”全部替换为”{C(‘URL’)}/public/admin”,如下图所示,代码截取不全,以实际文件中的代码为准,
在blog31/app/admin/controller/ArticleController.class.php中的showList方法里在渲染模板之前查询数据,
将查得的数据在blog31/app/admin/view/Article/articleIndex.html中回显,
测试访问效果:
实现列表页分页
将day16/source/page.php中的函数全部复制拷贝一份,
将上图拷贝的函数转移粘贴到blog31/core/Func.php中,
在blog31/app/admin/controller/ArticleController.class.php中的showList方法里计算分页所需各种参数,并且调整查询数据的SQL语句,如下图所示将蓝框中的代码改为红框中的代码:
调整blog31/app/admin/view/Article/articleIndex.html中展示分页HTML部分的代码,如下图所示将蓝框中的代码改为红框中的代码:
测试访问效果:
实现列表页序号
在blog31/app/admin/controller/ArticleController.class.php中showList方法里计算当前页第一条记录的序号,
然后在模板文件blog31/app/admin/view/Article/articleIndex.html中构建每一条记录的序号,
测试访问效果:
实现搜索功能
在blog31/app/admin/controller/ArticleController.class.php中的showList方法里查询出所有的管理员和所有的分类数据,
将上图中查得的数据回显到blog31/app/admin/view/Article/articleIndex.html中,
在blog31/app/admin/controller/ArticleController.class.php中的showList方法里接收搜索表单提交的数据,检查搜索表单提交的数据并且根据搜索表单提交的数据构建查询条件,
测试使用效果:
实现编辑页
调整blog31/app/admin/view/Category/articleIndex.html中的编辑按钮链接,
在blog31/app/controller/ArticleController.class.php中创建名为showUpd的方法,渲染后台博文编辑页模板,
通过从后台博文列表页点击编辑按钮访问后台博文管理系统编辑页,发现页面虽然能够展示,但是样式乱了,
于是进一步调整blog31/app/admin/view/Category/articleEdit.html中引入资源文件的路径,将”../libs”全部替换为”{C(‘URL’)}/public/admin”,如下图所示,代码截取不全,以实际文件中的代码为准,
在blog31/app/admin/controller/ArticleController.class.php中的showUpd方法里查找该页面需要的相关数据,
在blog31/app/admin/view/Article/articleEdit.html中调整form表单中的各表单域并且回显本条博文数据,
在blog31/app/admin/controller/ArticleController.class.php中构建updh方法,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62public function updh(){
//接收表单传递的数据
//GET传递的
$id = $_GET['id'];
//POST传递的
$new_title = trim($_POST['title']);
$new_intro = trim($_POST['intro']);
$cat = $_POST['cat'];
$catArr = explode('|', $cat);//处理接收到的分类数据
$new_cat_id = $catArr[0];
$new_cat_name = $catArr[1];
$new_content = trim($_POST['content']);
//检查哪些数据真的被修改了
@session_start();
$target = [];
if( $new_title!=$_SESSION['old_article']['title']&&!empty($new_title) ){//如果新的标题数据和老的标题数据不一样,则说明标题确实被修改了
$target[] = "title='{$new_title}'";
}
if( $new_intro!=$_SESSION['old_article']['intro']&&!empty($new_intro) ){//如果新的简介数据和老的简介数据不一样,则说明简介确实被修改了
$target[] = "intro='{$new_intro}'";
}
if( $new_cat_id!=$_SESSION['old_article']['cat_id'] ){//如果新的分类id数据和老的分类id数据不一样,则说明分类确实被修改了
// cat_id=xxx, cat_name="xxx"
$target[] = "cat_id={$new_cat_id}, cat_name='{$new_cat_name}'";
}
if( $new_content!=$_SESSION['old_article']['content']&&!empty($new_content) ){//如果新的内容数据和老的内容数据不一样,则说明内容确实被修改了
$target[] = "content='{$new_content}'";
}
//根据检查的结果判断是否需要真的执行更新操作
if( !empty($target) ){//$target不为空,则说明确实有数据被修改了
$strTarget = implode(',', $target);
$model = \core\App::single('\model\ArticleModel');
$sql = "update bg_article set {$strTarget} where id={$id}";
if( $model->setData($sql) ){//执行成功
echo '修改成功!';
}else{//执行失败
echo '修改失败,请联系超级管理员!';
}
}else{//否则,则说明没有数据需要被更新
echo '您还没有修改数据呐~';
}
//2秒之后跳回编辑页
$url = C('URL') . '/index.php?p=admin&m=article&a=showUpd&id='.$id;
header('Refresh:2; url='.$url);
exit;
}测试使用效果:
点击提交:
编辑成功,说明功能制作OK。
实现删除功能
4. 整理模板文件
将blog31/app/admin/view/Article/articleEdit.html中的head部分提取出来,剪切转移到blog31//app/admin/view中新创建出来的Common目录中的head.html文件,
剪切下图所示代码:
转移到Common/head.html中,
将blog31/app/admin/view/Article/articleEdit.html中的页头部分提取出来,剪切转移到blog31//app/admin/view中Common目录中的bodyhead.html文件,
剪切如下代码:
转移到Common/bodyhead.html中,
将blog31/app/admin/view/Article/articleEdit.html中的sidebar部分提取出来,剪切转移到blog31//app/admin/view中Common目录中的sidebar.html文件,
剪切如下代码:
转移到Common/sidebar.html中,
然后将blog31/app/admin/view/Article/articleEdit.html中所有用到公共代码的部分,换成引入公共文件的操作:
将blog31/app/admin/view/Article/articleAdd.html中所有用到公共代码的部分,换成引入公共文件的操作:
将blog31/app/admin/view/Article/articleIndex.html中所有用到公共代码的部分,换成引入公共文件的操作:
修改blog31/app/admin/view/Common/sidebar.html进入博文管理系统各页面的链接地址:
再次访问后台博文管理系统编辑页:
5. 全天总结
制作的页面和功能:
封装验证码工具类
实现后台分类管理系统的列表页和编辑页
实现无限级递归分类
实现博客文章管理系统添加页
实现博客文章管理系统列表页基础功能
七、昨日回顾
1. 知识回顾
- 实现后台分类管理系统的列表页和编辑页
- 实现博客文章管理系统添加页
- 实现博客文章管理系统列表页基础功能
2. 昨日反馈
八、知识路径
[ ] XSS攻击
[ ] 实现后台登陆页面和功能
防翻墙功能
7天免登录功能
[ ] 实现后台退出功能
==目标:能够防范XSS攻击,能够实现后台登陆退出功能==
九、今日课程内容:博客项目
1. XSS攻击
通常用户在表单界面输入的内容是不确定的,用户可以输入任何想要输入的内容进行提交,如果输入的是一些代码,并且我们在程序文件中还没有对用户输入提交的数据进行任何的过滤处理的话,那么,将会面临一定风险。
XSS的概念:Cross Site Scripting 全称 跨站脚本攻击,是一种经常出现在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。
==XSS攻击的案例==:
在用户输入区域构建script内容,如下图所示:
点击提交,效果为:
先弹出了提示框,
然后跳转到了一个大网站:
==小结==:
XSS攻击其实就是用户在表单输入区域构建了客户端或浏览器能够解析的代码内容,以此来改变其他用户在访问当前这个页面的效果。
==如何防?==
通过htmlspecialchars函数来进行过滤处理(主要用来处理富文本编辑器中的内容);
通过addslashes函数来进行过滤处理(主要用来处理非富文本编辑器的其他输入区域的内容);
addslashes可以将内容中的具有特殊意义的符号进行转义,如上图中将双引号转义了,这样一来,双引号将不再是包裹script代码标签信息的符号,而是普通的字符而已,这样就导致了script代码失效。
通过字符串替换或正则匹配的方式进行再过滤处理;
2. 实现后台登陆页面和功能
在blog31/app/admin/controller目录中创建LoginController.class.php,
在LoginController.class.php中创建相应的代码,如下图所示:
访问登陆页面,
调整登陆模板页面blog31/app/admin/view/Privilege/login.html的代码,
再次访问,
在blog31/app/admin/controller/LoginController.class.php中创建captcha方法,专门输出验证码,
调整blog31/app/admin/view/Privilege/login.html回显验证码,并且实现局部刷新
调整blog31/plugins/CaptchaTool.class.php中生成验证码字符串后的数据,将其记录到SESSION中,
在blog31/app/admin/controller/LoginController.class.php创建loginh方法,代码如下,专门处理登陆验证功能:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35//登陆验证方法
public function loginh(){
//接收表单提交的数据
$acc = trim($_POST['acc']);//帐号
$pwd = md5(addslashes(trim($_POST['pwd'])));//密码
$captcha = addslashes(trim($_POST['captcha']));//验证码
//检查验证码是否填写正确
@session_start();
if( $captcha!==$_SESSION['captchaStr'] ){//用户填写的验证码和生成的验证码不相等,则说明用户输入的验证码是错误的
echo '你好棒棒哟,竟然写错了哟!~';
$url = C('URL') . '/index.php?p=admin&m=login&a=showLogin';
header('Refresh:2; url='.$url);
exit;
}
//检查帐号和密码是否正确
$userModel = \core\App::single('\model\UserModel');
$sql = "select * from bg_user where acc='{$acc}' and pwd='{$pwd}'";
$row = $userModel->getRow($sql);
if( !empty($row) ){//帐号密码填写正确
echo '客官,您又来了吖~';
$url = C('URL') . '/index.php?p=admin&m=article&a=showList';
header('Refresh:2; url='.$url);
}else{//否则至少有一个填写错误
echo '帐号或密码填写错误,请重新填写!';
$url = C('URL') . '/index.php?p=admin&m=login&a=showLogin';
header('Refresh:2; url='.$url);
}
exit;
}测试使用效果:
访问登陆页面,输入帐号密码,
点击登录,
我们打开一个新的浏览器,在浏览器地址栏中直接手动输入后台博文管理系统列表页链接地址,发现,在没有登录的情况下都能直接访问,
这种效果就是翻墙效果。
那么,如何实现防翻墙呢?
防翻墙功能
在blog31/app/admin/controller/LoginController.class.php中的loginh方法里,登陆成功时记录下登陆用户的完整信息,
在父类控制器blog31/core/Controller.class.php中的构造方法里,进行防翻墙检查,
测试防翻墙效果:
首先在未登录的情况下直接访问后台博文列表页,经过防翻墙处理,已经不能直接访问进去了:
然后使用帐号密码登录后台,
登陆成功后提示:
登陆成功后再同一个浏览器中打开一个新的浏览器选项卡,直接在地址栏中输入后台博文管理系统列表页链接地址,
==优化==:回显登陆成功者的昵称
在blog31/app/admin/view/Common/bodyhead.html中调整登陆成功者的昵称信息,
使用效果如下:
7天免登录
在blog31/app/admin/view/Privilege/login.html中调整7天免登录按钮的value值,
在blog31/app/admin/controller/LoginController.class.php中的loginh方法里的登陆成功的部分判断是否有勾选7天免登录按钮,
在父类控制器blog31/core/Controller.class.php的构造方法中,检查在没有登录的情况下是否有勾选过7天免登录按钮,
测试使用效果:
直接访问后台页面:
重新登录,并且勾选7天免登录按钮:
登陆成功后:
再次关闭浏览器,直接访问后台页面,
3. 实现后台退出功能
在blog31/app/admin/view/Common/bodyhead.html构建退出链接,
在blog31/app/admin/controller/LoginController.class.php中创建logout方法,代码如下:
测试使用效果:
点击退出按钮后:
优化:
优化接收7天免登录按钮参数:在blog31/app/admin/controller/LoginController.class.php中的loginh方法里,
十、昨日回顾
1. 知识回顾
后台博文管理系统列表页搜索功能和分页功能;
后台博文管理系统编辑页;
整理后台公共模板页面;
XSS攻击怎防?
1)都使用trim函数处理一遍;2)非富文本编辑器的数据一般使用addslashes函数处理一次;3)富文本编辑器的数据一般使用htmlspecialchars函数处理一次;
后台登陆功能和防翻墙功能;
2. 昨日反馈
十一、知识路径
- 实现前台首页
- 实现前台博文详情页
- 实现前台注册功能
- 实现前台登陆功能
- 实现评论功能
- SQL注入
==目标:能够实现前台首页和详情页、能够实现前台注册登陆功能、能够实现评论功能、掌握如何防范SQL注入==
十二、今日课程内容:博客项目
1. 实现前台首页
将day17/code/templates/home目录下的两个模板文件拷贝一份,
将复制的两个模板文件转移粘贴到blog31/app/home/view目录中,
在blog31/app/home/controller中创建IndexController.class.php文件,代码如下:
在IndexController中创建index方法,渲染首页模板,
访问前台首页,发现样式乱了,
进一步调整模板页面引入资源文件的代码,
将day17/code/templates/home/libs文件夹复制一份,
拷贝到blog31/public目录中,
改名为home
将blog31/app/home/view/blogShowList.html中引入资源文件的地址进行调整,代码如下,截图不全,以实际文件中的为准:
在blog31/app/home/controller/IndexController.class.php中index方法里查询所有需要的数据,代码如下:
将查询的数据回显到blog31/app/home/view/blogShowList.html中,
测试访问效果:
首页数据基本展示正常,但是发布者的昵称没有,所以我们还需要到后台中做进一步调整,在发表文章时关联上发布者的昵称,
调整后台blog31/app/admin/controller/ArticleController.class.php中adh方法里新增文章数据的代码,关联上添加者的信息,如下图所示将蓝框改为红框:
再次从后台发表一篇文章,访问前台首页,
2. 实现前台博文详情页
调整blog31/app/home/view/blogShowList.html跳转到文章详情页的链接地址,
在blog31/app/home/controller/IndexController.class.php中新增showInfo方法,渲染详情页模板,
测试访问详情页:
将blog31/app/home/view/blogShow.html中所有引入资源的路径全部修改,如下图所示,代码截取不全,以实际程序文件中为准:
在blog31/app/home/controller/IndexController.class.php中的showInfo方法里查询需要回显的文章数据,
将查询出来的数据在模板页面blog31/app/home/view/blogShow.html回显,测试使用效果:
效果OK。
3. 实现前台注册功能
调整前台首页模板页面注册表单区域blog31/app/home/view/blogShowList.html,
在blog31/app/home/controller/IndexController.class.php中创建名为register的方法,实现注册功能,、
测试使用效果:
填写注册信息
点击注册后:
4. 实现前台登陆功能
调整前台首页blog31/app/home/view/blogShowList.html登录区域的表单,
将父类控制器中构造方法里的session_start前置到公共的部分,让前台和后台都能够开启,
在blog31/app/home/controller/IndexController.class.php中创建一个login方法,代码如下:
调整blog31/app/home/view/blogShowList.html中登陆状态的显示区域:
测试使用效果:
5. 实现前台评论功能
调整博文详情页评论form表单区域,
在blog31/app/model目录下创建CommentModel.class.php文件,代码如下:
在blog31/app/home/view/blogShow.html中新增携带两个数据,分别是本篇文章的id和本篇文章的title,
在blog31/app/home/controller/IndexController.class.php中构建talk方法,实现评论功能,
测试使用效果:
填写评论数据:
点击发布:
功能实现成功,但是在详情页中还没能回显评论数据,
==优化==:在博文详情页中回显评论数据
在blog31/app/home/controller/IndexController.class.php中的showInfo方法里查询出所有本篇文章的评论数据,
然后在模板文件blog31/app/home/view/blogShow.html中回显评论数据
6. SQL注入
SQL注入式攻击:指的是利用PHP程序拼接SQL语句使用的是字符串的特性,强行的在表单区域输入某些字符串数据与原本的SQL语句形成拼接,改变SQL语句条件性质的做法。
如何防?
通过addslashes处理接收的表单数据,以达到将接收的数据中所包含的引号转义掉;
通过逻辑处理,如在blog31/app/home/controller/LoginController.class.php的loginh方法中所作的针对检查帐号密码是否填写正确的调整:
==小结==:我们在程序中针对SQL注入式攻击行为,需要进行防御处理是:
- 通过addslashes过滤;
- 通过业务逻辑精确查找过滤;