• JWT实战:使用axios+PHP实现登录认证


    文中涉及的重要知识点有:

    因此在阅读这边文章之前,请先了解以上知识点以及JWT的基本概念,这样你会很快理解我们这篇文章中的实例代码。

    准备

    在本站上篇文章《有关JWT(Json Web Token)的那些事》有介绍用户登录鉴权流程:

    • 用户使用用户名密码来请求服务器
    • 服务器进行验证用户的信息
    • 服务器通过验证发送给用户一个token
    • 客户端存储token,并在每次请求时附送上这个token值
    • 服务端验证token值,并返回数据

    那么现在我们就按这个流程开始。

    HTML

    我们的HTML结构是这样的:一个登录表单,供用户输入用户名和密码,以及提交按钮;一个是登录成功后的显示信息。

    <div id="showpage" style="display: none">
      <div class="form-group">
        <label for="username">用户名</label>
        <input type="text" class="form-control" id="username" placeholder="请输入用户名">
      </div>
      <div class="form-group">
        <label for="password">密码</label>
        <input type="password" class="form-control" id="password" placeholder="请输入密码">
      </div>
      <button type="submit" id="sub-btn" class="btn btn-default">登录</button>
    
        <br/>
        <p class="bg-warning" style="padding: 10px;">演示用户名和密码都是<code>demo</code></p>
    </div>
    <div id="user" style="display: none"><p>欢迎<strong id="uname"></strong>,您已登录,<a href="javascript:;" id="logout">退出>></a></p></div>

    Javascript

    前端Javascript异步请求,我们使用Axios库,当然你也可以使用jQuery的Ajax方法。

    首先引入axios库:

    <script src="https://cdn.bootcss.com/axios/0.17.1/axios.min.js"></script>

    按照流程,1.提交登录表单,发送用户名和密码到PHP后端,2.后端验证成功后,会发送一个token给前端,3.前端再拿这个token去请求需要用户权限访问,4.后端验证toen,鉴权,返回相应结果。下面的js代码实现了1,3两步。

    <script>
        document.querySelector('#sub-btn').onclick = function() {
        let username = document.querySelector('#username').value;
        let password = document.querySelector('#password').value;
        
        var params = new URLSearchParams();
        params.append('user', username);
        params.append('pass', password);
     
        axios.post(
            'user.php?action=login',
            params
        )
        .then((response) => {
            if (response.data.result === 'success') {
                // 本地存储token
                localStorage.setItem('jwt', response.data.jwt);
                // 把token加入header里
                axios.defaults.headers.common['X-token'] = response.data.jwt;
                axios.get('user.php').then(function(response) {
                    if (response.data.result === 'success') {
                        document.querySelector('#showpage').style.display = 'none';
                        document.querySelector('#user').style.display = 'block';
                        document.querySelector('#uname').innerHTML = response.data.info.data.username;
                    } else {
                    }
                });
            } else {
                console.log(response.data.msg);
            }
        })
        .catch(function (error) {
            console.log(error);
        });
    }
     
    </script>

    很显然,当登录成功后,立马使用本地存储token,然后把这个token放在请求头header里,再次去请求后端另一个用户信息接口,如果成功了就显示用户信息。

    如果要退出登录,我们不需要再次去请求后端接口,直接前端清空本地存储就OK了。

    document.querySelector('#logout').onclick = function() {
        localStorage.removeItem('jwt');
        document.querySelector('#showpage').style.display = 'block';
        document.querySelector('#user').style.display = 'none';
    }

    登录成功后,当我们刷新页面(再次请求需要登录后才能访问的页面),需要进行判断,判断本地存储中是否有token,如果有token,那就拿去给后端接口验证下token是否合法,如果没问题就显示用户相关信息,如果验证失败,那可能是token过去或者伪造的token等原因。

    let jwt =  localStorage.getItem('jwt');
     
    if (jwt) {
        axios.defaults.headers.common['X-token'] = jwt;
        axios.get('user.php')
        .then(function (response) {
            if (response.data.result === 'success') {
                document.querySelector('#showpage').style.display = 'none';
                document.querySelector('#user').style.display = 'block';
                document.querySelector('#uname').innerHTML = response.data.info.data.username;
            } else {
                document.querySelector('#showpage').style.display = 'block';
                console.log(response.data.msg);
            }
        })
        .catch(function (error) {
            console.log(error);
        });
    } else {
        document.querySelector('#showpage').style.display = 'block';
    }

    PHP

    后端我们使用了一个专门的JWT库:php-jwt

    使用composer安装php-jwt,接收到登录用户名和密码后,PHP验证用户名和密码是否正确(实际开发中应该结合数据库,从数据库里拿用户名和密码比对,本实例为了演示只做简单验证),如果用户名和密码准确无误,那么就签发token,在token中,我们可以定义token的签发者、过期时间等等,并返回给前端。注意在签发token时,我们需要定义一个密钥,这个密钥是一个私钥,实际应用中是保密的不可告诉别人。

    require 'vendor/autoload.php';
     
    use FirebaseJWTJWT;
     
    define('KEY', '1gHuiop975cdashyex9Ud23ldsvm2Xq'); //密钥
     
    $res['result'] = 'failed';
     
    $action = isset($_GET['action']) ? $_GET['action'] : '';
     
    if ($action == 'login') {
        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
            $username = htmlentities($_POST['user']);
            $password = htmlentities($_POST['pass']);
     
            if ($username == 'demo' && $password == 'demo') { //用户名和密码正确,则签发tokon
                $nowtime = time();
                $token = [
                    'iss' => 'http://www.helloweba.net', //签发者
                    'aud' => 'http://www.helloweba.net', //jwt所面向的用户
                    'iat' => $nowtime, //签发时间
                    'nbf' => $nowtime + 10, //在什么时间之后该jwt才可用
                    'exp' => $nowtime + 600, //过期时间-10min
                    'data' => [
                        'userid' => 1,
                        'username' => $username
                    ]
                ];
                $jwt = JWT::encode($token, KEY);
                $res['result'] = 'success';
                $res['jwt'] = $jwt;
            } else {
                $res['msg'] = '用户名或密码错误!';
            }
        }
        echo json_encode($res);
     
    } else {
        $jwt = isset($_SERVER['HTTP_X_TOKEN']) ? $_SERVER['HTTP_X_TOKEN'] : '';
        if (empty($jwt)) {
            $res['msg'] = 'You do not have permission to access.';
            echo json_encode($res);
            exit;
        }
     
        try {
            JWT::$leeway = 60;
            $decoded = JWT::decode($jwt, KEY, ['HS256']);
            $arr = (array)$decoded;
            if ($arr['exp'] < time()) {
                $res['msg'] = '请重新登录';
            } else {
                $res['result'] = 'success';
                $res['info'] = $arr;
            }
        } catch(Exception $e) {
            $res['msg'] = 'Token验证失败,请重新登录';
        }
     
        echo json_encode($res);
    }

    用户每次请求都要带上后端签发的token,后端获取请求中的token,PHP中使用$_SERVER['HTTP_X_TOKEN']就可以获取token值。这个X_TOKEN就是在我们前端的请求header头信息中。

    然后PHP拿到这个token后,解密分析token值,返回给前端即可。

    结束语

    以上就是整个JWT的实战应用,我们可以看到,在用户鉴权的过程中并没有使用Session或者Cookie,服务端无需存储用户会话信息。只用了一个Token串,建立前后端的验证的数据传递,实现了有效的登录鉴权过程。

    附录:

    PHP 中 firebase/php-jwt RS256 公私钥生成指南

    工具:git命令行 https://git-scm.com/downloads

    # 2048 这个长度可根据实际情况调整
    openssl genrsa -out pri_key.pem 2048

    完成后桌面就会出现 pri_key.pem 文件,撸开看看,开头和结尾一定是

    -----BEGIN RSA PRIVATE KEY-----
    ...省略...
    -----END RSA PRIVATE KEY-----

    接着再输入一句命令拿到公钥就能玩儿了:

    openssl rsa -in pri_key.pem -pubout -out pub_key.pem

    同样的,在桌面会再多出一个 pub_key.pem 文件,开头结尾一定是:

    -----BEGIN PUBLIC KEY-----
    ...省略...
    -----END PUBLIC KEY-----
  • 相关阅读:
    堆和栈的区别
    熟悉熟悉常用的几个算法用JS的实现
    JS设置CSS样式的几种方式
    javascript的基本语法、数据结构
    DOM的概念及子节点类型
    三列自适应布局
    Javascript中括号“[]”的多义性
    Javascript中大括号“{}”的多义性
    Javascript小括号“()”的多义性
    JavaScript 开发者经常忽略或误用的七个基础知识点
  • 原文地址:https://www.cnblogs.com/jiafeimao-dabai/p/9638994.html
Copyright © 2020-2023  润新知