最近项目调研时,需要在集成板子上做个配置的网页,板子上装的是linux系统,配置信息在一个SQLite数据库中,经过讨论大家决定用PHP做这个网页。由于项目组没一个会PHP的,所以安排我调研下写个Demo,经过几天的研究终于完成了Demo的调研(调研过程主要参考网络,具体开发就交给月底入职的小弟去做了,哈哈,有个小弟真好),特此记录(根据我调研的顺序展开)。
项目截图:
1.搭建环境
首先当然是搭建环境了,我选择的是phpstudy(下载和安装可 参考,完全傻瓜式),编译器选择的是免费的Notepad++(只因为免费而选用,实际效果比记事本好点而已)。至此你可以开发第一个Hello World了。语法可 参考。发布也很简单,只要把php文件放到网站目录下就行了(如安装教程中,目录就是D:\WWW)。网站的端口号可以打开phpStudy主界面,点击其他选项菜单,点击myHomePage,就能看到端口号啦。2.PHP从SQLite中读取信息
代码如下:if ($db = sqlite_open('GateWay.db')) {
$sql = "select * from ComInfo";
$res = sqlite_unbuffered_query($db, $sql);
echo "<table border=1><tr>";
echo "<th>comId</th><th>comName</th><th>baud</th><th>parity</th><th>dataBit</th><th>stopBit</th>";
echo"</tr>";
while($item = sqlite_fetch_array($res, SQLITE_ASSOC))
{
echo "<tr>";
echo "<td>".$item["comId"]."</td>";
echo "<td>".$item["comName"]."</td>";
echo "<td>".$item["baud"]."</td>";
echo "<td>".$item["parity"]."</td>";
echo "<td>".$item["dataBit"]."</td>";
echo "<td>".$item["stopBit"]."</td></tr>";
}
echo "</table>";
sqlite_close($db);
}
3通过Ajax获得信息
作为一个网站,现在很少能看到每次提交刷新整个页面的网站了。下面是通过Ajax请求获得数据(这个跟html中类似,这边用的是jQuery中封装的Ajax):在页面上有一个id为fullDataDiv的DIV用来装获取的数据,代码如下://js代码
var data={ opType:"ShowAllComInfo" };
$.post("ComInfoServer.php", data, function (result) {
$("#fullDataDiv").html(result);
});
//ComInfoServer.php页面的代码
$opType = $_POST["opType"];
switch($opType)
{
case "ShowAllComInfo":
{
if ($db = sqlite_open('GateWay.db')) {
$sql = "select * from ComInfo";
$res = sqlite_unbuffered_query($db, $sql);
echo "<table border=1><tr>";
echo "<th>comId</th><th>comName</th><th>baud</th><th>parity</th><th>dataBit</th><th>stopBit</th>";
echo"</tr>";
while($item = sqlite_fetch_array($res, SQLITE_ASSOC))
{
echo "<tr>";
echo "<td>".$item["comId"]."</td>";
echo "<td>".$item["comName"]."</td>";
echo "<td>".$item["baud"]."</td>";
echo "<td>".$item["parity"]."</td>";
echo "<td>".$item["dataBit"]."</td>";
echo "<td>".$item["stopBit"]."</td></tr>";
}
echo "</table>";
sqlite_close($db);
}
}
break;
}
4.新建记录
接着是新建记录,本质跟Ajax查询类似,代码如下://js代码
var comName = $('#tbComName').val();
var baud = $("select#selBaud option:selected").text();
var parity = $("select#selParity option:selected").text();
var dataBit = $("select#selDataBit option:selected").text();
var stopBit = $("select#selStopBit option:selected").text();
var data={opType:"AddComInfo",comName: comName, baud: baud,parity:parity,dataBit:dataBit,stopBit:stopBit};
$.post("ComInfoServer.php", data, function (result) {
alert("Add Success!");
});
//ComInfoServer.php页面的代码
$comName =$_POST["comName"];
$baud = $_POST["baud"];
$parity = $_POST["parity"];
$dataBit = $_POST["dataBit"];
$stopBit = $_POST["stopBit"];
if ($db = sqlite_open($dbName)) {
$sql= "insert into ComInfo values(null,'".$comName."', '".$baud."','".$parity."', '".$dataBit."', '".$stopBit."')";
sqlite_query($db, $sql);
sqlite_close($db);
}
5.删除记录
然后就是删除记录,比新增还简单,只需要传一个ID就行,ID在当前行的第一列(var comInfoid = $(this).parents("tr").find('td:first').text();),代码跟新增类似,就不贴了。6.编辑记录
最后是编辑,为了调研页面跳转,我把编辑放在另一个页面,页面传值直接放在url中,EditComInfoServer.php页面中放了一个隐藏控件保存ComInfoid的值,在页面加载完毕之后根据id向服务端请求完整的数据。代码如下://ComInfoMain.php的js代码
$(".edit").click(function(){
var comInfoid = $(this).parents("tr").find('td:first').text();
window.location= "ComInfoServer.php?ComInfoid="+comInfoid;
});
//EditComInfoServer.php
//隐藏控件
<div>
<input type="hidden" id="tbComInfoid" value="<?php echo $_GET['ComInfoid']; ?>"/>
</div>
//js代码
$(function () {
var URL = "ComInfoHandler.ashx";
var data={ComInfoid: $("#tbComInfoid").val(),opType:"EditInit" };
$.post(URL, data, function (result) {
var info=eval("(" + result + ")");
comName =info.comName;
baud = info.baud;
parity = info.parity;
dataBit = info.dataBit;
stopBit = info.stopBit;
$("#tbComName").val(comName);
$("#selBaud option[value='"+baud+"']").attr("selected", true);
$("#selParity option[value='"+parity+"']").attr("selected", true);
$("#selDataBit option[value='"+dataBit+"']").attr("selected", true);
$("#selStopBit option[value='"+stopBit+"']").attr("selected", true);
});
});
7.PHP+Sqlite的防注入以及错误友好提示
至此初步的增删改查都实现了,但只是实现,根本没考虑安全性。主要有三个方面:如果输入数据库的完整路径,数据库都被下载下来了;或者在comName里精心填入一些字符串就能实现SQL注入;另外出错了,提示也很不友好,直接把程序信息都泄露出来了。7.1防止数据库被下载
对于数据库被下载(这个问题在Access和SQLite中都有),可以不让其直接访问,具体配置是在网站的根目录新建一个文件名为.htaccess的文件(直接新建不了,可以另存为该文件,另外注意的是这个文件与PHP的版本有关,我用的是Apache+PHP5.3),用记事本打开写入内容如下:<FilesMatch ".db$">
Deny from all
</FilesMatch>
其他的设置在最新的版本中已经都是默认设置了,这样,数据库文件就不会被访问下载了。
7.2防止SQL注入
对于注入攻击,从两方面下手,前台验证输入的内容(过滤一些注入的关键字),后台再进行二次验证。根据我们实际的业务,我在前台只限制了长度,后台对传入的字符串进行加密之后存到数据库中的,读取的时候再解密。其中解密的几个方法 来自这里。这样做法其实对存储有浪费的,经过测试,加密后的字符串长度是原来的两到三倍,图省事我把comName的字段类型改成了text。在实际操作时还遇到了截取字符串乱码的问题,原因是substr在遇到汉字时会遇到编码问题,因此采用mb_substr方法。代码如下://新增或更新时
$postComName=$_POST["comName"];
if(strlen($postComName)>10)
{
$postComName=mb_substr($postComName,0,10,'utf-8');
}
$comName =passport_encrypt($postComName,$key);
//读取时解密
$comName=passport_decrypt($item["comName"],$key);
7.3URL重写
在注入攻击的问题上还可以通过重写url隐藏网站的技术,当然这不能从本质上防SQL注入,只是提高注入的难度而已。在刚刚的.htaccess加入如下代码:<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule main.html ComInfoMain.php
RewriteRule ComInfoHandler.ashx ComInfoServer.php
RewriteRule ^edit([0-9]+)\.html$ EditComInfoServer.php?ComInfoid=$1
</IfModule>
这样访问main.html就自动解析为ComInfoMain.php,其中第三个规则是用正则表达式实现,例如edit23.html就解析成EditComInfoServer.php?ComInfoid=23。这个调查了大半天,只因为网上的大多都是以讹传讹(或许是版本的原因吧)。当然了,还有一个原因就是配置文件的苛刻要求。在调查期间删除了一个"看似多余"空格导致Apache启动不起来了,元凶如下:
7.4错误友好提示
最后就是错误友好提示了,打开phpStudy主界面,点击其他选项菜单,点击打开配置文件选httpd.conf找到ErrorDocument 404,将其前面的#去除,在后面加上error的页面,本例中设置是ErrorDocument 404 /error.html,这里表示error.html放在网站根目录下。至此全部完成,Demo很简单,希望能对刚学PHP的读者有些帮助,欢迎阅读、讨论、转载,转载请保留原文链接。