项目场景
在这个前后端分离的时代,前后端身份验证经常会成为自学者不可绕开的问题之一。这个问题也一直困扰了我很久,网上查阅到的资料都点到为止十分的尴尬,可能大佬们都一点及通,本人想了很久才结合到了demo中。
demo思路:
token使用的是JWT,相关说明百度很多我就不复制过来了
环境:
前端: html5, js,jq
后端: php
数据库: Mysql
表结构:
最后丢上代码
前端:
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录</title>
</head>
<body>
登录:</br>
code:<br><input id="int" type="text"><br/>
user:<br>
<input id="user" type="text">
<button id="btn">click</button>
</body>
</html>
<script src="./jq.js" ></script>
<script>
$('#btn').click(()=>{
// console.log($('#int').val());
let int = $('#int').val()
let user = $('#user').val()
// console.log(user);
$.ajax({
type: 'post',
url: './api.php',
data: {
act : 'login',
int,
user
},
success:(ret)=>{
console.log(ret);
let res = JSON.parse(ret)
if(res.code == '200'){
//验证成功,保存token
localStorage.setItem('token',res.token)
location.href = './1.html'
}else(
alert('登录错误')
)
}
})
})
</script>
1.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1> 展示</h1>
<pre id="a"></pre>
</body>
</html>
<script src="./jq.js"></script>
<script >
let token = localStorage.getItem('token');
console.log(token);
if(token == null || token == undefined){
location.replace('./login.html')
}else{
$.ajax({
type: 'post',
url: './api.php',
data: {
act : 'checktoken',
token
},
success:(ret)=>{
console.log(ret);
let res = JSON.parse(ret)
if(res.code == '200'){ //token验证成功
console.log(res.data);
//保存token
localStorage.setItem('token',res.token)
let data = res.data
// 显示时间戳
$('#a').html(data.exp)
}else{ //token验证失败
//移除本地token
localStorage.removeItem('token')
//跳转到登录页面
location.replace('./login.html')
}
}
})
}
</script>
php:
api.php
<?php
function sel($sql){
$conn = new mysqli("localhost", "root", "root", 'demo');
// Check connection
echo $username;
if ($conn->connect_error) {
die("连接失败: " . $conn->connect_error);
}
// $sql = " SELECT * FROM admin ";
$result = $conn->query($sql);
$res = [];
if ($result->num_rows > 0) {
// 输出数据
$arr = [];
while($row = $result->fetch_assoc()) {
$arr[] = $row;
}
$res['code'] = '200';
$res['data'] = $arr;
} else {
$res['code'] = '201';
$res['msg'] = '0 结果';
}
$conn->close();
return json_encode($res,JSON_UNESCAPED_UNICODE);
}
function add($sql){
$conn = new mysqli("localhost", "root", "root", 'demo');
if ($conn->connect_error) die("连接失败: " . $conn->connect_error);
// $sql = " INSERT INTO `admin`( `Token`, `Ative`) VALUES ('测试','2') ";
$result = $conn->query($sql);
return $result;
$conn->close();
}
function updata($sql){
$conn = new mysqli("localhost", "root", "root", 'demo');
if ($conn->connect_error) die("连接失败: " . $conn->connect_error);
$result = $conn->query($sql);
return $result;
$conn->close();
}
// print_r($_POST);
$act = $_POST['act'];
// echo $_POST['act'];
if( $act != null ) {
switch ($act) {
case 'login': //登录
$u = $_POST['int']; //前端页面传过来的辨别符
$user = $_POST['user']; //用户名
$ret = []; //该数组返回给前端
if( $u == 'aaa' ){ //验证通过
//引入token文件
require_once('./token.php');
//配置token内容
$payload_test=array(
'iss'=>'Daisy', //签发人
'iat'=>time(), //当前时间
'exp'=>time()+120, //过期时间
'nbf'=>time(), //在这之前不能使用
'sub'=>'www.juhuaguai.club',
'jti'=>md5(uniqid('JWT').time()), //token
'user'=>$user
);;
//生成token
$token_test=Jwt::getToken($payload_test);
//向数据库中添加token记录
$sql = " INSERT INTO `admin`( `Token`,`User`,`LoginTime`, `Ative`) VALUES ('".$token_test."','".$user."','".$payload_test['iat']."','1') ";
add($sql); //add()是自定义函数,往数据库添加记录
$ret['code'] = '200';
$ret['token'] = $token_test;
}else{ //验证失败
$ret['code'] = '201';
$ret['msg'] = '密码错误';
}
//以json格式输出
echo json_encode($ret,JSON_UNESCAPED_UNICODE);
break;
case 'checktoken': //验证
$token = $_POST['token']; //需要验证的token
//根据前端传递的 token 查询数据库记录
$sql = "SELECT * FROM `admin` WHERE `Token` ='".$token."'";
$sqlres = json_decode(sel($sql),true);
if($sqlres['code'] == '200' ){ //查询成功
$ret = [];
require_once('./token.php'); //引用token文件
$res=Jwt::verifyToken($token);
if($res != false ){
$ret['code'] = '200';
$ret['data'] = $res;
$res['exp'] = time()+360;
$newtoen = Jwt::getToken($res);
$ret['token'] = $newtoen;
$sql = "UPDATE `admin` SET `Token` = '".$newtoen."' WHERE `admin`.`Id` = ".$sqlres['data']['0']['Id'].";";
updata($sql);
}else{ //查询失败
$ret['code'] = '201';
$ret['msg'] = '重新登录';
}
echo json_encode($ret,JSON_UNESCAPED_UNICODE);
}
break;
default:
# code...
break;
}
}
token.php
<?php
/**
* PHP实现jwt
*/
class Jwt {
//头部
private static $header=array(
'alg'=>'HS256', //生成signature的算法
'typ'=>'JWT' //类型
);
//使用HMAC生成信息摘要时所使用的密钥
private static $key='123456';
/**
* 获取jwt token
* @param array $payload jwt载荷 格式如下非必须
* [
* 'iss'=>'jwt_admin', //该JWT的签发者
* 'iat'=>time(), //签发时间
* 'exp'=>time()+7200, //过期时间
* 'nbf'=>time()+60, //该时间之前不接收处理该Token
* 'sub'=>'www.admin.com', //面向的用户
* 'jti'=>md5(uniqid('JWT').time()) //该Token唯一标识
* ]
* @return bool|string
*/
public static function getToken($payload)
{
if(is_array($payload))
{
$base64header=self::base64UrlEncode(json_encode(self::$header,JSON_UNESCAPED_UNICODE));
$base64payload=self::base64UrlEncode(json_encode($payload,JSON_UNESCAPED_UNICODE));
$token=$base64header.'.'.$base64payload.'.'.self::signature($base64header.'.'.$base64payload,self::$key,self::$header['alg']);
return $token;
}else{
return false;
}
}
/**
* 验证token是否有效,默认验证exp,nbf,iat时间
* @param string $Token 需要验证的token
* @return bool|string
*/
public static function verifyToken($Token)
{
$tokens = explode('.', $Token);
if (count($tokens) != 3)
return false;
list($base64header, $base64payload, $sign) = $tokens;
//获取jwt算法
$base64decodeheader = json_decode(self::base64UrlDecode($base64header), JSON_OBJECT_AS_ARRAY);
if (empty($base64decodeheader['alg']))
return false;
//签名验证
if (self::signature($base64header . '.' . $base64payload, self::$key, $base64decodeheader['alg']) !== $sign)
return false;
$payload = json_decode(self::base64UrlDecode($base64payload), JSON_OBJECT_AS_ARRAY);
//签发时间大于当前服务器时间验证失败
if (isset($payload['iat']) && $payload['iat'] > time())
return false;
//过期时间小宇当前服务器时间验证失败
if (isset($payload['exp']) && $payload['exp'] < time())
return false;
//该nbf时间之前不接收处理该Token
if (isset($payload['nbf']) && $payload['nbf'] > time())
return false;
return $payload;
}
/**
* base64UrlEncode https://jwt.io/ 中base64UrlEncode编码实现
* @param string $input 需要编码的字符串
* @return string
*/
private static function base64UrlEncode($input)
{
return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
}
/**
* base64UrlEncode https://jwt.io/ 中base64UrlEncode解码实现
* @param string $input 需要解码的字符串
* @return bool|string
*/
private static function base64UrlDecode($input)
{
$remainder = strlen($input) % 4;
if ($remainder) {
$addlen = 4 - $remainder;
$input .= str_repeat('=', $addlen);
}
return base64_decode(strtr($input, '-_', '+/'));
}
/**
* HMACSHA256签名 https://jwt.io/ 中HMACSHA256签名实现
* @param string $input 为base64UrlEncode(header).".".base64UrlEncode(payload)
* @param string $key
* @param string $alg 算法方式
* @return mixed
*/
private static function signature($input, $key, $alg = 'HS256')
{
$alg_config=array(
'HS256'=>'sha256'
);
return self::base64UrlEncode(hash_hmac($alg_config[$alg], $input, $key,true));
}
}
全栈自学的盆友们奥利给!