php管理用户名和密码,如何去管理php的用户名和密码(二)

操练开始

在我们做出测试代码之前,首先要创建一个用户数据表。运行如下语句:

create database myapp;

use myapp;

create table users (user varchar(60), pass varchar(60));

create database myapp;

use myapp;

create table users (user varchar(60), pass varchar(60));

其中user用来储存用户名,pass用来储存密码的hash值。目前,phpass生成的密码hash值最大长度为60。

创建新用户

首先,我们从phpass项目网站把PasswordHash.php下载到网站目录中,并设置能让php加载的权限(Unix系统下一般为600或者644)。然后在网站目录中创建两个文件:user-man.html (644权限), and user-man.php (权限同asswordHash.php)。

下面,把下面的内容写在user-man.html中:

用户名:

密码:

用户名:

密码:

这个文件获取用户名和密码,然后提交到user-man.php。下面是user-man.php的代码:

header('Content-Type: text/plain');

// 本例只是简单的输出文本的hash值,所以开头要声明下,不让浏览器当作html解析。

require '../PasswordHash.php';

// Base-2 logarithm of the iteration count used for password stretching

$hash_cost_log2 = 8;

// Do we require the hashes to be portable to older systems (less secure)?

$hash_portable = FALSE;

//在实际应用中,上面两行最好写在配置文件中,比如config.inc.php

//下面开始获取提交的用户名和密码,实际应用中需要验证有效性,不再赘述。

$user = $_POST['user'];

$pass = $_POST['pass'];

//下面开始计算密码hash值

$hasher = new PasswordHash($hash_cost_log2, $hash_portable);

$hash = $hasher->HashPassword($pass);

if (strlen($hash) < 20)//这里用的CRYPT_EXT_DES方法,其它加密算法得到结果会更长。

fail('Failed to hash new password');

unset($hasher);

function fail($pub, $pvt = '')

{

$msg = $pub;

if ($pvt !== '')

$msg .= ": $pvt";

exit("An error occurred ($msg).\n");

}

//下面开始把用户信息存入到数据库中

$db_host = 'localhost';

$db_port = 3306;

$db_user = ‘dbuser’;

$db_pass = 'dbpass';

$db_name = 'dbname';

//数据库信息也最好存储在配置文件中。下面开始连接数据库,并注意弹出失败信息。

$db = new mysqli($db_host, $db_user, $db_pass, $db_name, $db_port);

if (mysqli_connect_errno())

fail('MySQL connect', mysqli_connect_error());

//下面用预备语句插入用户信息

($stmt = $db->prepare('insert into users (user, pass) values (?, ?)'))

|| fail('MySQL prepare', $db->error);

$stmt->bind_param('ss', $user, $hash)

|| fail('MySQL bind_param', $db->error);

$stmt->execute()

|| fail('MySQL execute', $db->error);

//最后数据库连接

$stmt->close();

$db->close();

header('Content-Type: text/plain');

// 本例只是简单的输出文本的hash值,所以开头要声明下,不让浏览器当作html解析。

require '../PasswordHash.php';

// Base-2 logarithm of the iteration count used for password stretching

$hash_cost_log2 = 8;

// Do we require the hashes to be portable to older systems (less secure)?

$hash_portable = FALSE;

//在实际应用中,上面两行最好写在配置文件中,比如config.inc.php

//下面开始获取提交的用户名和密码,实际应用中需要验证有效性,不再赘述。

$user = $_POST['user'];

$pass = $_POST['pass'];

//下面开始计算密码hash值

$hasher = new PasswordHash($hash_cost_log2, $hash_portable);

$hash = $hasher->HashPassword($pass);

if (strlen($hash) < 20)//这里用的CRYPT_EXT_DES方法,其它加密算法得到结果会更长。

fail('Failed to hash new password');

unset($hasher);

function fail($pub, $pvt = '')

{

$msg = $pub;

if ($pvt !== '')

$msg .= ": $pvt";

exit("An error occurred ($msg).\n");

}

//下面开始把用户信息存入到数据库中

$db_host = 'localhost';

$db_port = 3306;

$db_user = ‘dbuser’;

$db_pass = 'dbpass';

$db_name = 'dbname';

//数据库信息也最好存储在配置文件中。下面开始连接数据库,并注意弹出失败信息。

$db = new mysqli($db_host, $db_user, $db_pass, $db_name, $db_port);

if (mysqli_connect_errno())

fail('MySQL connect', mysqli_connect_error());

//下面用预备语句插入用户信息

($stmt = $db->prepare('insert into users (user, pass) values (?, ?)'))

|| fail('MySQL prepare', $db->error);

$stmt->bind_param('ss', $user, $hash)

|| fail('MySQL bind_param', $db->error);

$stmt->execute()

|| fail('MySQL execute', $db->error);

//最后数据库连接

$stmt->close();

$db->close();

好了,把左右文件保存好,放在web server下测试下。输入用户名和密码,提交后,到数据库中看下:

mysql> select * from users;

+——–+————————————————————–+

| user   |pass                                                         |

+——–+————————————————————–+

| myuser | $3b$08$Lg5XF1Tr.X5TGyfb43vBBeEFZm4GTRQhKQ6SY6emkcnhAGT8KfxFS |

+——–+————————————————————–+

1 row in set (0.00 sec)

至此,用户插入成功。

用户已经存在

下面,我们用上面的方法插入一个相同的用户,同时,用相同的密码。然后查看数据库:

mysql> select * from users;

+——–+————————————————————–+

| user   |pass                                                         |

+——–+————————————————————–+

| myuser | $3b$08$Lg5XF1Tr.X5TGyfb43vBBeEFZm4GTRQhKQ6SY6emkcnhAGT8KfxFS |

| myuser | $1a$08$7lM07FwQMm5/C8G/urT4z..MudfsS227e8oUEu6T51bNWk/RGb/qe |

+——–+————————————————————–+

2 rows in set (0.00 sec)

我们得到了用户名相同的两条记录,但是密码hash值不相同,虽然我们使用了相同的密码。

为了解决这个问题,我们可以在执行插入前先执行一个select语句,查询下该用户名是否已经存在了。但是,这对程序的效率来说不是最优化的。好的做法是让为用户名建立唯一索引,禁止用户用户名的出现:

DROP TABLE users;

CREATE TABLE users (user varchar(60), pass varchar(60), UNIQUE (user));

DROP TABLE users;

CREATE TABLE users (user varchar(60), pass varchar(60), UNIQUE (user));

当我们插入相同的用户名时,程序就会报错:

An error occurred (MySQL execute: Duplicate entry ‘myuser’ for key 1).

如此,系统效率会得到提高。虽然,这是纯技术性的错误提示, 我们将稍侯予以解决。

避免泄漏过多服务器细节

上面出现的报错多是mysql服务器报错,可能会泄漏一些敏感信息,如数据库名,数据库地址,甚至数据表文件的存储地址都会被显示,这是很危险的。因此,这些信息我们并不希望被显示,除非我们就是用户,或者是在调试。如此,我们可以修改fail()函数,把错误信息显示为用户可见的内容。

// 是否为debug模式,如果是,会显示敏感信息。

$debug = TRUE;

function fail($pub, $pvt = '')

{

global $debug;

$msg = $pub;

if ($debug && $pvt !== '')

$msg .= ": $pvt";

/* $pvt 可能会含有敏感信息,比如需要隐藏掉,或者需要编码才能被html正确显示的内容。*/

exit("An error occurred ($msg).\n");

}

// 是否为debug模式,如果是,会显示敏感信息。

$debug = TRUE;

function fail($pub, $pvt = '')

{

global $debug;

$msg = $pub;

if ($debug && $pvt !== '')

$msg .= ": $pvt";

/* $pvt 可能会含有敏感信息,比如需要隐藏掉,或者需要编码才能被html正确显示的内容。*/

exit("An error occurred ($msg).\n");

}

需要注意的,不管是apache还是php,默认情况下是会显示所有调试信息的。所以,作为一个程序员,我们的职责是防止这些信息被泄漏,就跟我们设置了debug模式一样,这对程序员或者服务器运维人员来说至关重要。默认情况下,要把$debug值设置为false,但我们的例子作为测试来说,将继续使用true.

如何区分mysql报错

我们需要去辨别mysql报错,以确定用户是否已经存在于数据库中,如果已经存在,需要输出一个友好的错误提示。因为当我们插入用户的时候,不只是会有一种错误,当出现其它错误的时候,我们不能傻不愣瞪的提示相同的错误(用户已经存在)吧?

一种解决方法是在出现报错后执行一个针对该用户名的select查询,如果能够返回一行数据,说明用户确实一定存在了。实现方法如下:

if (!$stmt->execute()) {

$save_error = $db->error;

$stmt->close();

// 用户已经存在了?

($stmt = $db->prepare('select user from users where user=?'))

|| fail('MySQL prepare', $db->error);

$stmt->bind_param('s', $user)

|| fail('MySQL bind_param', $db->error);

$stmt->execute()

|| fail('MySQL execute', $db->error);

$stmt->store_result()

|| fail('MySQL store_result', $db->error);

if ($stmt->num_rows === 1)

fail('This username is already taken');

else

fail('MySQL execute', $save_error);

}

if (!$stmt->execute()) {

$save_error = $db->error;

$stmt->close();

// 用户已经存在了?

($stmt = $db->prepare('select user from users where user=?'))

|| fail('MySQL prepare', $db->error);

$stmt->bind_param('s', $user)

|| fail('MySQL bind_param', $db->error);

$stmt->execute()

|| fail('MySQL execute', $db->error);

$stmt->store_result()

|| fail('MySQL store_result', $db->error);

if ($stmt->num_rows === 1)

fail('This username is already taken');

else

fail('MySQL execute', $save_error);

}

这个方法确实奏效,而且也很可靠。但是,我们还有更简捷的实现方法,那就是使用mysql错误码:

if (!$stmt->execute()) {

if ($db->errno === 1062 /* ER_DUP_ENTRY */)

fail('额滴神,该用户已经存在了');

else

fail('MySQL execute', $db->error);

}

if (!$stmt->execute()) {

if ($db->errno === 1062 /* ER_DUP_ENTRY */)

fail('额滴神,该用户已经存在了');

else

fail('MySQL execute', $db->error);

}

在接下来的例子中,我们将使用这种简单的方法做演示。

魔法引号的处理

Magic quotes 开启后会自动转义输入的数据。其中,所有的单引号(’)、双引号(”)、反斜线、和 NULL 字符都会被转义(增加个反斜线),其实这操作本质上调用的是 addslashes 函数。

这对程序员来说固然是一个很好的事情,省却了我们过滤的麻烦。但是,当用户输入用户名和密码中含有这些字符时,我们从$_POST中获取到的内容是不是也会被addslashes了呢?

这就需要我们去做判断,示例如下:

function get_post_var($var)

{

$val = $_POST[$var];

if (get_magic_quotes_gpc())

$val = stripslashes($val);

return $val;

}

function get_post_var($var)

{

$val = $_POST[$var];

if (get_magic_quotes_gpc())

$val = stripslashes($val);

return $val;

}

接下来,我们将用这个函数取post过来的数据,而不是单纯的$_POST数组。

(待续)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值