OBJUI

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

2024-04-25 19:06:46 56

一、什么是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';
}


更多精彩,请关注公众号

微信公众号

评论:
热门文章: