3
头图

🎈 Token Authentication Process

  • As the most popular cross-domain authentication solution, JWT(JSON Web Token) is deeply loved by developers. The main process is as follows:
  • The client sends an account and password to request a login
  • The server receives the request to verify whether the account password is passed
  • After the verification is successful, the server will generate a unique token and return it to the client
  • The client receives token and stores it in cookie or localStroge
  • After each time the client sends a request to the server, it will pass cookie or header to carry the token
  • The server verifies the validity of token , and returns the response data after passing

基于 Token 认证流程

🎈 Token Authentication Advantages

  • Support cross-domain access: Cookie is not allowed to cross-domain access, this point Token the mechanism does not exist, the premise is that the transmitted user authentication information passes HTTP header transfer
  • Stateless: Token mechanism does not need to store session information on the server, because Token itself contains all the information of logged in users, only needs to be stored on the client cookie or local media storage status information
  • Wider applicability: As long as it is a client that supports the http protocol, it can use token authentication.
  • 无需考虑CSRF: cookietoken认证方式不会CSRFCSRF defense

🎈 JWT structure

  • A JWT is actually a string consisting of three parts: 头部 , 载荷 and 签名 . The middle is separated into three parts by a dot . . Note JWT there is no line break inside.

JWT 结构

  • 🎨header /header
  • header组成: token的类型JWT名称: HMACSHA256RSA
 {
  "alg": "HS256",
  "typ": "JWT"
}
  • 🎨Payload
  • Payload part is also a JSON object, which is used to store the actual data that needs to be passed. JWT Specify seven default fields for selection.
  • In addition to the default fields, you can add any fields you want. Generally, after a user logs in successfully, the user information is stored here
 iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT
 {
  "iss": "xxxxxxx",
  "sub": "xxxxxxx",
  "aud": "xxxxxxx",
  "user": [
        'username': '极客飞兔',
        'gender': 1,
        'nickname': '飞兔小哥' 
   ] 
}
  • 🎨Signature
  • The signature part is the data signature for the above header and payload data.
  • In order to ensure that the data is not tampered with, you need to specify a key, and this key is generally only known to you and stored on the server
  • The code to generate the signature is generally as follows:
 // 其中secret 是密钥
String signature = HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

🎈 Basic use of JWT

  • The client receives the JWT returned by the server, which can be stored in Cookie , or in localStorage
  • Then every time the client communicates with the server, it must bring this JWT
  • Save --- JWT in Cookie and send the request, so it cannot 跨域
  • A better practice is to put it in the HTTP request header information Authorization field
 fetch('license/login', {
    headers: {
        'Authorization': 'X-TOKEN' + token
    }
})

🎈 Actual combat: use JWT login authentication

  • Here use ThinkPHP6 integration JWT login authentication for actual combat simulation
  • 🎨 Install JWT extension
 composer require firebase/php-jwt
  • 🎨 Encapsulate the generated JWT and decryption method
 <?php
/**
 * Desc: JWT认证
 * Author: autofelix
 * Time: 2022/07/04
 */

namespace app\services;

use app\Helper;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

class JwtService
{
    protected $salt;

    public function __construct()
    {
        //从配置信息这种或取唯一字符串,你可以随便写比如md5('token')
        $this->salt = config('jwt.salt') || "autofelix";
    }

    // jwt生成
    public function generateToken($user)
    {
        $data = array(
            "iss" => 'autofelix',        //签发者 可以为空
            "aud" => 'autofelix',             //面象的用户,可以为空
            "iat" => Helper::getTimestamp(),   //签发时间
            "nbf" => Helper::getTimestamp(),   //立马生效
            "exp" => Helper::getTimestamp() + 7200, //token 过期时间 两小时
            "user" => [ // 记录用户信息
                'id' => $user->id,
                'username' => $user->username,
                'truename' => $user->truename,
                'phone' => $user->phone,
                'email' => $user->email,
                'role_id' => $user->role_id
            ]
        );
        $jwt = JWT::encode($data, md5($this->salt), 'HS256');
        return $jwt;
    }

    // jwt解密
    public function chekToken($token)
    {
        JWT::$leeway = 60; //当前时间减去60,把时间留点余地
        $decoded = JWT::decode($token, new Key(md5($this->salt), 'HS256'));
        return $decoded;
    }
}
  • 🎨After the user logs in, generate a JWT ID
 <?php
declare (strict_types=1);

namespace app\controller;

use think\Request;
use app\ResponseCode;
use app\Helper;
use app\model\User as UserModel;
use app\services\JwtService;

class License
{
    public function login(Request $request)
    {
        $data = $request->only(['username', 'password', 'code']);

        // ....进行验证的相关逻辑...
        $user = UserModel::where('username', $data['username'])->find();
        
        // 验证通过生成 JWT, 返回给前端保存
        $token = (new JwtService())->generateToken($user);

        return json([
            'code' => ResponseCode::SUCCESS,
            'message' => '登录成功',
            'data' => [
                'token' => $token
            ]
        ]);
    }
}
  • 🎨 Middleware to verify if the user is logged in
  • Register the middleware at middleware.php
 <?php
// 全局中间件定义文件
return [
    // ...其他中间件
    // JWT验证
    \app\middleware\Auth::class
];
  • After registering the middleware, improve the verification logic in the permission verification middleware
 <?php
declare (strict_types=1);

namespace app\middleware;

use app\ResponseCode;
use app\services\JwtService;

class Auth
{
    private $router_white_list = ['login'];

    public function handle($request, \Closure $next)
    {
        if (!in_array($request->pathinfo(), $this->router_white_list)) {
            $token = $request->header('token');

            try {
                // jwt 验证
                $jwt = (new JwtService())->chekToken($token);
            } catch (\Throwable $e) {
                return json([
                    'code' => ResponseCode::ERROR,
                    'msg' => 'Token验证失败'
                ]);
            }

            $request->user = $jwt->user;
        }

        return $next($request);
    }
}

江户川亮仔
1.2k 声望649 粉丝