轶哥博客

九零后全栈开发者,未来学学者。想做的事情太多,既要环游世界,也妄图改变点什么的强迫症。

HTML5用户身份认证源代码:注册、登录、会话保持的解决方案

  本文是针对PHP新手的Session教程 —— 用户注册、登录、身份认证。为了方便浏览,以下代码均为示例代码。正式项目中需要修改完善强化其安全性。

MySQL数据库管理员表 admin.sql

# table admin
# ------------------------------------------------------------

CREATE TABLE `admin` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(30) DEFAULT NULL,
  `password` varchar(500) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `admin` (`id`, `username`, `password`)
VALUES
    (1,'admin','admin');

用户注册表单 register.html

<form id="user_register">
<label for="input_Username">用户名</label>
<input type="text" id="input_Username" name="input_Username" placeholder="用户名" required autofocus>
<label for="input_Password">密码</label>
<input type="password" id="input_Password" name="input_Password" placeholder="密码" required>
<button type="button" id="button_register">注册</button>
</form>

用户注册Ajax代码 register.js

$("#button_register").click(function () {
        $.ajax({
            cache: false,
            url:"php/register.php",
            type: "POST",
            async: false,
            data:$("#user_register").serialize(),
            dataType:"json",
            success: function(data){
                //处理返回结果
                switch(data.error){
                    case "false":
                        alert("注册成功!" + "注册的用户ID为:" + data.user_id);
                        break;
                    case "Insert error":
                        alert("数据写入失败,请联系管理员");
                        break;
                    case "Username exist":
                        alert("用户名已经存在");
                        break;
                    default:
                        alert("注册出错,请联系管理员");
                }
            },
            error: function (data) {
                alert("数据获取失败,请检查网络连接。" );
                console.info(data);
            }
        });
});

PHP数据库连接文件 connect.php

<?php
/**


数据库连接文件
*

PDO连接MySQL数据库

@author      王轶<a@wyr.me>

@version     0.1

@copyright   BSD
*/
try {
 $db_ip = "localhost";   //数据库IP地址
 $db_name = "";          //数据库名称
 $db_user = "";          //数据库用户名
 $db_pass = "";          //数据库密码
 $PDO = new PDO('mysql:host='.$db_ip.';dbname='.$db_name, $db_user, $db_pass, array(
 PDO::ATTR_PERSISTENT =&gt; true , // 设置数据库连接为持久连接
 PDO::ATTR_EMULATE_PREPARES =&gt; false , // 防止SQL注入
 PDO::ATTR_ORACLE_NULLS =&gt; true , // 设置当字符串为空转换为 SQL 的 NULL
 PDO::ATTR_ERRMODE , // 设置抛出错误
 PDO::ERRMODE_EXCEPTION // 设置抛出错误
 ));//PDO数据库连接代码
} catch (PDOException $e) {
 print "Error!: " . $e->getMessage() . "<br/>";
 die();
}


/**


格式化SQL数据类型
*

@param string $theValue 目标SQL数据类型

@param mixed $theType 传入需要变更类型的数据

@return mixed
*/
function GetSQLValueString($theValue, $theType)
{
 switch ($theType) {
 case "text":
     $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
     break;
 case "long":
 case "int":
     $theValue = ($theValue != "") ? intval($theValue) : "NULL";
     break;
 case "double":
     $theValue = ($theValue != "") ? doubleval($theValue) : "NULL";
     break;
 case "date":
     $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
     break;
 }
 return $theValue;
}

用户注册后端处理PHP代码 register.php

  其它语言相似,判断处理后返回json。

<?php
require_once("connect.php");//加载数据库连接文件
//判断是否接收到了表单数据
if(isset($_POST["input_Username"])) {
    //写入数据前检测用户名是否存在
    $username = $_POST["input_Username"];
    $Check_Username = $PDO->query("SELECT id FROM admin WHERE username=".GetSQLValueString($username, "text"));
    $Check_Username_result = $Check_Username->fetch(PDO::FETCH_ASSOC);
    if ($Check_Username_result["id"] == null) {
        //如果用户名不存在
        $password = md5(md5("aq1ja".$_POST['input_Password']."jfsah2"));//第一次执行md5的时候在前后加上安全码,再次执行md5防止md5破解
        //这个方法虽然能够加强密码强度防止暴力破解,但有弊端,仅供参考
        $insertSQL = sprintf("INSERT INTO admin SET username = %s, password = %s",
            GetSQLValueString($username, "text"),
            GetSQLValueString($password, "text"));
        $Result = $PDO->exec($insertSQL);//执行数据库写入操作
        $back_id = $PDO->lastInsertId();//获取写入的数据id
        if ($Result == "1") {//如果成功写入一条数据
            $doc = array("error" => "false", "user_id" => $back_id);//返回当前注册用户的id,可能其它业务逻辑需要
        } else {
            $doc = array("error" => "Insert error");
        }
    }else{
        $doc = array("error" => "Username exist");//返回错误提示:用户名已经存在
    }
}else{
    $doc = array("error" => "Illegal request");//没有接收到POST过来的表单数据,有可能是非法访问
}

$PDO = null;//关闭数据库链接
echo json_encode($doc);//返回为json字符串

用户登录表单 sign.html

<form id="user_sign">
    <label for="input_Username" class="sr-only">用户名</label>
    <input type="text" id="input_Username" name="input_Username" placeholder="用户名" required autofocus>
     <label for="input_Password" class="sr-only">密码</label>
     <input type="password" id="input_Password" name="input_Password" placeholder="密码" required>
     <div class="checkbox">
           <label>
                  <input type="checkbox" name="remember_me" value="remember-me"> 保持登录状态
           </label>
     </div>
     <button type="button" id="button_sign">登录</button>
</form>

用户登录Ajax代码 sign.js

        //用户登录按钮按下-提交登录表单:
        $("#button_sign").click(function () {
            $.ajax({
                cache: false,
                url: "php/sign.php",
                type: "POST",
                async: false,
                data: $("#user_sign").serialize() + "&sign=on",//sign=on是登录,sign=off是注销
                dataType: "json",
                success: function (data) {
                    //处理返回结果
                    switch (data.error) {
                        case "false":
                            if (window.sessionStorage && window.localStorage) {
                                if(data.type == "time_on"){
                                    window.localStorage["user_access"] = data.user_access;
                                }else{
                                    window.sessionStorage["user_access"] = data.user_access;
                                }
                                alert("登录成功!" + "返回的随机字符串是:" + data.user_access);
                            } else {
                                alert("浏览器不支持localStorage或sessionStorage,换chrome吧");
                            }
                            break;
                        case "Username or Password error":
                            alert("用户名或密码错误");
                            break;
                        default:
                            alert("登录出错,请联系管理员");
                    }
                },
                error: function (data) {
                    alert("数据获取失败,请检查网络连接。");
                    console.info(data);
                }
            });
        });

用户登录后端处理 sign.php

<?php
require_once("connect.php");//加载数据库连接文件
session_start(); //标志Session的开始
//判断是否接收到了表单数据
if(isset($_POST["sign"]) && $_POST["sign"] == "on") {
    date_default_timezone_set("PRC");
    $select = $PDO -> query("SELECT * FROM admin WHERE username = ".GetSQLValueString($_POST["input_Username"], "text"));
    $back = $select -> fetch(PDO::FETCH_ASSOC);
    if($back["username"] == $_POST["input_Username"] && $back["password"] == md5(md5("aq1ja".$_POST['input_Password']."jfsah2"))){
        $_SESSION["username"] = $back["username"];
        $_SESSION["user_id"] = $back["id"];
        $_SESSION["star_time"] = time();
        $_SESSION["user_access"] = (string)md5(rand(100000,999999)."wiq");//生成一个随机字符串
        if(isset($_POST["remember_me"]) && $_POST["remember_me"] == "remember-me"){
            //$doc = array("error" => "false" , "type" => "time_on" , "user_access" => $_SESSION["user_access"]);这里的user_access需要写入数据库
        }else{
            $doc = array("error" => "false" , "type" => "time_off", "user_access" => $_SESSION["user_access"]);
        }
    }else{
        $doc = array("error" => "Username or Password error");
    }
}else if(isset($_POST["sign"]) && $_POST["sign"] == "off"){
    unset($_SESSION["username"]);
    unset($_SESSION["user_access"]);
    unset($_SESSION["user_id"]);
    unset($_SESSION["star_time"]);
    $doc = array("error" => "false");
}else{
    $doc = array("error" => "Illegal request");//没有接收到POST过来的表单数据,有可能是非法访问
}

$PDO = null;//关闭数据库链接
echo json_encode($doc);//返回为json字符串

用户身份认证前端代码

<div id="show_on" style="display: none">
    <p id="show_text"></p>
    <a href="sign.html?sign=off">注销</a>
</div>
        //判断在线状态:
        if (window.sessionStorage && window.localStorage) {
            function sign(user_access,type){
                $.ajax({
                    cache: false,
                    url: "php/access.php",
                    type: "POST",
                    async: false,
                    data: {"user_access":user_access},
                    dataType: "json",
                    success: function (data) {
                        //处理返回结果
                        switch (data.error) {
                            case "false":
                                $("#show_on").show();
                                $("#user_sign").hide();
                                $("#show_text").text("身份认证成功!当前用户:" + data.username + " id: " + data["user_id"]);
                                break;
                            case "No_time":
                                if(type == "session"){
                                    window.sessionStorage.removeItem("user_access");
                                }else{
                                    window.localStorage.removeItem("user_access");
                                }
                                alert("身份认证过期,请重新登录");
                                break;
                            default:
                                alert("数据出错,请联系管理员");
                        }
                    },
                    error: function (data) {
                        alert("数据获取失败,请检查网络连接。");
                        console.info(data);
                    }
                });
            }
            var session = window.sessionStorage;
            if(session["user_access"]){
                sign(session["user_access"],"session");
            }else if(window.localStorage["user_access"]){
                sign(window.localStorage["user_access"],"local");
            }
        } else {
            alert("浏览器不支持localStorage或sessionStorage,换chrome吧");
        }

注销代码

$(function () {
        //获取URL中的参数 [分析GET过来的数据]
        function get(name){
            var geturl = window.location.search.substr(1).split("&");
            for(var n=0;n<geturl.length;n++){
                if(decodeURIComponent(name)==geturl[n].split("=")[0]){
                    return decodeURIComponent(geturl[n].split("=")[1]);
                }
            }
        }

    //注销:
    if(get("sign") == "off"){
        $.ajax({
            cache: false,
            url: "php/sign.php",
            type: "POST",
            async: false,
            data: $("#user_sign").serialize() + "&amp;sign=off",//sign=on是登录,sign=off是注销
            dataType: "json",
            success: function (data) {
                //处理返回结果
                switch (data.error) {
                    case "false":
                        alert("注销成功!");
                        break;
                    default:
                        alert("注销出错,请联系管理员");
                }
            },
            error: function (data) {
                alert("数据获取失败,请检查网络连接。");
                console.info(data);
            }
        });
    }
})

用户身份认证后端代码 access.php

<?php
session_start(); //标志Session的开始
error_reporting(0);//屏蔽报错
date_default_timezone_set("PRC");
if(isset($_POST["user_access"]) && $_POST["user_access"] == $_SESSION["user_access"]){
    if(time() - $_SESSION["star_time"] < 21600){
        $doc = array("error" => "false" , "username" => $_SESSION["username"], "user_id" => $_SESSION["user_id"]);
        //在这里编写认证后的业务逻辑
    }else{
        $doc = array("error" => "No_time");
    }
} else {
    $doc = array("error" => "No_time");
}
echo json_encode($doc);//返回为json字符串

代码使用说明

  以上代码仅为简单实例,这只是一个解决方案。安全性上还需要进一步完善。需要特别注意的是,上面的代码中的身份认证在用户关闭浏览器后随即失效,没有实现登录状态的长期保持,可以使用localStorage来保存本地的随机码(需要处理数据库配合)。在用户身份认证的时候比对数据库里面的信息判断localStorage中的随机码是否失效。