代码改变世界

17 身份验证

2016-08-22 14:33  yojiaku  阅读(302)  评论(0编辑  收藏  举报
第17章 使用PHP和MySQL实现身份验证
17.1 识别访问者
在网络上要求访问者提供唯一的登录名和密码进行身份验证。身份验证可以用来允许或禁止用户对特定页面或资源进行访问。

17.2 实现访问控制
首先实现一个简单的访问控制,具体的看程序清单 17-1 secret.php —— 提供简单的身份验证机制的PHP和html
<?php
// create short names for variables
$name = $_POST['name'];
$pw = $_POST['password'];
// 接收html中传过来的name和password信息

if(!isset($name) || !isset($pw))
{
    // 访问者需要提供不为空的name和password,下面使用回显语句构建html表单
?>
    <h1>Please Log In</h1>
    <p>This page is secret.</p>
    <form method="post" action="secret.php"> <!-- 使用post方法将信息传递到secret.php中去 -->
        <p>Username: <input type="text" name="name" /> </p>
        <p>Password: <input type="text" name="password" /> </p>
        <p><input type="submit" name="submit" value="Log In" /> </p>
    </form>
<?php //与上面的?>构成回显语句
}
elseif ($name == "user" && $pw == "pass")
{
    // 如果访问者提供的name和password都正确的话
    echo "<h1>Here it is!</h1>
        <p>I bet you are glad to see this page.</p>";
}
else
{
    // 如果访问者提供的name和password不正确的话
    echo "<h1>Go away!</h1>
          <p>You are not authorized to use this resource.</p>";
}
// 用这个方式有一个缺点就是会提示第三和第四行的变量名没有设置,这里主要是为了方便,最好还是采用html中嵌php比较好
?>

  效果图:

输入后:

 

程序清单17-1只是提供了一个简单的身份验证机制,它存在一些明显的问题:

·在脚本中我们可以看到只有名字为user和密码为pass的用户才能访问这个网页,这是“硬编码”;
·将密码以普通文本形式进行保存,一个密码应该需要经过加密算法才行;
·只保护了一个页面;
·以普通文本形式传递密码,也应该通过加密算法传输密码

下面我们通过一些方法来解决这些问题。
17.2.1 保存密码
从上面的例子可以看出将密码和用户名保存在脚本中是十分不好的一件事,因为修改数据将会变得困难。
于是,我们使用数据库来保存这些东西。
通过数据库验证访问者身份的脚本和程序如程序清单17-2 secretdb.php 所示。
<?php
// create short names for variables
$name = $_POST['name'];
$pw = $_POST['password'];

if(!isset($name) || !isset($pw))
{
    //Vistors needs to enter a name and password
?>
    <h1>Please Log In</h1>
    <p>This page is secret.</p>
    <form method="post" action="secretdb.php"> <!-- 使用post方法将信息传递到secret.php中去 -->
        <p>Username: <input type="text" name="name" /> </p>
        <p>Password: <input type="text" name="password" /> </p>
        <p><input type="submit" name="submit" value="Log In" /> </p>
    </form>
<?php
}
else
{
    // connect to mysql
    $mysql = mysqli_connect("localhost","webauth","webauth");
    if(!$mysql)
    {
        echo "Cannot connect to database.";
        exit;
    }
    // select the appropriate database
    $selected = mysqli_select_db($mysql, "auth");
    if(!$selected)
    {
        echo "Cannot select database.";
        exit;
    }
    // query the auth database to see if there is a record with matches
    $query = "select count(*) from authorized_users WHERE 
            name = '".$name."'and
            password = '".$pw."'";

    $result = mysqli_query($mysql, $query);
    if(!$result)
    {
        echo "Cannot run query.";
        exit;
    }

    $row = mysqli_fetch_row($result);
    $count = $row[0];

    if($count > 0)
    {
        // visitor's name and password combination are correct
        echo "<h1>Here it is!</h1>
            <p>I bet you are glad to see this page.</p>";
    }
    else{
        // visitor's name and password combination are not correct
        echo "<h1>Go away!</h1>
            <p>You are not authorized to use this resource.</p>";
    }
}
?>

  

在运行17-2的脚本时,我们需要先创建auth数据库和auth表和两个示例用户。具体创建看程序清单17-3 createauthdb.sql
create database auth;
use auth;
create table authorized_users (name VARCHAR(20),
                                password VARCHAR(40),
                                PRIMARY KEY (name));
INSERT INTO authorized_users VALUES ('username','password');
insert into authorized_users (name, password) values ('testuser',sha1('password'));
GRANT SELECT ON auth.*
              TO 'webauth'
              identified by 'webauth';
flush PRIVILEGES ;

  

(PS:在这里我遇到了MySQL密码正确却无法本地登录,但是不用密码却可以进入MySQL)
E:\xampp\mysql\bin>mysql -u webauth
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 9
可以看出,用webauth身份进入mysql时并没有使用密码,而:
E:\xampp\mysql\bin>mysql -u webauth -p
Enter password: *******
ERROR 1045 (28000): Access denied for user 'webauth'@'localhost' (using password: YES)
使用了密码却无法进入。
我试了一下,发现,不管什么用户都可以直接进入MySQL:
E:\xampp\mysql\bin>mysql
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 10
原因是我没有给MySQL设置密码,我们给MySQL设置密码就行了。
(记住,添加新用户webauth后需刷新权限记录:
MariaDB [mysql]> flush privileges;
Query OK, 0 rows affected (0.00 sec)
然后就能以webauth身份登录了:
E:\xampp\mysql\bin>mysql -u webauth -p
Enter password: *******
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 22)
效果图:

输入后:

 


17.2.2 密码的加密
让我们来看一下密码如何加密。
我们通常使用单向哈希算法来提高密码的安全性,即PHP提供的sha1()函数:
string sha1(string str [, bool raw_output])
给定字符串str,该函数就返回一个40个字符的伪随机字符串(即同一个密码返回的随即字符串是同一个),如果将bool raw_output改为true,
可以得到一个20个字符的二进制字符串数据。
通过sha1()函数加密的密码无法进行解密,这是单向的。

17.2.3 保护多个页面
要是脚本程序17-1和17-2保护多个页面是很困难的,因为HTTP是无状态的,来自同一人的连续请求之间并没有自动的连接或联系。
保护多个页面最简单的方法就是使用Web服务器提供的访问控制机制。—— HTTP基本身份验证和会话
(这里不细说,看后面的例子)

17.3 基本身份验证(看程序清单17-4 http.php —— PHP可以触发HTTP基本身份验证)
<?php
// if we are using IIS, we need to set
// $_SERVER['PHP_AUTH_USER'] and
// $_SERVER['PHP_AUTH_PW']

if((substr($_SERVER['SERVER_SOFTWARE'], 0, 9) == 'Microsoft') &&
    (!isset($_SERVER['PHP_AUTH_USER'])) &&
    (!isset($_SERVER['PHP_AUTH_PW'])) &&
    (substr($_SERVER['HTTP_AUTHORIZATION'], 0, 6) == 'Basic'))
{
    list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
}

// Replace this if statement with a database query or similar
if(($_SERVER['PHP_AUTH_USER'] != 'user') || ($_SERVER['PHP_AUTH_PW'] != 'pass'))
{
    // visitor has not yet given details, or their name and password combination are not correct

    header('WWW-Authenticate: Basic realm="Realm-Name"');

    if(substr($_SERVER['SERVER_SOFTWARE'], 0, 9) == 'Microsoft'){
        header('Status:401 Unauthorized');
    }else{
        header('HTTP/1.0 401 Unauthorized');
    }

    echo "<h1>Go away!</h1>
        <p>You are not authorized to use this resource.</p>";
}else{
    // visitor has provided correct details
    echo "<h1>Here it is!</h1>
        <p>I bet you are glad to see this page.</p>";
}
?>

  

17.4 在PHP中使用基本身份验证
在17-4的脚本中,我们不再提供要求输入信息的html表单,而是显示一个对话框。
如下图所示:

若输入的密码不正确,会让你继续输入,如果正确,显示: