token身份验证 前后端分离(PHP全栈)

项目场景

在这个前后端分离的时代,前后端身份验证经常会成为自学者不可绕开的问题之一。这个问题也一直困扰了我很久,网上查阅到的资料都点到为止十分的尴尬,可能大佬们都一点及通,本人想了很久才结合到了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));
    }
}

全栈自学的盆友们奥利给!

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
前后端分离是一种开发架构,将前端和后端的开发过程分开进行,前端主要负责用户界面展示和交互逻辑,后端负责数据处理和业务逻辑。而sa-token是一种单点登录的解决方案。 sa-token是基于Token的一个轻量级权限认证和申请令牌的工具,可以实现前后端分离项目的单点登录功能。它通过生成和验证Token令牌来实现用户身份认证。 在前后端分离的架构中,前端发送登录请求到后端,后端通过验证用户的账号和密码,如果验证成功,则生成一个Token令牌,并将该Token返回给前端。前端保存该Token,每次向后端发送请求时,需要在请求的Header中添加Token。后端通过获取请求Header中的Token,并进行验证,如果验证通过,则表示用户已登录。 sa-token提供了一些便捷的API,用于生成Token、验证Token、获取用户信息等操作。通过这些API,我们可以简便地实现单点登录的功能。sa-token还提供了一些定制化的配置选项,可以根据实际需求进行调整。 前后端分离的sa-token单点登录方案具有以下优点:简单易用、安全性高、扩展性强等。同时,由于前后端分离,使得前端和后端可以独立开发和部署,提高了开发效率和项目的可维护性。 总之,前后端分离sa-token单点登录是一种解决方案,通过Token令牌验证实现用户身份认证,适用于前后端分离的项目,具有诸多优点,可轻松实现单点登录功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值