thinkphp6: 前后端分离:用jwt+middleware做用户登录验证(php 8.1.1 / thinkphp v6.0.10LTS )

一,安装firebase/php-jwt扩展:

1,命令:
liuhongdi@lhdpc:/data/php/admapi$ composer require firebase/php-jwt
2,安装成功后的位置:
3,查看firebase/php-jwt的版本:
liuhongdi@lhdpc:/data/php/admapi$ composer show firebase/php-jwt

说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://www.cnblogs.com/architectforest

         对应的源码可以访问这里获取: https://github.com/liuhongdi/
         或: https://gitee.com/liuhongdi

说明:作者:刘宏缔 邮箱: 371125307@qq.com

二,前端vue代码:

Login.vue

<template>
  <div style="padding:20px;display: flex;align-items:center;justify-content: center;">
    <form :model="account"  style="margin-top:50px;400px;">
        <input v-model="account.username" placeholder="请输入用户名" style="392px;font-size:16px;" /><br/>
        <input v-model="account.password"  type="password" placeholder="请输入密码" style="392px;margin-top:10px;font-size:16px;" /><br/>
        <!--v-loading.fullscreen.lock="isLoading"-->
        <div    @click="login"  style="margin-top:10px; 100%;height:40px;line-height:40px;background: #ff0000;border-radius: 10px;">
          登录
        </div>
      <div    @click="info"  style="margin-top:10px; 100%;height:40px;line-height:40px;background: #ff0000;border-radius: 10px;">
        info
      </div>
        <div class="text-align-right">
        </div>
    </form>
  </div>
</template>

<script>
import { ref,  reactive } from "vue";
import { ElMessage } from "element-plus";
import { apiLogin,apiInfo,apiToken} from '@/api/api';
export default {
  name: "Login",
  setup() {
    const accountRef = ref(null);
    //表单字段
    const account = reactive({
      username: "",
      password: "",
    });

    //登录
    const login = async () => {
      console.log('begin login');

      var data = new FormData();
      data.append("username",account.username);
      data.append("password",account.password);
          apiLogin(data).then(res => {
            //成功
            if (res.code == 0) {
              //保存jwt token到本地
              localStorage.setItem('token', res.data.token);
              //提示
              ElMessage.success("登录成功!");

            } else {
              ElMessage.error("登录失败:"+res.msg);
            }

          }).catch((error) => {
            console.log(error)
          })
    };

    const login2 = () => {
      console.log('begin login2');
    }

    const info = () => {
      apiInfo().then(res => {
        //成功
        if (res.code == 0) {
          //保存jwt token到本地
          //localStorage.setItem('token', res.data.token);
          //提示
          //ElMessage.success("登录成功!");
           console.log(res.data);
        } else {
          ElMessage.error("用户信息获取失败:"+res.msg);
        }
      }).catch((error) => {
        console.log(error)
      })
    }
    return {
      account,
      //loginRules,
      accountRef,
      login,
      login2,
      info,
      //isLoading,
    };
  },
}
</script>

<style scoped>
</style>

三,后端php代码:

1,创建middleware
liuhongdi@lhdpc:/data/php/admapi$ php think make:middleware CheckJwt
Middleware:app\middleware\CheckJwt created successfully. 
2,CheckJwt的代码: 
<?php
declare (strict_types = 1);

namespace app\middleware;

use app\lib\util\JwtUtil;

class CheckJwt
{
    /**
     * 处理请求,得到用户信息
     *
     * @param \think\Request $request
     * @param \Closure       $next
     * @return Response
     */
    public function handle($request, \Closure $next)
    {
        $auth = $request->header('authorization');
        if ($auth == null) {
            return $next($request);
        }
        $token = str_replace("Bearer ","",$auth);
        $jUtil = new JwtUtil();
        $res = $jUtil->verifyjwt($token);
        if (isset($res['code']) && isset($res['userId']) && $res['code'] == 0 && is_int($res['userId'])) {
            $userId = $res['userId'];
            $request->auth = $userId;
        } else {
            $request->auth = 0;
        }
        return $next($request);
    }
}

3,app/middleware.php

使从jwt得到用户信息的middleware生效:

<?php
// 全局中间件定义文件
return [
    app\middleware\CheckJwt::class,
];

4,controller/Auth.php

<?php
declare (strict_types = 1);

namespace app\controller;

use app\BaseController;
use think\facade\Cache;
use think\Request;
use app\result\Result;
use think\response\Json;
use app\validate\Login as LoginValidate;
use app\validate\GoodsList as GoodsListValidate;
use think\exception\ValidateException;
use app\lib\util\JwtUtil;

class Auth extends BaseController
{
    /**
     * 登录
     *
     * @return \think\Response
     */
    public function login():Json {
        try {
            validate(LoginValidate::class)
                //->scene('edit')
                ->check($_POST);
        } catch (ValidateException $e) {
                // 验证失败 输出错误信息
                return Result::Error(422,$e->getError());
        }

        if ($_POST["username"] == "dddddd" && $_POST["password"] == "111111"){
            //验证成功,生成jwt返回
            $userId = 123;
            $jUtil = new JwtUtil();
            $token = $jUtil->createJwt($userId);
            $res = ["token"=>$token];
            // 防止重复提交
            Cache::delete($key);
            return Result::Success($res);
        } else {
            return Result::Error(422,"用户名密码错误");
        }
    }
    /**
     * 得到用户信息
     *
     * @return \think\Response
     */
    public function info()
    {
        if ($this->request->auth > 0) {
            $status = "已登录";
        } else {
            $status = "未登录";
        }

        $info = [
            'userId'=>$this->request->auth,
            'status'=>$status,
        ];
        return Result::Success($info);
    }
}

5,lib/util/JwtUtil.php

<?php
namespace app\lib\util;

use Firebase\JWT\ExpiredException;
use Firebase\JWT\JWT;

class JwtUtil {

    private $signKey = "lhd@2001:liuhongdi";
    private $timeMinutes = 5;
    /**
     * 根据json web token设置的规则生成token
     * @return \think\response\Json
     */
    public function createJwt($userId):string
    {
        $key = md5($this->signKey); //jwt的签发**,验证token的时候需要用到
        $time = time(); //签发时间
        $expire = $time + $this->timeMinutes*60; //过期时间
        $token = array(
            "userId" => $userId,
            "iss" => "http://www.liuhongdi.com/",//签发组织
            "aud" => "lhd", //签发作者
            "iat" => $time,    //签发时间
            "nbf" => $time,    //生效时间
            "exp" => $expire    //过期时间
        );
        $jwt = JWT::encode($token,$key);
        return $jwt;
    }

    /**
     * 验证token
     * @return \think\response\Json
     */
    public function verifyjwt($token)
    {
        $key = md5($this->signKey); //jwt的签发**,验证token的时候需要用到
        try{
            $jwtAuth = json_encode(JWT::decode($token,$key,array("HS256")));
            $authInfo = json_decode($jwtAuth,true);
            if (!$authInfo['userId']){
                return ['code'=>0,'msg'=>"用户不存在"];
            }
            return ['code'=>0,'userId'=>$authInfo['userId'],'msg'=>"ok"];
        }catch (ExpiredException $e){
            return ['code'=>0,'msg'=>"token过期"];
        }catch (\Exception $e){
            return ['code'=>0,'msg'=>$e->getMessage()];
        }
    }
}

四,效果测试

1,界面:用户名 dddddd,密码;111111,可见php的代码:

2,未登录时点info

3,登录后的返回:

4,登录后查看info的返回:

五,查看php和thinkphp的版本:

php:
liuhongdi@lhdpc:/data/php/admapi$ php --version
PHP 8.1.1 (cli) (built: Dec 20 2021 16:12:16) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.1, Copyright (c) Zend Technologies
    with Zend OPcache v8.1.1, Copyright (c), by Zend Technologies 
thinkphp:
liuhongdi@lhdpc:/var/www/html$ cd /data/php/admapi/
liuhongdi@lhdpc:/data/php/admapi$ php think version
v6.0.10LTS 
原文地址:https://www.cnblogs.com/architectforest/p/15776999.html