25.5 明日导航后台管理设计

25.5 明日导航后台管理设计

视频讲解:光盘TMlx25明日导航后台管理设计.exe

25.5.1 后台管理概述

明日导航的后台管理系统可以归纳为三部分内容,第一部分,后台登录;第二部分,对网站中设置的分类数据和导航链接数据进行管理;第三部分,退出后台管理系统。明日导航后台管理系统主页的运行效果如图25.10所示。

图25.10 明日导航后台管理主页面

25.5.2 通过系统配置文件存储后台登录数据

在后台登录模块中,常用的技术包括SESSION机制和加密技术。加密技术又分为很多种。将管理员名称和密码统一加密保存在数据库中就安全了吗?其实并不是这样的。高明的SQL注入手法可以很容易地取得密文。所以,在本项目中,笔者并没有采用将密码保存到数据库中,而是通过配置文件,隐式地保存登录的相关信息。

方法是在系统扩展目录Extend下的Vendor目录中创建PHP脚本文件admin.php,并且使用vendor方法导入。其代码如下:

<?php import("ORG.Util.Session"); Session::set("MR", "mr"); //设置SESSION变量存储后台登录用户名 Session::set("MRKJ", "mrsoft"); //设置SESSION变量存储后台登录密码 ?>

这样,用户不仅可以随时随地更改用户名和密码,还很好地实现了密码文件的安全性。用户也可以独立编写一个日志文件,记录Session的使用信息,从而达到检测非法用户暴力破解的情况。

如此存储后台管理员的登录信息后,在后台登录处理的admin方法中,需要先载入配置文件中设置的用户名和密码。然后,获取表单中提交的用户名和密码与SESSION变量中存储的用户名和密码进行比较,判断其是否为管理员。admin方法中验证管理员是否登录成功的代码如下:

public function admin(){ //后台登录处理方法 vendor('admin'); //载入配置文件中设置的用户名和密码 $username=$_POST['text']; //获取用户名 $userpwd=$_POST['pwd']; if($username==""||$userpwd==""){ //判断用户名和密码是否为空 $this->assign('hint', ’文本框内容不能为空’); $this->assign('url', '__URL__'); $this->display('information'); //指定提示信息模板页 }else{ if($username! =Session::get(MR)||$userpwd! =Session::get(MRKJ)){ //验证登录用户是否正确 $this->assign('hint', ’您不是权限用户’); $this->assign('url', '__URL__/'); $this->display('information'); }else{ $_SESSION['username']=$username; //将登录用户名赋给SESSION变量 $_SESSION['userpwd']=$userpwd; $this->assign('hint', ’欢迎管理员回归’); $this->assign('url', '__URL__/adminIndex'); //设置后台管理主页链接 $this->display('information'); } } }

25.5.3 后台管理架构解析

后台管理的登录从项目根目录下的admin.php入口文件开始,运行此文件生成后台管理项目文件夹,其具体存储于根目录下的Admin文件夹下。在25AdminLibAction目录下创建后台控制器IndexAction,所有后台的操作方法都存储于这个控制器中;在25AdminTpl目录下,创建与IndexAction控制器对应的模板文件夹Index,在这个模板文件夹下存储控制器中方法对应的模板文件。明日导航后台管理架构如图25.11所示。

图25.11 明日导航后台管理架构

25.5.4 ThinkPHP框架中的分页技术

在ThinkPHP框架中封装了自己的分页类,其存储于ThinkPHP框架的ThinkPHPExtendLibraryORGUtil目录下,在应用时,需要在控制器中通过import标签载入类文件,然后执行类的实例化操作,最后调用其中的方法完成数据的分页查询和输出。其关键代码如下:

① import("ORG.Util.Page"); //载入分页类 $count=$com->count(); //统计数据库中的记录数 ② $Page=new Page($count,8); //实例化分页类 ③ $show=$Page->show(); //获取分页超链接 ④ $list=$com->order('id')->limit($Page->firstRow.', '.$Page->listRows)->select(); //执行分页查询 $this->assign('list', $list); //将分页查询结果赋给模板变量 $this->assign('page', $show); //将获取的分页超链接赋给模板变量

①通过“import("ORG.Util.Page"); ”载入分页类。

②实例化分页类,同时可以传递3个参数:第一个是页面总记录数,第二个是每页显示的记录数,第三个为可选参数,通过分页超链接的值。

③调用分页类中的show方法,输出分页超链接。

④ Page:查询分页。属于新增特性,可以更加快速地进行分页查询。Page方法的用法和limit方法类似,格式为:

Page('page[, listRows]')

其中,page表示当前的页数,listRows表示每页显示的记录数。例如,Page('2,10')表示每页显示10条记录,获取第2页的数据。

如果省略listRows,会读取limit('length')的值。例如,“limit(25)->page(3); ”表示每页显示25条记录,获取第3页的数据。如果也没有设置limit,则默认为每页显示20条记录。

说明

在通过Page方法进行数据的分页查询时,Page方法的第一个参数是当前的页数,需要使用$_GET['p'],第二个参数是当前页显示的记录数。

25.5.5 后台管理视图中应用的模板标签

(1)在明日导航的后台管理系统中,应用模板引擎中的switch标签创建一个简单的网页框架,在adminindex方法中根据超链接传递的值实现在不同页面之间的跳转操作。

switch标签的语法如下:

<switch name="变量" > <case value="值1">输出内容1</case> <case value="值2">输出内容2</case> <default />默认情况 </switch>

switch标签类似于PHP中的switch语句,其中name属性可以使用函数以及系统变量,例如:

<switch name="Think.get.userId|abs"> <case value="1">admin</case> <default />default </switch>

对于case的value属性可以支持多个条件的判断,使用“|”进行分隔,例如:

<switch name="Think.get.type"> <case value="gif|png|jpg">图像格式</case> <default />其他格式 </switch>

上述代码表示,如果$_GET["type"]是gif、png或者jpg,就判断为图像格式。

也可以对case的value属性使用变量,例如:

<switch name="User.userId"> <case value="$adminId">admin</case> <case value="$memberId">member</case> <default />default </switch>

使用变量方式的情况下,不再支持多个条件的同时判断。

(2)应用标签中的系统变量,输出超链接传递的参数值。其语法如下:

{$Think.get.pageNumber }

输出超链接变量pageNumber传递的值。

(3)应用include标签包含外部模板文件。其应用的语法如下:

<include file="完整模板文件名"/> //使用完整文件名包含 <include file="操作名"/> //包含当前模块的其他操作模板文件 <include file="模块名:操作名"/> //包含其他模块的操作模板 <include file="主题名@模块名:操作名"/> //包含其他模板主题的模块操作模板 <include file="$变量名"/> //用变量控制要导入的模板

完整文件名的包含,例如,<include file="./Tpl/Public/header.html" />。这种情况下,模板文件名必须包含后缀。使用完整文件名包含的时候,特别要注意文件包含指的是服务器端包含,而不是包含一个URL地址,也就是说file参数的写法是服务器端的路径,如果使用相对路径,则是基于项目的入口文件位置。

用变量包含,例如,<include file="$tplName" />。给$tplName赋不同的值就可以包含不同的模板文件,变量的值的用法和上面的用法相同。

(4)通过foreach标签循环输出模板变量传递的数据。其语法如下:

<foreach name="list" item="vo" > {$vo.id} {$vo.name} </foreach>

其中,name指定在控制器中设置的模板变量名称,必须与控制器中设置的名称相同;item在foreah语句中自行定义的变量,用于输出模板变量传递的数据。

(5)通过if标签完成更加复杂的判断操作。例如,判断当变量name的值等于1或者大于100时输出value1的值,或者当name的值等于2时输出value2的值,否则输出value3的值。其代码如下:

<if condition="($name eq 1) OR ($name gt 100) "> value1 <elseif condition="$name eq 2" /> value2 <else /> value3 </if>

在condition属性中可以支持eq等判断表达式,等同于比较标签,但是不支持带有“>”、“<”等符号的用法,因为会混淆模板解析。

另外,在condition属性中还可以使用PHP代码,例如:

<if condition="strtoupper($user['name']) neq 'THINKPHP' "> ThinkPHP <else /> other Framework </if>

注意

由于if标签的condition属性中基本上使用的是PHP语法,所以尽可能使用判断标签和switch标签会更加简洁。原则上来说,能够用switch和比较标签解决的尽量不用if标签完成。因为switch和比较标签可以使用变量调节器和系统变量。如果某些特殊的要求下面,if标签仍然无法满足要求,可以使用原生PHP代码或者PHP标签来直接书写代码。

25.5.6 后台登录

前面已经对明日导航后台管理系统的架构和所涉及的技术进行了详细讲解,下面讲解后台登录模块的实现方法。后台登录模块的创建由三部分组成:

第一部分,设置后台登录的用户名和密码,已经在25.5.2节中进行了详细讲解。

第二部分,在后台管理系统的默认视图文件index.html中创建表单,将管理员的用户名和密码提交到IndexAction控制器的admin方法中进行处理。创建表单的代码如下:

<form action="__URL__/admin" method="post"> <tr> <td rowspan="3"> <img src="__ROOT__/Public/images/login_02.jpg" width="136" height="150" ></td> <td colspan="3" width="242" height="99" background="__ROOT__/Public/images/login_07.jpg"> <div>用户名:<input class="user" type="text" name="text"></div><br> <div>密&nbsp; &nbsp;码:<input class="pwd" type="password" name="pwd"></div><br> </td> <td> <img src="__ROOT__/Public/images/login_04.jpg" width="26" height="99" ></td> </tr> <tr> <td width="106" height="41"> <input class="buttonSub" type="submit" value=""></td> <td width="98" height="41"> <input class="buttonRes" type="reset" value=""></td> <td colspan="2"> <img src="__ROOT__/Public/images/login_07.jpg" width="64" height="41" ></td> </tr> </form>

第三部分,在IndexAction控制器中创建admin方法,获取表单提交的用户名和密码,与SESSION变量中存储的用户名和密码进行比较,判断用户提交的名称和密码是否正确。如果正确则说明是管理员,将登录用户名和密码存储到SESSION变量中,在information.html模板页中输出“欢迎管理员回归”,在4秒钟后跳转到后台管理主页;否则,可能是提交用户名或者密码为空,提交的用户名或者密码不正确,那么将在information.html模板页中输出“用户名或者密码不能为空”或者“您不是权限用户”,在4秒钟后跳转到后台登录页面。admin方法的关键代码如下:

public function admin(){ //后台登录处理方法 vendor('admin'); //载入配置文件中设置的用户名和密码 $username=$_POST['text']; //获取用户名 $userpwd=$_POST['pwd']; if($username==""||$userpwd==""){ //判断用户名和密码是否为空 $this->assign('hint', ’用户名或者密码不能为空’); $this->assign('url', '__URL__'); $this->display('information'); //指定提示信息模板页 }else{ if($username! =Session::get(MR)||$userpwd! =Session::get(MRKJ)){ //验证登录用户是否正确 $this->assign('hint', ’您不是权限用户’); $this->assign('url', '__URL__/'); $this->display('information'); }else{ $_SESSION['username']=$username; //将登录用户名赋给SESSION变量 $_SESSION['userpwd']=$userpwd; $this->assign('hint', ’欢迎管理员回归’); $this->assign('url', '__URL__/adminIndex'); //设置后台管理主页链接 $this->display('information'); } } }

至此,完成明日导航的后台登录模块。

25.5.7 后台管理主页

管理员登录成功后,将跳转到明日导航的后台管理主页中。在后台管理主页中,根据超链接传递的参数值,实现不同子功能页面之间的跳转操作,进而实现对应的管理操作。

后台管理主页由两部分组成:

第一部分,是在IndexAction控制器中定义adminindex方法。首先,调用当前控制器中的checkEnv方法判断当前用户是否具有访问权限。然后,应用switch语句,根据$_GET[]方法获取的超链接参数值进行判断,当参数值为high时,执行IndexAction控制器中的high方法;当参数值为空时,则执行默认的common方法。最后,指定模板页adminindex。adminindex方法的代码如下:

public function adminIndex(){ //后台管理系统主页 if(IndexAction::checkEnv()){ //判断是否具有访问权限 switch($_GET['type_link']){ //根据超链接传递的变量值输出对应的内容 case "high": IndexAction::high(); //执行high方法 break; case "middle": IndexAction::middle(); break; case "elementary": IndexAction::elementary(); break; case "small": IndexAction::small(); break; case "data": IndexAction::common(); break; default: //默认输出数据管理内容 IndexAction::common(); } $this->display('adminIndex'); //指定模板页 } }

第二部分,在25AdminTplIndex模板文件夹下,创建adminindex.html模板文件,创建后台管理中的功能导航菜单。应用if标签,根据$_GET['type_link']获取的参数值进行判断,应用include标签包含不同的模板文件。其关键代码如下:

<img src="__ROOT__/Public/images/_html_02.gif" width="743" height="65" border="0" usemap="#Map" > <map name="Map"> <area shape="rect" coords="35,24,126,55" href="__URL__/adminIndex? type_link=high"> <area shape="rect" coords="163,21,258,53" href="__URL__/adminIndex? type_link=middle"> <area shape="rect" coords="294,21,388,55" href="__URL__/adminIndex? type_link=elementary"> <area shape="rect" coords="429,26,528,53" href="__URL__/adminIndex? type_link=small"> <area shape="rect" coords="568,22,660,53" href="__URL__/adminIndex? type_link=data"> </map> <if condition="($_GET['type_link'] eq 'high')"> <include file="./Admin/Tpl/Index/high.html" /> <elseif condition="($_GET['type_link'] eq 'middle')"/> <include file="./Admin/Tpl/Index/middle.html" /> <elseif condition="($_GET['type_link'] eq 'elementary')"/> <include file="./Admin/Tpl/Index/elementary.html" /> <elseif condition="($_GET['type_link'] eq 'small')"/> <include file="./Admin/Tpl/Index/elementary.html" /> <else /> <include file="./Admin/Tpl/Index/data.html" /> </if>

当管理员进入后台管理主页后,单击“高级类别管理”超链接时,后台管理主页的运行效果如图25.12所示。

图25.12 明日导航后台管理——高级类别管理页面

25.5.8 高级类别管理

在后台管理主页中,单击“高级类别管理”超链接,在主页中将分页输出高级类别数据,并且在每条记录之后都添加了“删除”超链接,用于删除指定的数据。此时,如果单击管理员左侧的“类别添加”超链接,将跳转到高级类别添加页面,完成高级类别的添加操作,如图25.13所示。如果单击“类别管理”超链接则返回到高级类别输出的页面。

图25.13 明日导航后台管理——高级类别添加页面

高级类别管理包括3个子功能,分别是数据的添加、浏览和删除。其具体实现方法如下:

(1)在IndexAction控制器中创建high方法,根据超链接传递的参数值进行判断,是执行数据的添加操作,还是执行数据的分页查询。其关键代码如下:

public function high(){ //高级类别处理方法 header("Content-Type:text/html; charset=utf-8"); //设置编码格式 $com=M('hightype'); //实例化模型类,a_hightype表 if($_GET['handle']=='insert'){ //判断超级链接的参数值,是添加语句还是管理数据 if(IndexAction::checkEnv()){ //判断用户是否具有添加权限 if(isset($_POST['button'])){ $data['ChineseName']=$_POST['ChineseName']; //获取表单提交的数据 $data['EnglishName']=$_POST['EnglishName']; $data=$com->data($data)->add(); //执行添加操作 if($data! =false){ $this->assign('hint', ’数据添加成功!'); $this->assign('url', 'adminIndex? type_link=high&handle=admin'); $this->display('information'); }else{ $this->assign('hint', ’添加失败!'); $this->assign('url', 'adminIndex? type_link=high&handle=insert'); $this->display('information'); } } } }else{ import("ORG.Util.Page"); //载入分页类 $count=$com->count(); //统计总的记录数 $Page=new Page($count,8); //实例化分页类,设置每页显示8条记录 $show=$Page->show(); //输出分页超链接 $list=$com->order('id')->limit($Page->firstRow.', '.$Page->listRows)->select(); //执行分页查询 $this->assign('list', $list); //将查询结果赋给模板变量 $this->assign('page', $show); //将获取的分页超链接赋给模板变量 } }

(2)在25AdminTplIndex模板文件夹下创建high.html模板文件,应用switch标签进行条件判断,如果超链接传递的参数值是insert,那么输出高级类别添加的表单;如果超链接传递的是admin,则应用foreach语句循环输出模板变量传递的高级类别数据,并且创建“删除”超链接,链接到IndexAction控制器下的deletetype方法,完成删除操作,以记录的ID值为参数值;默认输出高级类别数据。其关键代码如下:

<switch name="Think.get.handle"> <case value="insert"> <form name="form1" method="post" action="__URL__/high? type_link=high&handle=insert"> <table width="750" border="1" cellspacing="1" cellpadding="1"> <tr> <td colspan="2" align="center">高级类别添加</td> </tr> <tr> <td width="178" align="right">中文名称</td> <td width="559"><input name="ChineseName" type="text" id="ChineseName" size="40"></td> </tr> <tr> <td align="right">英文名称</td> <td><input name="EnglishName" type="text" id="EnglishName" size="40"></td> </tr> <tr> <td align="center">&nbsp; </td> <td><input type="submit" name="button" id="button" value="提交"> &nbsp; &nbsp; &nbsp; &nbsp; <input type="reset" name="button2" id="button2" value="重置"></td> </tr> </table> </form> </case> <case value="admin"> <table width="750" border="1" cellspacing="1" cellpadding="1"> <tr> <td align="center">ID</td> <td align="center">中文名称</td> <td align="center">英文名称</td> <td align="center">操作</td> </tr> <foreach name="list" item="result" > <tr> <td align="center">{$result.id}</td> <td align="center">{$result.ChineseName}</td> <td align="center">{$result.EnglishName}</td> <td align="center"><a href="__URL__/deletetype? type_link={$Think.get.type_link }&handle= admin&link_id={$result.id}">删除</a></td> </tr> </foreach> <tr> <td colspan="4">{$page}</td> </tr> </table> </case> <default /> <table width="750" border="1" cellspacing="1" cellpadding="1"> <tr> <td align="center">ID</td> <td align="center">中文名称</td> <td align="center">英文名称</td> <td align="center">操作</td> </tr> <foreach name="list" item="result" > <tr> <td align="center">{$result.id}</td> <td align="center">{$result.ChineseName}</td> <td align="center">{$result.EnglishName}</td> <td align="center"><a href="__URL__/deletetype? type_link={$Think.get.type_link }&handle= admin&link_id= {$result.id}">删除</a></td> </tr> </foreach> <tr> <td colspan="4">{$page}</td> </tr> </table> </switch>

(3)在IndexAction控制器中创建deletetype方法,根据超链接传递的ID值,执行delete语句,删除高级类别的数据。在删除高级类别的数据时,与其关联的中级类别、初级类别和子类别中的数据也都将被删除。其关键代码如下:

function deletetype(){ if(IndexAction::checkEnv()){ //判断当前用户是否具备删除权限 $cl=urldecode($_GET['link_id']); //获取超链接传递的ID值 $new=M('hightype'); //实例化模型类 $new=$new->execute("delete from a_hightype where id in(".$cl.")"); //以ID值为条件执行删除操作 if($new! =false){ $new=M('middletype'); //实例化中级类别表 $new=$new->execute("delete from a_middletype where hightid in (".$cl.")"); //删除中级类别的数据 $newe=M('elementarytype'); $newe=$newe->execute("delete from a_elementarytype where middleid in (".$cl.")"); $news=M('smalltype'); $news=$news->execute("delete from a_smalltype where elementaryid in (".$cl.")"); $this->assign('hint', ’数据删除成功!'); $this->assign('url', 'adminIndex? type_link=high&handle=admin'); $this->display('information'); }else{ $this->assign('hint', ’出现未知错误!'); $this->assign('url', 'adminIndex? type_link=high&handle=admin'); $this->display('information'); } } }

25.5.9 判断访问用户的权限

在明日导航后台管理系统中,为了避免其他用户登录后台管理系统给网站带来不必要的麻烦,我们设置了后台登录功能,只有正确登录的用户才可以对数据进行管理。那么在后台是如何判断用户权限的呢?其原理是:当管理员登录成功后,将其登录的用户名和密码存储到SESSION变量中,由此可以在执行每项操作之前,判断当前用户SESSION变量中存储的用户名和密码与系统指定的用户名和密码是否相同,如果相同,则具备数据的操作权限,否则将提示“您不是权限用户”,并且跳转到管理员登录页面。

将这个权限判断的操作封装到checkEnv方法中,完成对当前用户权限的判断操作,如果用户具备访问权限,则返回true,否则返回false。checkEnv方法的语法如下:

public function checkEnv(){ import("ORG.Util.Session"); //判断用户名和密码是否正确 if($_SESSION['username']! =session::get(MR) and $_SESSION['userpwd']! =session::get(MRKJ)){ $this->assign('hint', ’您不是权限用户’); $this->assign('url', '__URL__/'); $this->display('information'); $login=false; }else{ $login=true; } return$login; //返回判断结果 }

25.5.10 操作提示页面

在后台管理系统中,每执行一项操作后,无论是成功,还是失败,都会跳转到同一个提示页面,返回不同的提示信息,并且跳转到指定的页面。例如,当管理登录成功后,将弹出如图25.14所示的提示信息。

图25.14 管理员登录

如果非权限用户登录到后台,则显示的提示信息如图25.15所示。

图25.15 非权限用户登录

操作提示功能是根据在方法中定义的提示信息和跳转路径,经由information.html模板页完成提示和跳转操作的。在information.html模板页中,输出模板变量传递的提示信息,根据模板变量传递的路径进行跳转。其关键代码如下:

<table width="750" border="0" cellspacing="0" cellpadding="0" > <tr> <td align="center">{$hint}</td> </tr> <tr> <td align="center"><span class="spanT">5</span>秒后自动跳转,如未跳转,请单击<a href="{$url}"> 这里</a></td> </tr> </table> <script type="text/javascript"> $(function(){ time(); }); var times=$("span").text(); function time(){ if(times==0){ var url=$("a").attr('href'); window.location.href=url; }else{ window.setTimeout('time()',1000); times=times-1; $("span").text(times); } } </script>

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

微信扫一扫

微信扫一扫

微信扫一扫,分享到朋友圈

25.5 明日导航后台管理设计