使用Axios+PHP+JWT实现登录认证

1789 2023年04月04日 后端

一、什么是JWT

JWT(JSON Web Token),顾名思义就是可以在Web上传输的token,这种token是用JSON格式进行format的。它是一个开源标准(RFC7519),定义了一个紧凑的自包含的方式在不同实体之间安全的用JSON格式传输信息。

官网:https://jwt.io/

二、JWT优缺点

优点:是在分布式系统中,很好地解决了单点登录问题,很容易解决了session共享的问题。

缺点:对分发出去的Token不可控,续签问题需要谨慎处理好。

三、JWT组成

一个JWT实际上就是一个字符串,它由三部分组成:头部、载荷与签名。

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIxMi4xMi4xMi4yMDIiLCJhdWQiM
SwibmJmIjoxNTQzNTcxNDgxLCJleHAiOjE1NDM1Nzg2NzEsImRhdGEiOnsidXNlcmlkI
joxLCJ1c2VybmFtZSI6ImFkb.LPKOTnUjSyKTjGVW2553ZuPh9yNRkW8_IuEtXDQc6sI 

1、头部(Header)

在header中通常包含了两部分:token类型和采用的加密算法。

{
"typ": "JWT",
"alg": "HS256"
}

JWS算法名称描述

JWS算法名称描述
HS256HMAC256HMAC with SHA-256
HS384HMAC384HMAC with SHA-384
HS512HMAC512HMAC with SHA-512
RS256RSA256RSASSA-PKCS1-v1_5 with SHA-256
RS384RSA384RSASSA-PKCS1-v1_5 with SHA-384
RS512RSA512RSASSA-PKCS1-v1_5 with SHA-512

对这部分内容使用 Base64 Url编码组成了JWT结构的第一部分。

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

2、载荷Payload 部分

{ 
"sub": "1", 
"iss": "http://localhost:8000/auth/login",
 "iat": 1451888119, 
"exp": 1454516119,
 "nbf": 1451888119, 
"jti": "37c107e4609ddbcc9c096ea5ee76c667" 
}

sub: 该JWT所面向的用户

iss: 该JWT的签发者

iat(issued at): 在什么时候签发的token

exp(expires): token什么时候过期

nbf(not before):token在此时间之前不能被接收处理

jti:JWT ID为web token提供唯一标识

上述的负载需要经过Base64 Url编码后作为JWT结构的第二部分。

eyJpc3MiOiIxMi4xMi4xMi4yMDIiLCJhdWQiMSwibmJmIjoxNTQzNTcxNDgxLCJleHAiOjE1NDM1Nzg2NzEsImRhdGEiOnsidXNlcmlkIjoxLCJ1c2VybmFtZSI6ImFkb

3、签名

jwt的第三部分是一个签证信息,这个签证信息算法如下:

base64UrlEncode(header) + "." + base64UrlEncode(payload)+secret 

这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

例如使用HMAC SHA256算法,那么签名应该使用下列方式创建:

Signature = HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) 

最终得出jwt的第三部分是:

eyJpc3MiOiIxMi4xMi4xMi4yMDIiLCJhdWQiMSwibmJmIjoxNTQzNTcxNDgxLCJleHAiOjE1NDM1Nzg2NzEsImRhdGEiOnsidXNlcmlkIjoxLCJ1c2VybmFtZSI6ImFkb

四、应用场景

1、用于向Web应用传递一些非敏感信息。例如完成加好友、下订单的操作等等。

2、用于设计用户认证和授权系统。

3、实现Web应用的单点登录。

五、示例

安装php-jwt

composer require firebase/php-jwt

login.php

require 'vendor/autoload.php';
use \Firebase\JWT\JWT;
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);
}

index.html

<xmp><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> </xmp>

index.js

    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);
    });
}
#退出登录
document.querySelector('#logout').onclick = function() {
    localStorage.removeItem('jwt');
    document.querySelector('#showpage').style.display = 'block';
    document.querySelector('#user').style.display = 'none';
}

#登录后带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';
}
评论

0 条评论
OBJUI公众号
热门文章
  • 使用Echarts画甘特图

    Echarts是一个非常强大的图表库, 下面我们来使用它来画甘特图,

  • go语言怎么连接mysql,并实现增删改查

    要使用Go语言连接MySQL,需要使用第三方库。常用的库包括: 这里以go-sql-driver/mysql为 […]

  • beego实现模块化开发

    Beego 框架可以通过模块化开发来提高项目的可维护性和可扩展性,可以将一个大型的应用划分为多个模块,每个模块独立维护,有自己的控制器、视图和模型等。

  • Beego实现JWT

    Beego是一个基于Go语言的Web框架,实现JWT认证可以通过beego的中间件机制来实现,下面是一个简单的 […]

  • 使用Axios+PHP+JWT实现登录认证

    JWT(JSON Web Token),顾名思义就是可以在Web上传输的token,这种token是用JSON格式进行format的。它是一个开源标准(RFC7519),定义了一个紧凑的自包含的方式在不同实体之间安全的用JSON格式传输信息。

  • PHP解决雪花算法ID在前端精度丢失的问题

    雪花算法(Snowflake)是一种用于生成全局唯一ID的算法,其基本思路是结合时间戳、机器ID、租约ID和序列号生成一个64位的数据,从而保证ID的唯一性。 雪花算法的生成过程如下:

  • 虚拟滚动实现页面百万数据滚动

    1.概念 虚拟滚动是一种优化长列表性能的技术,它通过按需渲染的方式,只渲染可见部分的列表项,而不是渲染整个列表 […]

  • go实现MD5

    在这个示例中,我们使用了Go标准库中的crypto/md5包来计算一个字符串的MD5值。首先,我们将字符串转换 […]

  • Linux服务器Rsync结合inotify同步文件

    一、实现效果 服务器A:192.168.161.150 (分布服务器)(rsync客户端+inotify) 服务器B:192.168.161.151 (WEB服务器1)(rsync服务端) 服务器C:192.168.161.152 (WEB服务器2)(rsync服务端) 说明:服务器A有文件更新,自动同步到服务器B和C

  • JavaScript实现滑动验证码

    滑动验证码(也叫做滑动拼图验证码)是一种用户交互形式的验证码,通常用于网页或移动应用的登录、注册或敏感操作。其 […]