今天我们来学习CI中的模型和辅助函数。
【本讲内容】
一、模型;
之前我们分别介绍了MVC模式中的C层和V层,而M层还没有谈到。现在我们来谈一谈。
了解什么是模型?如何创建一个简单的模型?怎样载入模型?
二、辅助函数。
当我们需要完成某些特定任务的时候,辅助函数将会派上用场啦。还记得军哥在第一讲中介绍CI能够为我们做什么的时候,讲到其中有一个好处是减少代码量,那CI的辅助函数就可以帮助我们做到这一点。
【具体内容】
1、模型。
(1)、什么是模型?
模型对于那些传统MVC方式的人来说是可选的。比如,我们之前讲的一些例子中就没有用到M层,那是由于我们暂时还没有涉及到与数据库的操作,一旦涉及到数据库操作,我们就要用M层了,因为模型(M)就是专门用来和数据库打交道的。
(2)、那怎么样创建一个模型呢?
其实模型就是一个PHP类,里面包含添加、删除、更新和统计数据等方法,我们来创建一个简单的看一下,代码如下:
此时我们需要注意以下几点:
a、创建的模型类文件应该放在application/models/ 文件夹,文件名应该是模型类名的小写格式。例如上例当中的,文件名应该为user_model.php;
b、类名的首字母必须大写,其他字母小写;
c、确保你的类继承了基本模型类(CI_Model)。
那这样的模型文件建好了,该怎么用呢?
(3)、载入模型。
我们写好的模型是通过控制器来载入和引用的。载入模型在CI中有两种方式:
一种是手动载入,假如我们想载入上面那个模型类,那它可以很容易的在控制器中的方法中装载,就像这样:
以上通常都放在控制器的构造函数中进行装载,毕竟这样只需要手动装载一次就够了。
另一种是自动载入,找到并打开 application/config/autoload.php 文件,然后在自动装载数组中加入这个模型,只要设置$autoload['model'] = array();为:
接下来军哥带大家写一个完整的例子,把之前讲的内容一起连贯起来,正好也复习一下。
由于我们会涉及到数据库操作,因此首先要进行数据库的配置,打开application/config/database.php文件,设置连接数据库的一些参数值,如下:
上面只是设置了其中一部分的参数,想了解更多你可以浏览CI中国的中文手册,http://codeigniter.org.cn/user_guide/database/configuration.html 。
然后我们开始建一个数据表,这里叫user表,表结构如下:
我们插入一条数据,如下:
简单地说,SQL注入是一种技术,它可以让恶意用户注入SQL命令,这些命令在语义上是合法的,但可以改变命令预期的行为,并且有可能危及应用程序的安全。这会儿你可能在挠头,并尝试多读几次看是否有更多的意义。通过示例解释起来会更直观一些。
想想前面章节看到的SQL,一个典型的例子是根据所属的类别检索记录:
var sql = "SELECT* FROM Items where CategoryId = 2";
该语句的问题是:类别是常量。它不会改变,这意味着始终会检索出属于WHERE语句指定的类别的数据。根据网站的情况和需求,这可能确实不是一件坏事。向用户提供一种可以通过类别过滤产品的方法是非常常见的需求。通常使用表单,将类别列表放在下拉菜单或文本框中。服务器端代码接收到传递过来的选项,用它动态组装成一条可用的SQL语句,如下所示:
var sql = "SELECT * FROM Items WHERE CategoryId = " + Request["categoryId"];
此刻,任何传入Request[“categoryId”]的值都将和SQL字符串连接在一起并在数据库中执行。没有阻止用户向文本框里输入非法信息的措施。如果用户向文本框中输入垃圾数据,连接在一起的结果可能会不符合SQL语法,网站会出现问题;否则SQL会成功执行。在前面的例子里,如果用户输入数值2,同时对前一章的示例数据库执行SQL,那么将导致所有属于Computing类别的条目被检索出来。但是用户还可以向文本框中输入下面的内容:
2 or 1 = 1
当被拼接在一起时候,结果会是这样:
SELECT * FROMItems WHERE CategoryId = 2 or 1 = 1
这完全是有效的SQL,同时会返回所有行,因为WHERE 1 = 1这个条件一直是成立的。
该SQL的初衷是提供一种方法,在用户知道哪些有效的类别可用的情况下,可以将结果限制在一种类别里;但是由于允许用户通过注入额外的过滤器修改SQL的行为,导致了预料不到的结果。这就是SQL注入,是安全性不高的网站暴露出的最多的两个漏洞之一。
前面的例子看起来不怎么危险,那么SQL注入能导致什么问题呢?首先,为了好的初衷才提供了过滤器。因为有上百万条记录,过滤器原本可能是用来防止检索出所有的记录,同时持续请求所有的记录会降低网站的性能。这也使得拒绝服务攻击(DoS)变得更加容易,另外还有更大的风险。
看看下面的代码段:
var sql = @"SELECTCount(*) FROM Users WHERE UserName ='" + Request["UserName"] + "' AND Password = '"+ Request["UserPass"] + "' ";
这常作为用户认证的方式,用于匹配在登录表单中提供的用户名和密码。它用于测试数据库中有多少条记录和用户提供的用户名和密码匹配,如果结果大于0,用户就可以进一步操作。在前面的示例中,展示了总是成立的条件并导致返回了所有的记录。这里有另外一个总是成立的条件:
WHERE ' ' = ' '
对于黑客来说,很容易向这个SQL中注入代码,从而返回用户表中的所有数据,因此只需要向用户名或密码文本框中输入'or'"=',就可以访问网站中的限制区域。拼接将完成创建有效的可被执行的SQL语句的工作。
幸运的是,相比SQL Server完整版,SQL Server Compact有一些功能性限制。例如,Compact版本不支持批处理语句,更强大的数据库可以做到这点,这会令它们面临潜在的严重后果。批处理语句可以在一条SQL语句中传递多个操作,每个独立的命令都会依次执行。下面展示了一个批处理示例:
SELECT * FROMItems; DROP TABLE Users
在SQL Server数据库中执行该语句,首先会返回Items表中的所有条目,然后会永久删除名为Users的数据表。甚至还有更强大的命令,能够导致整个数据库被删除,甚至导致黑客获取数据库所在服务器的完整控制权。
参数保护
现在知道了问题的本质,以及能够导致的潜在的非常严重的后果,就需要学习如何使代码远离SQL注入的威胁。一些人建议使用黑名单作为有效途径。这涉及监控用户是否输入了SQL关键词,如果检查到关键字,就驳回提交的请求。这种做法具有两面性:很多SQL关键词是日常用语(or、and等),并且SQL关键词时常变更。这意味着如果黑名单中引入了新的关键词,就不得不更新构建的每个网站。第二个建议是避开单引号,当然这也是有效的,但如果字符串不是SQL的一部分就起不了作用,就像前面的第一个示例。
事实是,只有一种被证明有效的方法能够保护网站不受SQL注入攻击,那就是使用参数。参数是动态数据的占位符,在执行SQL时,这些动态数据以一种安全的方式和SQL语句拼接在一起。为了帮助大家理解它的工作机制,下面举一个例子展示数据库辅助程序如何支持参数的使用:
var sql ="SELECT* FROM Items WHERE CategoryId = @0; var data =db.Query(sql, Request["categoryId"]);
注意,@0标记代表了SQL语句中的动态数据,@符号后面跟一个0。像往常一样,SQL被传递到Query方法,但是这次它后面跟着动态数据的源。在内部,数据库辅助程序会创建ADO.NET参数对象并且将其传递到数据库中。数据库会依次检查传入的参数值以保证它们的数据类型和目标列是匹配的,从而避免期望的是数值类型、但传入的是字符串类型的情况;也能保证按照字面含义处理所有被成
(function() { window['XR'] = {}; var midContainer = new Array(); var mapContainer = new Array(); var MAPID = 0; function Map(mid) { var type = typeof (mid); if ((type != "string") && (type != "number")) { throw "Map id must be a string or number!"; } for (var _c = 0; midContainer[_c]; _c++) { if (mid == midContainer[_c]) throw "You have already created Map : " + mid; } var identify = MAPID++; midContainer[identify] = mid; mapContainer[identify] = {}; mapContainer[identify]["id"] = mid; this.id = mid; this.prefix = "K_"; this.toString = function() { return "This is a map object!"; } } Map.prototype.getMapId = function() { return this.id; } Map.prototype.getMapIndex = function() { var index = -1; for (var _i = 0; mapContainer[_i]; _i++) { if (this.id == mapContainer[_i]["id"]) { index = _i; } } return index; } Map.prototype.put = function(key, value) { if ( typeof (key) != "string") { throw "key must be a string!"; } if ( typeof (value) == "function") { throw "value shouldn't be a function!"; } if (this.trimStr(key) == "") { throw "key is empty!"; } var index = -1; index = this.getMapIndex(); if (index != -1) { key = this.prefix + key; mapContainer[index][key] = value; } } Map.prototype.get = function(key) { var index = -1; index = this.getMapIndex(); var value = ""; if (index != -1) { var _tV = mapContainer[index]; key = this.prefix + key; value = (_tV.hasOwnProperty(key)) ? _tV[key] : "You haven't save this key's value!"; } else { value = "Current Map has lost connection!"; } return value; } Map.prototype.deleteKey = function(key) { var index = -1; index = this.getMapIndex(); key = this.prefix + key; var _tV = mapContainer[index]; if (_tV.hasOwnProperty(key)) { delete _tV[key]; } } Map.prototype.clearMap = function() { var index = -1; index = this.getMapIndex(); var maxId = MAPID - 1; if (index <= maxId) { for (var t = index; t < maxId; t++) { mapContainer[t] = mapContainer[t + 1]; midContainer[t] = midContainer[t + 1]; } mapContainer[maxId] = null; midContainer[maxId] = null; this.id = null; this.toString = null; MAPID--; } } Map.prototype.trimStr = function(str) { return str.replace(/(^\s*)|(\s*$)/g, ""); } Map.prototype.isEmpty = function() { var index = -1; index = this.getMapIndex(); if (index != -1) { for (var attr in mapContainer[index]) { //alert(mapContainer[index][attr]); if (attr != "id") { return false; } } } return true; } Map.prototype.showMap = function() { var index = -1; index = this.getMapIndex(); var str = ""; if (this.id != null) { str = "Map:\t" + this.id + "\n"; for (var attr in mapContainer[index]) { if (attr != "id") { str += attr + ":\t" + mapContainer[index][attr] + "\n"; } } } else { str = "This Map doesn't exist!"; } alert(str); return str; } window['XR']['Map'] = Map; })() //TEST CODES------------ function test() { with (XR) { var m1 = new Map("ddd"); m1.put("dd", "dfdfdf"); m1.put("dd2", "8yeah!"); //alert(m1.get("dd")); //alert(m1.isEmpty()); m1.deleteKey("dd"); m1.clearMap(); m1.showMap(); } } test(); //-----------------