一,安装第三方库
1,官方代码站:
https://github.com/lcobucci/jwt
文档地址:
https://lcobucci-jwt.readthedocs.io/en/latest/
2,用composer安装
liuhongdi@lhdpc:/data/laravel/dignews$ composer require lcobucci/jwt
3,安装后查看库的版本:
liuhongdi@lhdpc:/data/laravel/dignews$ composer show lcobucci/jwt name : lcobucci/jwt descrip. : A simple library to work with JSON Web Token and JSON Web Signature keywords : JWS, jwt versions : * 4.0.4 type : library ...
二,php代码
1,封装类:App\extend\jwt\JwtUtil.php
<?php
namespace App\extend\jwt;
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;
/**
* 单例模式
* Class JwtUtil
* @package App\extend\jwt
*/
class JwtUtil
{
private $config;
private $key = "Ge1KCTRhdVsmUUZY0GrwgEvLubPvLOCM";
private $iss = "liuhongdi.com";//颁发者(iss声明)
private $aud = "liuhongdiauth.com";//访问群体(aud声明)
private $jti = "5t6y9400453"; //id(jti声明)
private $expTime = 1;//令牌有效时间,单位小时
private static $instance;// 单例模式JwtAuth句柄
// 获取JwtAuth的句柄
public static function getInstance()
{
if (is_null(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
/**
* 构造
*/
public function __construct()
{
self::init();
}
/**
* 初始化
*/
private function init()
{
$config = Configuration::forSymmetricSigner(
new Sha256(),
InMemory::base64Encoded($this->key)
);
$this->config = $config;
}
/**
* 创建JWT
* @param array $arrClaim
* @return string
* @throws \Exception
* @author 老刘
*/
public function createToken(array $arrClaim)
{
$config = $this->config;
assert($config instanceof Configuration);
if (is_array($arrClaim) && count(array_filter(array_keys($arrClaim),'is_string'))>0) {
//是关联数组
} else {
//不是关联数组
throw new \Exception("claim参数必须为关联数组");
}
$now = new \DateTimeImmutable();
$token = $config->builder()
// 配置颁发者(iss声明)
->issuedBy($this->iss)
// 配置访问群体(aud声明)
->permittedFor($this->aud)
// 配置id(jti声明)
->identifiedBy($this->jti)
// 配置令牌发出的时间(iat声明)
->issuedAt($now)
// 配置令牌的过期时间(exp claim)
->expiresAt($now->modify("+{$this->expTime} hour"));
//claim
foreach ($arrClaim as $k => $item) {
$token = $token->withClaim($k, $item);
}
// 生成新令牌
$token = $token->getToken($config->signer(), $config->signingKey());
return $token->toString();
}
/**
* 解析token
* @param string $jwt
* @return mixed
* @author 老刘
*/
public function parseToken(string $jwt)
{
$config = $this->config;
$token = $config->parser()->parse($jwt);
return $token->claims();
}
/**
* 验证令牌
* @param $jwt
* @return mixed
* @throws \Exception
* @author 老刘
*/
public function validatorToken($jwt)
{
$config = $this->config;
$token = $config->parser()->parse($jwt);
$claims = $token->claims();
$jti = (string)$claims->get('jti');
$iss = (string)$claims->get('iss');
$aud = $claims->get('aud');
$exp = $claims->get('exp');
$now = new \DateTimeImmutable();
// 是否过期
if ($exp < $now) {
throw new \Exception("身份已过期");
}
//验证jwt id是否匹配
$validate_jwt_id = new \Lcobucci\JWT\Validation\Constraint\IdentifiedBy($jti);
// 验证签发人url是否正确
$validate_issued = new \Lcobucci\JWT\Validation\Constraint\IssuedBy($iss);
// 验证客户端url是否匹配
$validate_aud = new \Lcobucci\JWT\Validation\Constraint\PermittedFor($aud[0]);
$config->setValidationConstraints($validate_jwt_id, $validate_issued, $validate_aud);
$constraints = $config->validationConstraints();
//验证
if (!$config->validator()->validate($token, ...$constraints)) {
throw new \Exception("非法的请求");
}
return $claims;
}
}
2,创建controller:
liuhongdi@lhdpc:/data/laravel/dignews$ php artisan make:controller LoginController
INFO Controller [app/Http/Controllers/LoginController.php] created successfully.
代码:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\extend\result\Result;
use App\Models\Staff as StaffModel;
use App\business\StaffLogin;
use Illuminate\Support\Facades\Redis;
use App\extend\jwt\JwtUtil;
class LoginController extends Controller
{
/**
* 得到用户信息
*/
public function info(Request $request)
{
//得到数据库的用户id
$staffId = $request->auth;
$staff = new StaffModel();
$rows = $staff->getOneByStaffId($staffId);
if (is_null($rows)) {
return Result::ErrorCode(100,'用户不存在');
}
unset($rows['password']);
//添加头像
$host = "http://192.168.219.6/imgstaffhead";
$rand = rand(1000,9999);
$rows['head'] = $host."/".$staffId.".jpg?rand=".$rand;
//返回
return Result::Success($rows);
}
//得到jwt信息,返回给线下
function getJwtInfo($userId,$nickName) {
//验证成功,生成jwt返回
//$jUtil = new JwtUtil();
//$token = $jUtil->createJwt($userId);
$token = JwtUtil::getInstance()->createToken(['uid' => $userId]);
$res = ["tokenString"=>$token];
$host = "http://192.168.219.6/imgstaffhead";
$rand = rand(1000,9999);
$res['head'] = $host."/".$userId.".jpg?rand=".$rand;
$res['nickname'] = $nickName;
return $res;
}
/**
* 登录
*
*/
public function login(Request $request) {
$username = $request->post('username');
$password = $request->post('password');
$uniqid = $request->post('uniqid');
$captcode = $request->post('captcode');
//检查验证码:
$value = Redis::get($uniqid);
if (strtolower($captcode) !== strtolower($value)) {
return Result::ErrorCode(400,"输入的验证码不正确");
}
//用username读取用户信息
$staff = new StaffModel();
$rows = $staff->getOneByUsername($username);
if ($rows == null) {
return Result::ErrorCode(422,'用户名出现错误');
} else {
if ($rows['is_active'] == 0) {
return Result::ErrorCode(422,'用户未激活');
} else {
//检查密码是否正确
if (password_verify($password,$rows['password'])) {
$res = $this->getJwtInfo($rows['staff_id'],$rows['nickname']);
//登录成功,写日志
$loginLog = new StaffLogin();
$loginLog->log($rows['staff_id']);
return Result::Success($res);
} else {
//报密码错误
return Result::ErrorCode(422,"用户名密码错误");
}
}
}
}
}
3,创建middleware
liuhongdi@lhdpc:/data/laravel/dignews$ php artisan make:middleware JwtAuth
INFO Middleware [app/Http/Middleware/JwtAuth.php] created successfully.
代码:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use App\extend\jwt\JwtUtil;
class JwtAuth
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
//得到jwt的token
$auth = $request->header('authorization');
if ($auth == null) {
return $next($request);
}
$token = str_replace("Bearer ","",$auth);
if (count(explode('.', $token)) <> 3) {
throw new \Exception('非法身份信息,请重新登录', -1);
}
//检验token
$objJwtAuth = JwtUtil::getInstance();
$claims = $objJwtAuth->validatorToken($token);
//得到用户id
$uid = $claims->get('uid');
//添加到request
$request->merge(['auth'=>$uid]);
return $next($request);
}
}
三,vue代码:
api/axios.js
import axios from 'axios'
import { ElMessage } from "element-plus";
import router from "../route/router.js"
import { getUser } from '@/utils/user'
import { showLoading, hideLoading } from '@/utils/loading'
let config = {
timeout:10000,
};
const _axios = axios.create(config);
//request interceptor
_axios.interceptors.request.use(
function(config) {
showLoading();
//在发送请求前,增加token字串到header
let userInfo = getUser().data;
if ('tokenString' in userInfo && userInfo["tokenString"] != ""){
config.headers.Authorization ="Bearer "+userInfo["tokenString"];
}
return config;
},
function(error) {
// Do something with request error
return Promise.reject(error);
}
);
//response interceptor
_axios.interceptors.response.use(
function(response) {
hideLoading();
let code = response.data.code;
if (code == '401') {
//现在跳转去登录
let redi = {path: router.currentRoute.value.fullPath};
let redistr = JSON.stringify(redi);
if (router.currentRoute.value.meta.redirectstr) {
redistr = router.currentRoute.value.meta.redirectstr;
}
//打印出参数
if (router.currentRoute.value.meta.actstr) {
let actstr = router.currentRoute.value.meta.actstr;
router.replace({ path: '/login/login',query: { redirect: redistr,act:actstr } });
} else {
router.replace({ path: '/login/login',query: { redirect: redistr } });
}
}
return response;
},
function(error) {
hideLoading();
// Do something with response error
if (error.response.status) {
//alert(error.response.status);
switch (error.response.status) {
// 401: 未登录
// 未登录则跳转登录页面,并携带当前页面的路径
// 在登录成功后返回当前页面,这一步需要在登录页操作。
case 401:
router.replace({ path: '/login/login' });
break;
// 403 token过期
// 登录过期对用户进行提示
// 清除本地token和清空vuex中token对象
// 跳转登录页面
case 403:
ElMessage.error("登录过期,请重新登录");
// 清除token
localStorage.removeItem('token')
// 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面
setTimeout(() => {
router.push({ path: '/login/login' });
}, 1000)
break;
// 404请求不存在
case 404:
ElMessage.error("网络请求不存在,404错误");
break;
// 500服务端错误
case 500:
ElMessage.error("服务端有故障,500错误");
break;
// 其他错误,直接抛出错误提示
default:
var tip = "";
if (typeof(error.response.data.message) == "undefined") {
tip = error.toString();
} else {
tip = error.response.data.message;
}
ElMessage.error(tip);
break;
}
return Promise.reject(error.response)
}
}
);
/**
* get方法,对应get请求
* @param {String} url [请求的url地址]
* @param {Object} params [请求时携带的参数]
*/
export function get (url, params) {
return new Promise((resolve, reject) => {
_axios.get(url, {
params: params,
}).then(res => {
resolve(res.data)
}).catch(err => {
reject(err.data)
})
})
}
//postForm
export function post (url, params) {
return new Promise((resolve, reject) => {
_axios.post(url, params,{headers: {
'Content-Type': 'application/x-www-form-urlencoded',
}})
.then(res => {
resolve(res.data)
})
.catch(err => {
reject(err.data)
})
})
}
说明:刘宏缔的架构森林—专注it技术的博客,
网址:https://imgtouch.com
本文: https://blog.imgtouch.com/index.php/2023/10/31/laravel-yong-hu-ren-zheng-zhi-lcobucci-jwt-10-27/
代码: https://github.com/liuhongdi/ 或 https://gitee.com/liuhongdi
说明:作者:刘宏缔 邮箱: 371125307@qq.com
四,测试效果:
五,查看laravel框架的版本:
liuhongdi@lhdpc:/data/laravel/dignews$ php artisan --version Laravel Framework 10.27.0