使用HTTP基本或摘要认证

希望利用PHP用密码保护网站的某些部分。不是将密码保存在一个外部文件中并由Web服务器来处理认证,而是希望在一个PHP程序中实现密码认证逻辑。
function validate($user, $pass){

/*可以替换为适当的用户名和密码检查,如检查一个数据库*/
$users = array('david'=>'fadj&32',
			   'admin'=>'aadmin');
if(isset($users[$user])&&($users[$user] === $pass)){
	echo $user;
	echo '<br>';
	echo $pass;
	return true;
}else{
	return false;
}

}
使用验证函数
if(!validate($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'])){
http_response_code(401);
header('WWW-Authenticate: Basic realm="My Website"');
echo "You need to enter a valid username and password.";
exit();
}
使用摘要认证
// 定义用户数组,包含用户名和对应的密码(这里密码是简单文本,实际应用中应为哈希值)
$users = array('david'=>'fadj&32', 'admin'=>'aadmin');

// 定义认证领域(realm),用于区分不同的受保护区域
$realm = 'My Website';

// 尝试验证Digest认证的用户名
$username = validate_digest($realm, $users);

// 输出验证结果
print "Hello, " . htmlentities($username);

// validate_digest函数:验证Digest认证请求
function validate_digest($realm, $users){
// 检查是否设置了PHP_AUTH_DIGEST服务器变量,即是否有Digest认证头
if(! isset($_SERVER['PHP_AUTH_DIGEST'])){
// 如果没有,发送Digest认证请求头给客户端
send_digest($realm);
}

// 解析Digest认证头,获取用户名
$username = parse_digest($_SERVER['PHP_AUTH_DIGEST'], $realm, $users);
// 如果解析失败(即用户名不存在或认证失败),则再次发送Digest认证请求头
if($username === false){
    send_digest($realm);
}

// 返回验证通过的用户名
return $username;

}

// send_digest函数:发送401 Unauthorized响应和Digest认证头给客户端
function send_digest($realm){
// 设置HTTP响应状态码为401
http_response_code(401);
// 生成nonce值(一个唯一的标识符,用于防止重放攻击)
$nonce = md5(uniqid());
// 生成opaque值(一个不透明的字符串,由服务器指定,客户端在后续请求中回传)
$opaque = md5($realm);
// 发送WWW-Authenticate头,指示客户端使用Digest认证
header("WWW-Authenticate: Digest realm="$realm" qop="auth" nonce="$nonce" opaque="$opaque"");
// 输出错误信息
echo "You need to enter a valid username and password.";
// 终止脚本执行
exit();
}

// parse_digest函数:解析Digest认证头,验证请求
function parse_digest($digest, $realm, $users){
// 初始化一个数组,用于存储解析出的Digest认证信息
$digest_info = array();
// 定义需要解析的Digest认证头字段
$required_parts = array('username', 'uri', 'nonce', 'cnonce', 'response');
// 遍历这些字段,尝试从$digest字符串中解析出它们的值
foreach($required_parts as $part){
// 使用正则表达式匹配字段名和值,并捕获值部分
if(preg_match('/'.$part.'=(['"]?)(.*?)\1/', $digest, $match)){
// 将解析出的值存入$digest_info数组
$digest_info[$part] = $match[2];
}else{
// 如果任何一个字段解析失败,则返回false
return false;
}
}

// 检查是否存在qop字段,且值为auth
if(preg_match('/qop=auth(,|$)/', $digest)){
    $digest_info['qop'] = 'auth';
}else{
    // 如果没有,则返回false
    return false;
}

// 检查是否存在nc字段,并捕获其值
if(preg_match('/nc=([0-9a-f]{8})(,|$)/', $digest, $match)){
    $digest_info['nc'] = $match[1];
}else{
    // 如果没有,则返回false
    return false;
}

// 根据Digest认证算法计算请求的摘要值
$A1 = $digest_info['username'] . ':' . $realm . ':' . $users[$digest_info['username']];
$A2 = $_SERVER['REQUEST_METHOD'] . ':' . $digest_info['uri'];
$request_digest = md5(implode(':', array(md5($A1), $digest_info['nonce'], $digest_info['nc'],
                                        $digest_info['cnonce'], $digest_info['qop'], md5($A2))));

// 比较计算出的摘要值和客户端提供的响应值
if($request_digest != $digest_info['response']){
    // 如果不匹配,则返回false
    return false;
}

// 如果所有检查都通过,则返回用户名
return $digest_info['username'];

}

使用基本认证强制注销
if(!validate($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'])){
$realm = 'My Website for '.date('Y-m-d');
http_response_code(401);
header('WWW-Authenticate: Basic realm="'.$realm.'"');
echo "You need to enter a valid username and password.";
exit();
}

自用户上一次请求一个受保护页面之后已经超过了15分钟,则强制注销

function validate_date($user,$pass){
$db =new PDO('sqlite:/databases/users');
// 准备和执行
$st = $db->prepare('SELECT password, last_access FROM users WHERE user LIKE ?');
$st->execute(array($user));
if($ob=$st->fetchobject()){
if($ob->password==$pass){
$now=time();
if(($now-$ob->last_access)>(15*60)){
return false;
}else{
//更新上一次访问时间
$st2 =$db->prepare('UPDATE users SET last_access = "now" WHERE user LIKE ?');
$st2->execute(array($user));
return true;
}
}
}
return false;
}

posted @   kksllss  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示