1. 程式人生 > 其它 >thinkphp6:前後端分離使用表單令牌(php 8.1.1 / thinkphp v6.0.10LTS/vue 3.2.26)

thinkphp6:前後端分離使用表單令牌(php 8.1.1 / thinkphp v6.0.10LTS/vue 3.2.26)

一,vue前端程式碼

1,Login.vue
<template>
  <div style="padding:20px;display: flex;align-items:center;justify-content: center;">
    <form :model="account"  style="margin-top:50px;width:400px;">
        <input v-model="account.username" placeholder="請輸入使用者名稱" style="width:392px;font-size:16px;"
/><br/> <input v-model="account.password" type="password" placeholder="請輸入密碼" style="width:392px;margin-top:10px;font-size:16px;" /><br/> <!--v-loading.fullscreen.lock="isLoading"--> <div @click="login" style="margin-top:10px;width: 100%;height:40px;line-height:40px;background: #ff0000;border-radius: 10px;"
> 登入 </div> <div @click="info" style="margin-top:10px;width: 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 formToken = ref(""); const getToken = () => { apiToken().then(res => { //成功 if (res.code == 0) { formToken.value = res.data.token; } else { ElMessage.error("獲取表單令牌失敗:"+res.msg); } }).catch((error) => { console.log(error) }) } getToken(); //登入 const login = async () => { console.log('begin login'); var data = new FormData(); data.append("username",account.username); data.append("password",account.password); data.append("X-CSRF-TOKEN",formToken.value); 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) { console.log(res.data); } else { ElMessage.error("使用者資訊獲取失敗:"+res.msg); } }).catch((error) => { console.log(error) }) } return { account, accountRef, login, login2, info, }; }, } </script> <style scoped> </style>
2,api/axios.js
...
//post
export function postForm (url, params) {
  return new Promise((resolve, reject) => {
      let headers = {
          'Content-Type': 'application/x-www-form-urlencoded',
      }
      //判斷params中是否包含X-CSRF-TOKEN
      var token = "";
      if (params.has("X-CSRF-TOKEN")) {
          token = params.get("X-CSRF-TOKEN");
          params.delete("X-CSRF-TOKEN");
          headers = {
              'Content-Type': 'application/x-www-form-urlencoded',
              'X-CSRF-TOKEN': token,
          }
      }

    _axios.post(url, params,{headers})
        .then(res => {
          resolve(res.data)
        })
        .catch(err => {
            console.log("api error:");
            console.log(err);
            //alert(err);
          reject(err.data)
        })
  })
}
...

3,api/api.js

//get,
import { get, postForm} from './axios'

//user
export const apiLogin = pLogin => postForm('/api/auth/login', pLogin)
...

說明:劉巨集締的架構森林是一個專注架構的部落格,地址:https://www.cnblogs.com/architectforest

對應的原始碼可以訪問這裡獲取:https://github.com/liuhongdi/
或:https://gitee.com/liuhongdi

說明:作者:劉巨集締 郵箱: [email protected]

二,服務端程式碼:

1,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
{
       /*
     *得到form token
     *
     *@return \think\Response
     * */
    public function token():Json {
        $type="sha1";
        $type  = is_callable($type) ? $type : 'md5';
        $token = call_user_func($type, $this->request->server('REQUEST_TIME_FLOAT'));
        $key = uniqid();
        Cache::set($key,$token,300);
        $res = [
            "token"=>$key.$token,
            ];
        return Result::Success($res);
    }

    /**
     * 登入
     *
     * @return \think\Response
     */
    public function login():Json {
        // Header驗證
        if ($this->request->header('X-CSRF-TOKEN')){
            $reqToken = $this->request->header('X-CSRF-TOKEN');
            $key = substr($reqToken,0,13);
            $value = Cache::get($key);
            $origToken = $key.$value;
            if ($origToken === $reqToken) {
                //驗證通過
            } else {
                //驗證不通過
                return Result::Error(422,'表單token錯誤');
            }
        } else {
            return Result::Error(423,'找不到表單token');
        }
        try {
            validate(LoginValidate::class)
                ->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,"使用者名稱密碼錯誤");
        }
    }
}
2,Result.php
<?php

namespace app\result;
use think\response\Json;
class Result {
    //success
    static public function Success($data):Json {
        $rs = [
            'code'=>0,
            'msg'=>"success",
            'data'=>$data,
        ];
        return json($rs);
    }
    //error
    static public function Error($code,$msg):Json {
        $rs = [
            'code'=>$code,
            'msg'=>$msg,
            'data'=>"",
        ];
        return json($rs);
    }
}

三,測試效果:

1,介面 2, 提交與返回:

四,檢視vue的版本

liuhongdi@lhdpc:/data/vue/demo1$ npm list vue
demo1@0.1.0 /data/vue/demo1
├─┬ @vue/cli-plugin-babel@4.5.15
│ └─┬ @vue/babel-preset-app@4.5.15
│   └── vue@3.2.26 deduped
├─┬ element-plus@1.2.0-beta.6
│ ├─┬ @element-plus/icons-vue@0.2.4
│ │ └── vue@3.2.26 deduped
│ ├─┬ @vueuse/core@7.4.1
│ │ ├─┬ @vueuse/shared@7.4.1
│ │ │ └── vue@3.2.26 deduped
│ │ ├─┬ vue-demi@0.12.1
│ │ │ └── vue@3.2.26 deduped
│ │ └── vue@3.2.26 deduped
│ └── vue@3.2.26 deduped
└─┬ vue@3.2.26
  └─┬ @vue/server-renderer@3.2.26
    └── vue@3.2.26 deduped

五,檢視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