1
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
<?php
|
||||
|
||||
namespace support;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
|
||||
/**
|
||||
* 日期时间处理类
|
||||
*/
|
||||
class Date
|
||||
{
|
||||
const YEAR = 31536000;
|
||||
const MONTH = 2592000;
|
||||
const WEEK = 604800;
|
||||
const DAY = 86400;
|
||||
const HOUR = 3600;
|
||||
const MINUTE = 60;
|
||||
|
||||
/**
|
||||
* 计算两个时区间相差的时长,单位为秒
|
||||
*
|
||||
* $seconds = self::offset('America/Chicago', 'GMT');
|
||||
*
|
||||
* [!!] A list of time zones that PHP supports can be found at
|
||||
* <http://php.net/timezones>.
|
||||
*
|
||||
* @param string $remote timezone that to find the offset of
|
||||
* @param string $local timezone used as the baseline
|
||||
* @param mixed $now UNIX timestamp or date string
|
||||
* @return integer
|
||||
*/
|
||||
public static function offset($remote, $local = null, $now = null)
|
||||
{
|
||||
if ($local === null) {
|
||||
// Use the default timezone
|
||||
$local = date_default_timezone_get();
|
||||
}
|
||||
if (is_int($now)) {
|
||||
// Convert the timestamp into a string
|
||||
$now = date(DateTime::RFC2822, $now);
|
||||
}
|
||||
// Create timezone objects
|
||||
$zone_remote = new DateTimeZone($remote);
|
||||
$zone_local = new DateTimeZone($local);
|
||||
// Create date objects from timezones
|
||||
$time_remote = new DateTime($now, $zone_remote);
|
||||
$time_local = new DateTime($now, $zone_local);
|
||||
// Find the offset
|
||||
$offset = $zone_remote->getOffset($time_remote) - $zone_local->getOffset($time_local);
|
||||
return $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个时间戳之间相差的时间
|
||||
*
|
||||
* $span = self::span(60, 182, 'minutes,seconds'); // array('minutes' => 2, 'seconds' => 2)
|
||||
* $span = self::span(60, 182, 'minutes'); // 2
|
||||
*
|
||||
* @param int $remote timestamp to find the span of
|
||||
* @param int $local timestamp to use as the baseline
|
||||
* @param string $output formatting string
|
||||
* @return string when only a single output is requested
|
||||
* @return array associative list of all outputs requested
|
||||
* @from https://github.com/kohana/ohanzee-helpers/blob/master/src/Date.php
|
||||
*/
|
||||
public static function span($remote, $local = null, $output = 'years,months,weeks,days,hours,minutes,seconds')
|
||||
{
|
||||
// Normalize output
|
||||
$output = trim(strtolower((string)$output));
|
||||
if (!$output) {
|
||||
// Invalid output
|
||||
return false;
|
||||
}
|
||||
// Array with the output formats
|
||||
$output = preg_split('/[^a-z]+/', $output);
|
||||
// Convert the list of outputs to an associative array
|
||||
$output = array_combine($output, array_fill(0, count($output), 0));
|
||||
// Make the output values into keys
|
||||
extract(array_flip($output), EXTR_SKIP);
|
||||
if ($local === null) {
|
||||
// Calculate the span from the current time
|
||||
$local = time();
|
||||
}
|
||||
// Calculate timespan (seconds)
|
||||
$timespan = abs($remote - $local);
|
||||
if (isset($output['years'])) {
|
||||
$timespan -= self::YEAR * ($output['years'] = (int)floor($timespan / self::YEAR));
|
||||
}
|
||||
if (isset($output['months'])) {
|
||||
$timespan -= self::MONTH * ($output['months'] = (int)floor($timespan / self::MONTH));
|
||||
}
|
||||
if (isset($output['weeks'])) {
|
||||
$timespan -= self::WEEK * ($output['weeks'] = (int)floor($timespan / self::WEEK));
|
||||
}
|
||||
if (isset($output['days'])) {
|
||||
$timespan -= self::DAY * ($output['days'] = (int)floor($timespan / self::DAY));
|
||||
}
|
||||
if (isset($output['hours'])) {
|
||||
$timespan -= self::HOUR * ($output['hours'] = (int)floor($timespan / self::HOUR));
|
||||
}
|
||||
if (isset($output['minutes'])) {
|
||||
$timespan -= self::MINUTE * ($output['minutes'] = (int)floor($timespan / self::MINUTE));
|
||||
}
|
||||
// Seconds ago, 1
|
||||
if (isset($output['seconds'])) {
|
||||
$output['seconds'] = $timespan;
|
||||
}
|
||||
if (count($output) === 1) {
|
||||
// Only a single output was requested, return it
|
||||
return array_pop($output);
|
||||
}
|
||||
// Return array
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化 UNIX 时间戳为人易读的字符串
|
||||
*
|
||||
* @param int Unix 时间戳
|
||||
* @param mixed $local 本地时间
|
||||
*
|
||||
* @return string 格式化的日期字符串
|
||||
*/
|
||||
public static function human($remote, $local = null)
|
||||
{
|
||||
$time_diff = (is_null($local) || $local ? time() : $local) - $remote;
|
||||
$tense = $time_diff < 0 ? 'after' : 'ago';
|
||||
$time_diff = abs($time_diff);
|
||||
$chunks = [
|
||||
[60 * 60 * 24 * 365, 'year'],
|
||||
[60 * 60 * 24 * 30, 'month'],
|
||||
[60 * 60 * 24 * 7, 'week'],
|
||||
[60 * 60 * 24, 'day'],
|
||||
[60 * 60, 'hour'],
|
||||
[60, 'minute'],
|
||||
[1, 'second']
|
||||
];
|
||||
$name = 'second';
|
||||
$count = 0;
|
||||
|
||||
for ($i = 0, $j = count($chunks); $i < $j; $i++) {
|
||||
$seconds = $chunks[$i][0];
|
||||
$name = $chunks[$i][1];
|
||||
if (($count = floor($time_diff / $seconds)) != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return __("%d $name%s $tense", $count, ($count > 1 ? 's' : ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个基于时间偏移的Unix时间戳
|
||||
*
|
||||
* @param string $type 时间类型,默认为day,可选minute,hour,day,week,month,quarter,year
|
||||
* @param int $offset 时间偏移量 默认为0,正数表示当前type之后,负数表示当前type之前
|
||||
* @param string $position 时间的开始或结束,默认为begin,可选前(begin,start,first,front),end
|
||||
* @param int $year 基准年,默认为null,即以当前年为基准
|
||||
* @param int $month 基准月,默认为null,即以当前月为基准
|
||||
* @param int $day 基准天,默认为null,即以当前天为基准
|
||||
* @param int $hour 基准小时,默认为null,即以当前年小时基准
|
||||
* @param int $minute 基准分钟,默认为null,即以当前分钟为基准
|
||||
* @return int 处理后的Unix时间戳
|
||||
*/
|
||||
public static function unixtime($type = 'day', $offset = 0, $position = 'begin', $year = null, $month = null, $day = null, $hour = null, $minute = null)
|
||||
{
|
||||
$year = is_null($year) ? date('Y') : $year;
|
||||
$month = is_null($month) ? date('m') : $month;
|
||||
$day = is_null($day) ? date('d') : $day;
|
||||
$hour = is_null($hour) ? date('H') : $hour;
|
||||
$minute = is_null($minute) ? date('i') : $minute;
|
||||
$position = in_array($position, array('begin', 'start', 'first', 'front'));
|
||||
|
||||
$baseTime = mktime(0, 0, 0, $month, $day, $year);
|
||||
|
||||
switch ($type) {
|
||||
case 'minute':
|
||||
$time = $position ? mktime($hour, $minute + $offset, 0, $month, $day, $year) : mktime($hour, $minute + $offset, 59, $month, $day, $year);
|
||||
break;
|
||||
case 'hour':
|
||||
$time = $position ? mktime($hour + $offset, 0, 0, $month, $day, $year) : mktime($hour + $offset, 59, 59, $month, $day, $year);
|
||||
break;
|
||||
case 'day':
|
||||
$time = $position ? mktime(0, 0, 0, $month, $day + $offset, $year) : mktime(23, 59, 59, $month, $day + $offset, $year);
|
||||
break;
|
||||
case 'week':
|
||||
$weekIndex = date("w", $baseTime);
|
||||
$time = $position ?
|
||||
strtotime($offset . " weeks", strtotime(date('Y-m-d', strtotime("-" . ($weekIndex ? $weekIndex - 1 : 6) . " days", $baseTime)))) :
|
||||
strtotime($offset . " weeks", strtotime(date('Y-m-d 23:59:59', strtotime("+" . (6 - ($weekIndex ? $weekIndex - 1 : 6)) . " days", $baseTime))));
|
||||
break;
|
||||
case 'month':
|
||||
$_timestamp = mktime(0, 0, 0, $month + $offset, 1, $year);
|
||||
$time = $position ? $_timestamp : mktime(23, 59, 59, $month + $offset, self::days_in_month(date("m", $_timestamp), date("Y", $_timestamp)), $year);
|
||||
break;
|
||||
case 'quarter':
|
||||
$quarter = ceil(date('n', $baseTime) / 3) + $offset;
|
||||
$month = $quarter * 3;
|
||||
$offset_year = ceil($month/12) - 1;
|
||||
$year = $year + $offset_year;
|
||||
$month = $month - ($offset_year * 12);
|
||||
$time = $position ?
|
||||
mktime(0, 0, 0, $month-2, 1, $year) :
|
||||
mktime(23, 59, 59, $month, self::days_in_month($month, $year), $year);
|
||||
break;
|
||||
case 'year':
|
||||
$time = $position ? mktime(0, 0, 0, 1, 1, $year + $offset) : mktime(23, 59, 59, 12, 31, $year + $offset);
|
||||
break;
|
||||
default:
|
||||
$time = mktime($hour, $minute, 0, $month, $day, $year);
|
||||
break;
|
||||
}
|
||||
return $time;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定年月拥有的天数
|
||||
* @param int $month
|
||||
* @param int $year
|
||||
* @return false|int|string
|
||||
*/
|
||||
public static function days_in_month($month, $year)
|
||||
{
|
||||
if (function_exists("cal_days_in_month")) {
|
||||
return cal_days_in_month(CAL_GREGORIAN, $month, $year);
|
||||
} else {
|
||||
return date('t', mktime(0, 0, 0, $month, 1, $year));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace support;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
|
||||
/**
|
||||
* 日期时间处理类
|
||||
*/
|
||||
class Env
|
||||
{
|
||||
static function get($name='',$default=null){
|
||||
$envfn = run_path().'/.env';
|
||||
$env = parse_ini_file($envfn,true);
|
||||
$ret = $env;
|
||||
if(!$name){
|
||||
return $env;
|
||||
}
|
||||
$names = explode('.',$name);
|
||||
if(!isset($ret[$names[0]])){
|
||||
return $default;
|
||||
}
|
||||
foreach($names as $_name){
|
||||
if(isset($ret[$_name])){
|
||||
$ret = $ret[$_name];
|
||||
}else{
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* @desc ExceptionHandler
|
||||
* @author Tinywan(ShaoBo Wan)
|
||||
* @email 756684177@qq.com
|
||||
* @date 2022/3/6 14:08
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace support;
|
||||
|
||||
use FastRoute\BadRouteException;
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\DbException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use Throwable;
|
||||
use Tinywan\ExceptionHandler\Event\DingTalkRobotEvent;
|
||||
use Tinywan\ExceptionHandler\Exception\BaseException;
|
||||
use Tinywan\ExceptionHandler\Exception\ServerErrorHttpException;
|
||||
use Tinywan\Jwt\Exception\JwtRefreshTokenExpiredException;
|
||||
use Tinywan\Jwt\Exception\JwtTokenException;
|
||||
use Tinywan\Jwt\Exception\JwtTokenExpiredException;
|
||||
use Tinywan\Validate\Exception\ValidateException;
|
||||
use Webman\Exception\ExceptionHandler;
|
||||
use Webman\Http\Request;
|
||||
use Webman\Http\Response;
|
||||
|
||||
class Exception extends ExceptionHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Throwable $exception
|
||||
* @return Response
|
||||
*/
|
||||
public function render(Request $request, Throwable $exception): Response
|
||||
{
|
||||
|
||||
$code = $exception->getCode();
|
||||
$debug = $this->_debug ?? $this->debug;
|
||||
if ($request->expectsJson()) {
|
||||
$json = ['code' => $code ?: 500, 'msg' => $debug ? $exception->getMessage() : 'Server internal error'];
|
||||
$debug && $json['traces'] = (string)$exception;
|
||||
return new Response($code, ['Content-Type' => 'application/json'],
|
||||
\json_encode($json, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||
}
|
||||
$error = $debug ? \nl2br((string)$exception) : 'Server internal error';
|
||||
return new Response($code, [], $error);
|
||||
// $header = array_merge(['Content-Type' => 'application/json;charset=utf-8'], $this->header);
|
||||
|
||||
// return new Response($this->statusCode, $header, json_encode($responseBody));
|
||||
}
|
||||
|
||||
}
|
||||
+528
@@ -0,0 +1,528 @@
|
||||
<?php
|
||||
|
||||
namespace support;
|
||||
|
||||
use app\model\User;
|
||||
use app\model\UserRule;
|
||||
use support\think\Db;
|
||||
|
||||
class Jwt
|
||||
{
|
||||
protected static $instance = null;
|
||||
protected static $_error = '';
|
||||
protected static $_logined = false;
|
||||
protected static $_user = null;
|
||||
protected static $_token = '';
|
||||
//Token默认有效时长
|
||||
protected static $keeptime = 2592000;
|
||||
protected static $requestUri = '';
|
||||
protected static $rules = [];
|
||||
//默认配置
|
||||
protected static $config = [];
|
||||
protected static $options = [];
|
||||
protected static $disallowFields = [
|
||||
'password','trade_password','totp_secret',//安全信息
|
||||
'created_at','updated_at','loginfailure',//系统信息
|
||||
//'last_time','last_ip','join_time','join_ip',最近登录信息
|
||||
//'score',
|
||||
'level',
|
||||
|
||||
];
|
||||
/**
|
||||
* 注册用户
|
||||
*
|
||||
* @param string $username 用户名
|
||||
* @param string $password 密码
|
||||
* @param string $email 邮箱
|
||||
* @param string $mobile 手机号
|
||||
* @param array $extend 扩展参数
|
||||
* @return boolean
|
||||
*/
|
||||
public static function register($username, $password, $email = '', $mobile = '', $extend = []): array|bool
|
||||
{
|
||||
// 检测用户名、昵称、邮箱、手机号是否存在
|
||||
if (User::getByUsername($username)) {
|
||||
self::setError('Username already exist');
|
||||
return false;
|
||||
}
|
||||
if (User::getByNickname($username)) {
|
||||
self::setError('Nickname already exist');
|
||||
return false;
|
||||
}
|
||||
if ($email && User::getByEmail($email)) {
|
||||
self::setError('Email already exist');
|
||||
return false;
|
||||
}
|
||||
if ($mobile && User::getByMobile($mobile)) {
|
||||
self::setError('Mobile already exist');
|
||||
return false;
|
||||
}
|
||||
$request = request();
|
||||
if($request){
|
||||
$ip = getRealIp();
|
||||
}else{
|
||||
$ip = '127.0.0.1';
|
||||
}
|
||||
$time = time();
|
||||
|
||||
$data = [
|
||||
'username' => $username,
|
||||
'password' => $password,
|
||||
'email' => $email,
|
||||
'mobile' => $mobile,
|
||||
'level' => 1,
|
||||
'score' => 0,
|
||||
'avatar' => '',
|
||||
];
|
||||
$params = array_merge($data, [
|
||||
'nickname' => preg_match("/^1[3-9]{1}\d{9}$/", $username) ? substr_replace($username, '****', 3, 4) : $username,
|
||||
//'salt' => Random::alnum(),
|
||||
'join_time' => $time,
|
||||
'join_ip' => $ip,
|
||||
'last_time' => $time,
|
||||
'last_ip' => $ip,
|
||||
'status' => '1'
|
||||
]);
|
||||
$params['password'] = \plugin\admin\app\common\Util::passwordHash($password);
|
||||
$params = array_merge($params, $extend);
|
||||
|
||||
//账号注册时需要开启事务,避免出现垃圾数据
|
||||
Db::startTrans();
|
||||
try {
|
||||
$user = User::create($params,[], true);
|
||||
/**
|
||||
* @var User $user
|
||||
* */
|
||||
$user->role_id;
|
||||
$user = User::where('id',$user->id)->find();
|
||||
$user['client'] = \support\Jwt\JwtToken::TOKEN_CLIENT_WEB;
|
||||
if(Request()->client == 'win'){
|
||||
$user['client'] = \support\Jwt\JwtToken::TOKEN_CLIENT_PC;
|
||||
\support\Jwt\JwtToken::clear(\support\Jwt\JwtToken::TOKEN_CLIENT_PC);
|
||||
}
|
||||
$_token = \support\Jwt\JwtToken::generateToken($user->toArray());
|
||||
$user->token = $_token['access_token'];
|
||||
Db::commit();
|
||||
//注册成功的事件
|
||||
Hook("user.register_successed", $user);
|
||||
return self::getUserinfo($user);
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 用户登录
|
||||
*
|
||||
* @param string $account 账号,用户名、邮箱、手机号
|
||||
* @param string $password 密码
|
||||
* @return boolean
|
||||
*/
|
||||
public static function login($account, $password,$type=''): array|bool
|
||||
{
|
||||
if($type){
|
||||
$field = $type;
|
||||
}else{
|
||||
$field = strpos('@',$account)!==false ? 'email' : 'username';
|
||||
}
|
||||
/**
|
||||
* @var User $user
|
||||
* */
|
||||
if($field=='mobile'){
|
||||
$user = User::where($field , $account)->where('region',Request()->post('region'))->find();
|
||||
}else{
|
||||
$user = User::where($field , $account)->find();
|
||||
}
|
||||
if (!$user) {
|
||||
self::setError('Account is incorrect');
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($user->status != 1) {
|
||||
self::setError('Account is locked');
|
||||
return false;
|
||||
}
|
||||
//if ($user->username!='' && !\plugin\admin\app\common\Util::passwordVerify($password,$user->password)) {
|
||||
if (!\plugin\admin\app\common\Util::passwordVerify($password,$user->password)) {
|
||||
self::setError('Password is incorrect');
|
||||
return false;
|
||||
}
|
||||
|
||||
//直接登录会员
|
||||
return self::direct($user->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function logout()
|
||||
{
|
||||
//退出成功的事件
|
||||
Hook("user.logout_successed");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
* @param string $newpassword 新密码
|
||||
* @param string $oldpassword 旧密码
|
||||
* @param bool $ignoreoldpassword 忽略旧密码
|
||||
* @return boolean
|
||||
*/
|
||||
public static function changepwd($newpassword, $oldpassword = '', $ignoreoldpassword = false)
|
||||
{
|
||||
if (!self::isLogin()) {
|
||||
self::setError('You are not logged in');
|
||||
return false;
|
||||
}
|
||||
$user = self::getUser();
|
||||
//判断旧密码是否正确
|
||||
if (!\plugin\admin\app\common\Util::passwordVerify($oldpassword,$user->password) || $ignoreoldpassword) {
|
||||
Db::startTrans();
|
||||
try {
|
||||
$salt = Random::alnum();
|
||||
User::where('id',$user->id)->save([
|
||||
'loginfailure' => 0,
|
||||
'password' => \plugin\admin\app\common\Util::passwordHash($newpassword)
|
||||
]);
|
||||
//修改密码成功的事件
|
||||
Hook("user.changepwd_successed", $user);
|
||||
Db::commit();
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
self::setError('Password is incorrect');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改交易密码
|
||||
* @param string $newpassword 新密码
|
||||
* @param string $oldpassword 旧密码
|
||||
* @param bool $ignoreoldpassword 忽略旧密码
|
||||
* @return boolean
|
||||
*/
|
||||
public static function change_trade_pwd($newpassword, $oldpassword = '', $ignoreoldpassword = false)
|
||||
{
|
||||
$user = self::getUser();
|
||||
if (!self::isLogin()) {
|
||||
self::setError('You are not logged in');
|
||||
return false;
|
||||
}
|
||||
//判断旧密码是否正确
|
||||
if (!$user->trade_password || $ignoreoldpassword || \plugin\admin\app\common\Util::passwordVerify($oldpassword,$user->trade_password)) {
|
||||
Db::startTrans();
|
||||
try {
|
||||
$salt = Random::alnum();
|
||||
User::where('id',$user->id)->save(['trade_password' => \plugin\admin\app\common\Util::passwordHash($newpassword)]);
|
||||
//修改密码成功的事件
|
||||
Hook("user.change_trade_pwd_successed", $user);
|
||||
Db::commit();
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
self::setError('Old password is incorrect');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static function verify_trade_password($password){
|
||||
$user = self::getUser();
|
||||
if(is_null($user->trade_password)){
|
||||
throw new \Exception(__('Please set the trade password first'),200);
|
||||
}
|
||||
if (!\plugin\admin\app\common\Util::passwordVerify($password,$user->trade_password)) {
|
||||
throw new \Exception(__('Trade password is incorrect'),200);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接登录账号
|
||||
* @param int $user_id
|
||||
* @return boolean
|
||||
*/
|
||||
public static function direct($user_id): array|bool
|
||||
{
|
||||
$user = User::where('id',$user_id)->find();
|
||||
if ($user) {
|
||||
Db::startTrans();
|
||||
try {
|
||||
$ip = getRealIp();
|
||||
$time = time();
|
||||
|
||||
//记录本次登录的IP和时间
|
||||
$user->last_ip = $ip;
|
||||
$user->last_time = $time;
|
||||
//重置登录失败次数
|
||||
$user->loginfailure = 0;
|
||||
|
||||
$user->save();
|
||||
Db::commit();
|
||||
$user['client'] = \support\Jwt\JwtToken::TOKEN_CLIENT_WEB;
|
||||
if(Request()->client == 'win'){
|
||||
$user['client'] = \support\Jwt\JwtToken::TOKEN_CLIENT_PC;
|
||||
//\support\Jwt\JwtToken::clear(\support\Jwt\JwtToken::TOKEN_CLIENT_PC);
|
||||
}
|
||||
$_user = $user->toArray();
|
||||
$_user = [
|
||||
'id' => $_user['id'],
|
||||
'username' => $_user['username'],
|
||||
'client' => $_user['client'],
|
||||
];
|
||||
$_token = \support\Jwt\JwtToken::generateToken($_user);
|
||||
$user->token = $_token['access_token'];
|
||||
//登录成功的事件
|
||||
Hook("user.login_successed", $user);
|
||||
return self::getUserinfo($user);
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否是否有对应权限
|
||||
* @param string $path 控制器/方法
|
||||
* @param string $module 模块 默认为当前模块
|
||||
* @return boolean
|
||||
*/
|
||||
public static function check($path = null, $module = null)
|
||||
{
|
||||
if (!self::isLogin()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$ruleList = self::getRuleList();
|
||||
$rules = [];
|
||||
foreach ($ruleList as $k => $v) {
|
||||
$rules[] = $v['key'];
|
||||
}
|
||||
$url = ($module ? $module : request()->app) . '/' . (is_null($path) ? self::getRequestUri() : $path);
|
||||
$url = strtolower(str_replace('.', '/', $url));
|
||||
return in_array($url, $rules);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否登录
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isLogin()
|
||||
{
|
||||
$user_id = \support\Jwt\JwtToken::getCurrentId();
|
||||
if ($user_id) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取User模型
|
||||
* @return User
|
||||
*/
|
||||
public static function getUser($user=null)
|
||||
{
|
||||
$user = $user ? $user : \support\Jwt\JwtToken::getUser();
|
||||
return $user;
|
||||
}
|
||||
/**
|
||||
* 获取会员基本信息
|
||||
*/
|
||||
public static function getUserinfo($user=null)
|
||||
{
|
||||
$data = self::getUser($user)->toArray();
|
||||
$disallowFields = self::getDisallowFields();
|
||||
$userinfo = array_diff_key($data, array_flip($disallowFields));
|
||||
$userinfo['has_trade_password'] = $data['trade_password'] ? true: false;
|
||||
return $userinfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员组别规则列表
|
||||
* @return array|bool|\PDOStatement|string|\think\Collection
|
||||
*/
|
||||
public static function getRuleList()
|
||||
{
|
||||
$user = self::getUser();
|
||||
$role_id = $user->role_id;
|
||||
if (!$role_id) {
|
||||
return [];
|
||||
}
|
||||
if (cache('group_rules_'.$role_id)) {
|
||||
return cache('group_rules_'.$role_id);
|
||||
}
|
||||
$rules = explode(',', $role_id);
|
||||
$rules = UserRule::where('status', 'normal')->where('id', 'in', $rules)->field('id,pid,key,title,type')->select();
|
||||
cache('group_rules_'.$role_id,$rules);
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前请求的URI
|
||||
* @return string
|
||||
*/
|
||||
public static function getRequestUri()
|
||||
{
|
||||
return self::$requestUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前请求的URI
|
||||
* @param string $uri
|
||||
*/
|
||||
public static function setRequestUri($uri)
|
||||
{
|
||||
self::$requestUri = $uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取允许输出的字段
|
||||
* @return array
|
||||
*/
|
||||
public static function getDisallowFields()
|
||||
{
|
||||
return self::$disallowFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置允许输出的字段
|
||||
* @param array $fields
|
||||
*/
|
||||
public static function setDisallowFields($fields)
|
||||
{
|
||||
self::$disallowFields = $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个指定会员
|
||||
* @param int $user_id 会员ID
|
||||
* @return boolean
|
||||
*/
|
||||
public static function delete($user_id)
|
||||
{
|
||||
$user = User::where('id',$user_id)->find();
|
||||
if (!$user) {
|
||||
return false;
|
||||
}
|
||||
Db::startTrans();
|
||||
try {
|
||||
// 删除会员
|
||||
User::destroy($user_id);
|
||||
|
||||
Hook("user.delete_successed", $user);
|
||||
Db::commit();
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
self::setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取密码加密后的字符串
|
||||
* @param string $password 密码
|
||||
* @param string $salt 密码盐
|
||||
* @return string
|
||||
*/
|
||||
public function getEncryptPassword($password, $salt = '')
|
||||
{
|
||||
return md5(md5($password) . $salt);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测当前控制器和方法是否匹配传递的数组
|
||||
*
|
||||
* @param array $arr 需要验证权限的数组
|
||||
* @return boolean
|
||||
*/
|
||||
public static function match($arr = [])
|
||||
{
|
||||
$request = Request();
|
||||
$arr = is_array($arr) ? $arr : explode(',', $arr);
|
||||
if (!$arr) {
|
||||
return false;
|
||||
}
|
||||
$arr = array_map('strtolower', $arr);
|
||||
// 是否存在
|
||||
if (in_array(strtolower($request->action), $arr) || in_array('*', $arr)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 没找到匹配
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染用户数据
|
||||
* @param array $datalist 二维数组
|
||||
* @param mixed $fields 加载的字段列表
|
||||
* @param string $fieldkey 渲染的字段
|
||||
* @param string $renderkey 结果字段
|
||||
* @return array
|
||||
*/
|
||||
public function render(&$datalist, $fields = [], $fieldkey = 'user_id', $renderkey = 'userinfo')
|
||||
{
|
||||
$fields = !$fields ? ['id', 'nickname', 'level', 'avatar'] : (is_array($fields) ? $fields : explode(',', $fields));
|
||||
$ids = [];
|
||||
foreach ($datalist as $k => $v) {
|
||||
if (!isset($v[$fieldkey])) {
|
||||
continue;
|
||||
}
|
||||
$ids[] = $v[$fieldkey];
|
||||
}
|
||||
$list = [];
|
||||
if ($ids) {
|
||||
if (!in_array('id', $fields)) {
|
||||
$fields[] = 'id';
|
||||
}
|
||||
$ids = array_unique($ids);
|
||||
$selectlist = User::where('id', 'in', $ids)->column($fields);
|
||||
foreach ($selectlist as $k => $v) {
|
||||
$list[$v['id']] = $v;
|
||||
}
|
||||
}
|
||||
foreach ($datalist as $k => &$v) {
|
||||
$v[$renderkey] = $list[$v[$fieldkey]] ?? null;
|
||||
}
|
||||
unset($v);
|
||||
return $datalist;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置错误信息
|
||||
*
|
||||
* @param string $error 错误信息
|
||||
* @return void
|
||||
*/
|
||||
public static function setError($error)
|
||||
{
|
||||
self::$_error = $error;
|
||||
abort($error);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误信息
|
||||
* @return string
|
||||
*/
|
||||
public static function getError()
|
||||
{
|
||||
return self::$_error ? __(self::$_error) : '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
/**
|
||||
* @desc JwtCacheTokenExveption.php 描述信息
|
||||
* @author Tinywan(ShaoBo Wan)
|
||||
* @date 2022/3/18 18:15
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace support\Jwt\Exception;
|
||||
|
||||
class JwtCacheTokenException extends \RuntimeException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* @desc JwtConfigException
|
||||
* @author Tinywan(ShaoBo Wan)
|
||||
* @date 2022/2/21 9:53
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace support\Jwt\Exception;
|
||||
|
||||
|
||||
class JwtConfigException extends \RuntimeException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* @desc JwtRefreshTokenExpiredException
|
||||
* @author Tinywan(ShaoBo Wan)
|
||||
* @date 2022/2/21 9:53
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace support\Jwt\Exception;
|
||||
|
||||
|
||||
class JwtRefreshTokenExpiredException extends \RuntimeException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
/**
|
||||
* @desc JwtTokenException.php 描述信息
|
||||
* @author Tinywan(ShaoBo Wan)
|
||||
* @date 2022/2/21 9:53
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace support\Jwt\Exception;
|
||||
|
||||
class JwtTokenException extends \RuntimeException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* @desc JwtTokenExpiredException
|
||||
* @author Tinywan(ShaoBo Wan)
|
||||
* @date 2022/2/21 9:53
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace support\Jwt\Exception;
|
||||
|
||||
|
||||
class JwtTokenExpiredException extends \RuntimeException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,394 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @desc JwtToken.php 描述信息
|
||||
* @date 2022/2/21 9:45
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace support\Jwt;
|
||||
|
||||
use Firebase\JWT\BeforeValidException;
|
||||
use Firebase\JWT\ExpiredException;
|
||||
use Firebase\JWT\JWT;
|
||||
use Firebase\JWT\Key;
|
||||
use Firebase\JWT\SignatureInvalidException;
|
||||
use support\Jwt\Exception\JwtCacheTokenException;
|
||||
use support\Jwt\Exception\JwtRefreshTokenExpiredException;
|
||||
use support\Jwt\Exception\JwtTokenException;
|
||||
use support\Jwt\Exception\JwtConfigException;
|
||||
use support\Jwt\Exception\JwtTokenExpiredException;
|
||||
use UnexpectedValueException;
|
||||
|
||||
class JwtToken
|
||||
{
|
||||
/**
|
||||
* access_token.
|
||||
*/
|
||||
private const ACCESS_TOKEN = 1;
|
||||
|
||||
/**
|
||||
* refresh_token.
|
||||
*/
|
||||
private const REFRESH_TOKEN = 2;
|
||||
|
||||
/** WEB Client. */
|
||||
public const TOKEN_CLIENT_WEB = 'WEB';
|
||||
|
||||
/** Mobile Client. */
|
||||
public const TOKEN_CLIENT_MOBILE = 'MOBILE';
|
||||
|
||||
/** PC Client. */
|
||||
public const TOKEN_CLIENT_PC = 'PC';
|
||||
|
||||
/**
|
||||
* @desc: 获取当前登录ID
|
||||
* @return mixed
|
||||
* @throws JwtTokenException
|
||||
*/
|
||||
public static function getCurrentId()
|
||||
{
|
||||
return self::getExtendVal('id') ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc: 获取当前用户信息
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getUser()
|
||||
{
|
||||
$config = self::_getConfig();
|
||||
if (is_callable($config['user_model'])) {
|
||||
return $config['user_model'](self::getCurrentId());
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc: 获取指定令牌扩展内容字段的值
|
||||
*
|
||||
* @param string $val
|
||||
* @return mixed|string
|
||||
* @throws JwtTokenException
|
||||
*/
|
||||
public static function getExtendVal(string $val)
|
||||
{
|
||||
return self::getTokenExtend()[$val] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 获取指定令牌扩展内容
|
||||
* @return array
|
||||
* @throws JwtTokenException
|
||||
*/
|
||||
public static function getExtend(): array
|
||||
{
|
||||
return self::getTokenExtend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc: 刷新令牌
|
||||
* @param array $_extend 拓展数据
|
||||
* @return array|string[]
|
||||
* @throws JwtTokenException
|
||||
*/
|
||||
public static function refreshToken(&$_extend = []): array
|
||||
{
|
||||
$token = self::getTokenFromHeaders();
|
||||
$config = self::_getConfig();
|
||||
try {
|
||||
$extend = self::verifyToken($token, self::REFRESH_TOKEN);
|
||||
if (!empty($extend['extend'])){
|
||||
$_extend = $extend['extend'];
|
||||
}
|
||||
} catch (SignatureInvalidException $signatureInvalidException) {
|
||||
throw new JwtRefreshTokenExpiredException('Refresh token is invalid.',401021);
|
||||
} catch (BeforeValidException $beforeValidException) {
|
||||
throw new JwtRefreshTokenExpiredException('The refresh token has not yet taken effect.',401022);
|
||||
} catch (ExpiredException $expiredException) {
|
||||
throw new JwtRefreshTokenExpiredException('The refresh token session has expired, please log in again!',401023);
|
||||
} catch (UnexpectedValueException $unexpectedValueException) {
|
||||
throw new JwtRefreshTokenExpiredException('The extended field obtained from the refresh token does not exist.',401024);
|
||||
} catch (JwtCacheTokenException | \Exception $exception) {
|
||||
throw new JwtRefreshTokenExpiredException($exception->getMessage(),401025);
|
||||
}
|
||||
$payload = self::generatePayload($config, $extend['extend']);
|
||||
$secretKey = self::getPrivateKey($config);
|
||||
$extend['exp'] = time() + $config['access_exp'];
|
||||
$newToken['access_token'] = self::makeToken($extend, $secretKey, $config['algorithms']);
|
||||
if (!isset($config['refresh_disable']) || ($config['refresh_disable'] === false)) {
|
||||
$refreshSecretKey = self::getPrivateKey($config, self::REFRESH_TOKEN);
|
||||
$newToken['refresh_token'] = self::makeToken($payload['refreshPayload'], $refreshSecretKey, $config['algorithms']);
|
||||
}
|
||||
if ($config['is_single_device']) {
|
||||
$client = $extend['extend']['client'] ?? self::TOKEN_CLIENT_WEB;
|
||||
RedisHandler::generateToken($config['cache_token_pre'], (string)$client, (string)$extend['extend']['id'], $config['access_exp'], $newToken['access_token']);
|
||||
RedisHandler::refreshToken($config["cache_refresh_token_pre"], (string)$client, (string)$extend['extend']['id'], $config['refresh_exp'], $newToken['refresh_token']);
|
||||
}
|
||||
return $newToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc: 生成令牌.
|
||||
* @param array $extend
|
||||
* @return array
|
||||
* @throws JwtConfigException
|
||||
*/
|
||||
public static function generateToken(array $extend): array
|
||||
{
|
||||
if (!isset($extend['id'])) {
|
||||
throw new JwtTokenException('Missing globally unique field: ID.');
|
||||
}
|
||||
$config = self::_getConfig();
|
||||
$config['access_exp'] = $extend['access_exp'] ?? $config['access_exp'];
|
||||
$config['refresh_exp'] = $extend['refresh_exp'] ?? $config['refresh_exp'];
|
||||
$payload = self::generatePayload($config, $extend);
|
||||
$secretKey = self::getPrivateKey($config);
|
||||
$token = [
|
||||
'token_type' => 'Bearer',
|
||||
'expires_in' => $config['access_exp'],
|
||||
'access_token' => self::makeToken($payload['accessPayload'], $secretKey, $config['algorithms'])
|
||||
];
|
||||
if (!isset($config['refresh_disable']) || ($config['refresh_disable'] === false)) {
|
||||
$refreshSecretKey = self::getPrivateKey($config, self::REFRESH_TOKEN);
|
||||
$token['refresh_token'] = self::makeToken($payload['refreshPayload'], $refreshSecretKey, $config['algorithms']);
|
||||
}
|
||||
if ($config['is_single_device']) {
|
||||
$client = $extend['client'] ?? self::TOKEN_CLIENT_WEB;
|
||||
RedisHandler::generateToken($config['cache_token_pre'], (string)$client, (string)$extend['id'], $config['access_exp'], $token['access_token']);
|
||||
if (!isset($config['refresh_disable']) || ($config['refresh_disable'] === false)) {
|
||||
if (isset($config["cache_refresh_token_pre"])) {
|
||||
RedisHandler::generateToken($config["cache_refresh_token_pre"], (string)$client, (string)$extend['id'], $config['refresh_exp'], $token['refresh_token']);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc: 验证令牌
|
||||
* @param int $tokenType
|
||||
* @param string|null $token
|
||||
* @return array
|
||||
* @throws JwtTokenException
|
||||
*/
|
||||
public static function verify(int $tokenType = self::ACCESS_TOKEN, string $token = null): array
|
||||
{
|
||||
$token = $token ?? self::getTokenFromHeaders();
|
||||
try {
|
||||
return self::verifyToken($token, $tokenType);
|
||||
} catch (SignatureInvalidException $signatureInvalidException) {
|
||||
throw new JwtTokenException('Authentication token is invalid.',401011);
|
||||
} catch (BeforeValidException $beforeValidException) {
|
||||
throw new JwtTokenException('The authentication token has not yet taken effect.',401012);
|
||||
} catch (ExpiredException $expiredException) {
|
||||
throw new JwtTokenExpiredException('The authentication session has expired, please log in again!',401013);
|
||||
} catch (UnexpectedValueException $unexpectedValueException) {
|
||||
throw new JwtTokenException('The obtained extended field does not exist.',401014);
|
||||
} catch (JwtCacheTokenException | \Exception $exception) {
|
||||
throw new JwtTokenException($exception->getMessage(),401015);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc: 获取扩展字段.
|
||||
* @return array
|
||||
* @throws JwtTokenException
|
||||
*/
|
||||
private static function getTokenExtend(): array
|
||||
{
|
||||
return (array)self::verify()['extend'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc: 获令牌有效期剩余时长.
|
||||
* @param int $tokenType
|
||||
* @return int
|
||||
*/
|
||||
public static function getTokenExp(int $tokenType = self::ACCESS_TOKEN): int
|
||||
{
|
||||
return (int)self::verify($tokenType)['exp'] - time();
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc: 获取Header头部authorization令牌
|
||||
*
|
||||
* @throws JwtTokenException
|
||||
*/
|
||||
private static function getTokenFromHeaders(): string
|
||||
{
|
||||
$authorization = request()->header('token',request()->header('authorization'));
|
||||
if (!$authorization || 'undefined' == $authorization) {
|
||||
$config = self::_getConfig();
|
||||
if (!isset($config['is_support_get_token']) || false === $config['is_support_get_token']) {
|
||||
throw new JwtTokenException('The request did not carry authorization information.',401000);
|
||||
}
|
||||
$authorization = request()->get($config['is_support_get_token_key']);
|
||||
if (empty($authorization)) {
|
||||
throw new JwtTokenException('The request did not carry authorization information.',401000);
|
||||
}
|
||||
}
|
||||
if(substr($authorization,0,6) != 'Bearer'){
|
||||
$authorization = 'Bearer '.$authorization;
|
||||
}
|
||||
|
||||
if (self::REFRESH_TOKEN != substr_count($authorization, '.')) {
|
||||
throw new JwtTokenException('Illegal authorization information.'.$authorization,401001);
|
||||
}
|
||||
|
||||
if (2 != count(explode(' ', $authorization))) {
|
||||
throw new JwtTokenException('Invalid token.',401000);
|
||||
}
|
||||
|
||||
[$type, $token] = explode(' ', $authorization);
|
||||
if ('Bearer' !== $type) {
|
||||
throw new JwtTokenException('Invalid token.',401000);
|
||||
}
|
||||
if (!$token || 'undefined' === $token) {
|
||||
throw new JwtTokenException('The Authorization information attempted to retrieve does not exist.',401000);
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc: 校验令牌
|
||||
* @param string $token
|
||||
* @param int $tokenType
|
||||
* @return array
|
||||
*/
|
||||
private static function verifyToken(string $token, int $tokenType): array
|
||||
{
|
||||
$config = self::_getConfig();
|
||||
$publicKey = self::ACCESS_TOKEN == $tokenType ? self::getPublicKey($config['algorithms']) : self::getPublicKey($config['algorithms'], self::REFRESH_TOKEN);
|
||||
JWT::$leeway = $config['leeway'];
|
||||
|
||||
$decoded = JWT::decode($token, new Key($publicKey, $config['algorithms']));
|
||||
$decodeToken = json_decode(json_encode($decoded), true);
|
||||
if ($config['is_single_device']) {
|
||||
$cacheTokenPre = $config['cache_token_pre'];
|
||||
if ($tokenType == self::REFRESH_TOKEN) {
|
||||
$cacheTokenPre = $config['cache_refresh_token_pre'];
|
||||
}
|
||||
$client = $decodeToken['extend']['client'] ?? self::TOKEN_CLIENT_WEB;
|
||||
RedisHandler::verifyToken($cacheTokenPre, $client, (string)$decodeToken['extend']['id'], $token);
|
||||
}
|
||||
return $decodeToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc: 生成令牌.
|
||||
*
|
||||
* @param array $payload 载荷信息
|
||||
* @param string $secretKey 签名key
|
||||
* @param string $algorithms 算法
|
||||
* @return string
|
||||
*/
|
||||
private static function makeToken(array $payload, string $secretKey, string $algorithms): string
|
||||
{
|
||||
return JWT::encode($payload, $secretKey, $algorithms);
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc: 获取加密载体.
|
||||
*
|
||||
* @param array $config 配置文件
|
||||
* @param array $extend 扩展加密字段
|
||||
* @return array
|
||||
*/
|
||||
private static function generatePayload(array $config, array $extend): array
|
||||
{
|
||||
$basePayload = [
|
||||
'iss' => $config['iss'], // 签发者
|
||||
'aud' => $config['iss'], // 接收该JWT的一方
|
||||
'iat' => time(), // 签发时间
|
||||
'nbf' => time() + ($config['nbf'] ?? 0), // 某个时间点后才能访问
|
||||
'exp' => time() + $config['access_exp'], // 过期时间
|
||||
'extend' => $extend // 自定义扩展信息
|
||||
];
|
||||
$resPayLoad['accessPayload'] = $basePayload;
|
||||
$basePayload['exp'] = time() + $config['refresh_exp'];
|
||||
$resPayLoad['refreshPayload'] = $basePayload;
|
||||
|
||||
return $resPayLoad;
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc: 根据签名算法获取【公钥】签名值
|
||||
* @param string $algorithm 算法
|
||||
* @param int $tokenType 类型
|
||||
* @return string
|
||||
* @throws JwtConfigException
|
||||
*/
|
||||
private static function getPublicKey(string $algorithm, int $tokenType = self::ACCESS_TOKEN): string
|
||||
{
|
||||
$config = self::_getConfig();
|
||||
switch ($algorithm) {
|
||||
case 'HS256':
|
||||
$key = self::ACCESS_TOKEN == $tokenType ? $config['access_secret_key'] : $config['refresh_secret_key'];
|
||||
break;
|
||||
case 'RS512':
|
||||
case 'RS256':
|
||||
$key = self::ACCESS_TOKEN == $tokenType ? $config['access_public_key'] : $config['refresh_public_key'];
|
||||
break;
|
||||
default:
|
||||
$key = $config['access_secret_key'];
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc: 根据签名算法获取【私钥】签名值
|
||||
* @param array $config 配置文件
|
||||
* @param int $tokenType 令牌类型
|
||||
* @return string
|
||||
*/
|
||||
private static function getPrivateKey(array $config, int $tokenType = self::ACCESS_TOKEN): string
|
||||
{
|
||||
switch ($config['algorithms']) {
|
||||
case 'HS256':
|
||||
$key = self::ACCESS_TOKEN == $tokenType ? $config['access_secret_key'] : $config['refresh_secret_key'];
|
||||
break;
|
||||
case 'RS512':
|
||||
case 'RS256':
|
||||
$key = self::ACCESS_TOKEN == $tokenType ? $config['access_private_key'] : $config['refresh_private_key'];
|
||||
break;
|
||||
default:
|
||||
$key = $config['access_secret_key'];
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc: 获取配置文件
|
||||
* @return array
|
||||
* @throws JwtConfigException
|
||||
*/
|
||||
private static function _getConfig(): array
|
||||
{
|
||||
$config = config('jwt.jwt');
|
||||
if (empty($config)) {
|
||||
throw new JwtConfigException('Invalid config.');
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc: 注销令牌
|
||||
* @param string $client
|
||||
* @return bool
|
||||
*/
|
||||
public static function clear(string $client = self::TOKEN_CLIENT_WEB): bool
|
||||
{
|
||||
$config = self::_getConfig();
|
||||
if ($config['is_single_device']) {
|
||||
$clearCacheRefreshTokenPre = RedisHandler::clearToken($config['cache_refresh_token_pre'], $client, (string)self::getCurrentId());
|
||||
$clearCacheTokenPre = RedisHandler::clearToken($config['cache_token_pre'], $client, (string)self::getCurrentId());
|
||||
return $clearCacheTokenPre && $clearCacheRefreshTokenPre;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @desc RedisHanle.php 描述信息
|
||||
* @author Tinywan(ShaoBo Wan)
|
||||
* @date 2022/3/18 17:13
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace support\Jwt;
|
||||
|
||||
use support\Redis;
|
||||
use support\Jwt\Exception\JwtCacheTokenException;
|
||||
|
||||
class RedisHandler
|
||||
{
|
||||
/**
|
||||
* @desc: 生成缓存令牌
|
||||
* (1)登录时,判断该账号是否在其它设备登录,如果有,就请空之前key清除,
|
||||
* (2)重新设置key,然后存储用户信息在redis当中
|
||||
* @param string $pre
|
||||
* @param string $client
|
||||
* @param string $uid
|
||||
* @param int $ttl
|
||||
* @param string $token
|
||||
* @author Tinywan(ShaoBo Wan)
|
||||
*/
|
||||
public static function generateToken(string $pre, string $client, string $uid, int $ttl, string $token): void
|
||||
{
|
||||
$cacheKey = $pre . $client. ':'. $uid;
|
||||
Redis::del($cacheKey);
|
||||
Redis::setex($cacheKey, $ttl, $token);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @desc: 刷新存储的缓存令牌
|
||||
* @param string $pre
|
||||
* @param string $client
|
||||
* @param string $uid
|
||||
* @param int $ttl
|
||||
* @param string $token
|
||||
* @return void
|
||||
*/
|
||||
public static function refreshToken(string $pre, string $client, string $uid, int $ttl, string $token): void
|
||||
{
|
||||
$cacheKey = $pre . $client . ':' . $uid;
|
||||
$isExists = Redis::exists($cacheKey);
|
||||
if ($isExists) {
|
||||
$ttl = Redis::ttl($cacheKey);
|
||||
}
|
||||
Redis::setex($cacheKey, $ttl, $token);
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc: 检查设备缓存令牌
|
||||
* @param string $pre
|
||||
* @param string $client
|
||||
* @param string $uid
|
||||
* @param string $token
|
||||
* @return bool
|
||||
* @author Tinywan(ShaoBo Wan)
|
||||
*/
|
||||
public static function verifyToken(string $pre, string $client, string $uid, string $token): bool
|
||||
{
|
||||
$cacheKey = $pre . $client. ':'. $uid;
|
||||
if (!Redis::exists($cacheKey)) {
|
||||
throw new JwtCacheTokenException('The account has been logged in on another device and forced to log out.');
|
||||
}
|
||||
if (Redis::get($cacheKey) != $token) {
|
||||
throw new JwtCacheTokenException('Identity verification session has expired, please log in again!');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc: 清理缓存令牌
|
||||
* @param string $pre
|
||||
* @param string $client
|
||||
* @param string $uid
|
||||
* @return bool
|
||||
* @author Tinywan(ShaoBo Wan)
|
||||
*/
|
||||
public static function clearToken(string $pre, string $client, string $uid): bool
|
||||
{
|
||||
Redis::del($pre . $client. ':'. $uid);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'enable' => true,
|
||||
'jwt' => [
|
||||
/** 算法类型 HS256、HS384、HS512、RS256、RS384、RS512、ES256、ES384、Ed25519 */
|
||||
'algorithms' => 'HS256',
|
||||
|
||||
/** access令牌秘钥 */
|
||||
'access_secret_key' => '2022d3d3LmJq',
|
||||
|
||||
/** access令牌过期时间,单位:秒。默认 2 小时 */
|
||||
'access_exp' => 7200,
|
||||
|
||||
/** refresh令牌秘钥 */
|
||||
'refresh_secret_key' => '2022KTxigxc9o50c',
|
||||
|
||||
/** refresh令牌过期时间,单位:秒。默认 7 天 */
|
||||
'refresh_exp' => 604800,
|
||||
|
||||
/** refresh 令牌是否禁用,默认不禁用 false */
|
||||
'refresh_disable' => false,
|
||||
|
||||
/** 令牌签发者 */
|
||||
'iss' => 'webman.tinywan.cn',
|
||||
|
||||
/** 某个时间点后才能访问,单位秒。(如:30 表示当前时间30秒后才能使用) */
|
||||
'nbf' => 0,
|
||||
|
||||
/** 时钟偏差冗余时间,单位秒。建议这个余地应该不大于几分钟 */
|
||||
'leeway' => 60,
|
||||
|
||||
/** 是否允许单设备登录,默认不允许 false */
|
||||
'is_single_device' => false,
|
||||
|
||||
/** 缓存令牌时间,单位:秒。默认 7 天 */
|
||||
'cache_token_ttl' => 604800,
|
||||
|
||||
/** 缓存令牌前缀,默认 JWT:TOKEN: */
|
||||
'cache_token_pre' => 'JWT:TOKEN:',
|
||||
|
||||
/** 缓存刷新令牌前缀,默认 JWT:REFRESH_TOKEN: */
|
||||
'cache_refresh_token_pre' => 'JWT:REFRESH_TOKEN:',
|
||||
|
||||
/** 用户信息模型 */
|
||||
'user_model' => function ($uid) {
|
||||
return [];
|
||||
},
|
||||
|
||||
/** 是否支持 get 请求获取令牌 */
|
||||
'is_support_get_token' => false,
|
||||
/** GET 请求获取令牌请求key */
|
||||
'is_support_get_token_key' => 'authorization',
|
||||
|
||||
/** access令牌私钥 */
|
||||
'access_private_key' => <<<EOD
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
...
|
||||
-----END RSA PRIVATE KEY-----
|
||||
EOD,
|
||||
|
||||
/** access令牌公钥 */
|
||||
'access_public_key' => <<<EOD
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
...
|
||||
-----END PUBLIC KEY-----
|
||||
EOD,
|
||||
|
||||
/** refresh令牌私钥 */
|
||||
'refresh_private_key' => <<<EOD
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
...
|
||||
-----END RSA PRIVATE KEY-----
|
||||
EOD,
|
||||
|
||||
/** refresh令牌公钥 */
|
||||
'refresh_public_key' => <<<EOD
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
...
|
||||
-----END PUBLIC KEY-----
|
||||
EOD,
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace support;
|
||||
|
||||
/**
|
||||
* 随机生成类
|
||||
*/
|
||||
class Random
|
||||
{
|
||||
|
||||
/**
|
||||
* 生成数字和字母
|
||||
*
|
||||
* @param int $len 长度
|
||||
* @return string
|
||||
*/
|
||||
public static function alnum($len = 6)
|
||||
{
|
||||
return self::build('alnum', $len);
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅生成字符
|
||||
*
|
||||
* @param int $len 长度
|
||||
* @return string
|
||||
*/
|
||||
public static function alpha($len = 6)
|
||||
{
|
||||
return self::build('alpha', $len);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成指定长度的随机数字
|
||||
*
|
||||
* @param int $len 长度
|
||||
* @return string
|
||||
*/
|
||||
public static function numeric($len = 4)
|
||||
{
|
||||
return self::build('numeric', $len);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成指定长度的无0随机数字
|
||||
*
|
||||
* @param int $len 长度
|
||||
* @return string
|
||||
*/
|
||||
public static function nozero($len = 4)
|
||||
{
|
||||
return self::build('nozero', $len);
|
||||
}
|
||||
|
||||
/**
|
||||
* 能用的随机数生成
|
||||
* @param string $type 类型 alpha/alnum/numeric/nozero/unique/md5/encrypt/sha1
|
||||
* @param int $len 长度
|
||||
* @return string
|
||||
*/
|
||||
public static function build($type = 'alnum', $len = 8)
|
||||
{
|
||||
switch ($type) {
|
||||
case 'alpha':
|
||||
case 'alnum':
|
||||
case 'numeric':
|
||||
case 'nozero':
|
||||
switch ($type) {
|
||||
case 'alpha':
|
||||
$pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
break;
|
||||
case 'alnum':
|
||||
$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
break;
|
||||
case 'numeric':
|
||||
$pool = '0123456789';
|
||||
break;
|
||||
case 'nozero':
|
||||
$pool = '123456789';
|
||||
break;
|
||||
}
|
||||
return substr(str_shuffle(str_repeat($pool, ceil($len / strlen($pool)))), 0, $len);
|
||||
case 'unique':
|
||||
case 'md5':
|
||||
return md5(uniqid(mt_rand()));
|
||||
case 'encrypt':
|
||||
case 'sha1':
|
||||
return sha1(uniqid(mt_rand(), true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全球唯一标识
|
||||
* @return string
|
||||
*/
|
||||
public static function uuid()
|
||||
{
|
||||
return sprintf(
|
||||
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
|
||||
mt_rand(0, 0xffff),
|
||||
mt_rand(0, 0xffff),
|
||||
mt_rand(0, 0xffff),
|
||||
mt_rand(0, 0x0fff) | 0x4000,
|
||||
mt_rand(0, 0x3fff) | 0x8000,
|
||||
mt_rand(0, 0xffff),
|
||||
mt_rand(0, 0xffff),
|
||||
mt_rand(0, 0xffff)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
namespace support\TagLib;
|
||||
class Html extends \think\template\TagLib{
|
||||
|
||||
/**
|
||||
* 定义标签列表
|
||||
*/
|
||||
protected $tags = [
|
||||
'switch' => ['attr' => 'name,yes,no,value', 'close' => 0],
|
||||
'radio' => ['attr' => 'name,options,value', 'close' => 0],
|
||||
'checkbox' => ['attr' => 'name,options,value', 'close' => 0],
|
||||
'upload' => ['attr' => 'name,value,mimetype,multiple,url,maxsize,maxcount,params', 'close' => 0]
|
||||
];
|
||||
/**
|
||||
* 上传
|
||||
* @param mixed $tag
|
||||
* @return string
|
||||
*/
|
||||
public function tagUpload($tag)
|
||||
{
|
||||
$value = isset($tag['value'])?$tag['value']:'';unset($tag['value']);
|
||||
$name = isset($tag['name'])?$tag['name']:'';unset($tag['name']);
|
||||
$mimetype = isset($tag['mimetype'])?$tag['mimetype']:'';unset($tag['mimetype']);
|
||||
$multiple = isset($tag['multiple'])?$tag['multiple']:'';unset($tag['multiple']);
|
||||
$maxcount = isset($tag['maxcount'])?$tag['maxcount']:'';unset($tag['maxcount']);
|
||||
$maxsize = isset($tag['maxsize'])?$tag['maxsize']:'';unset($tag['maxsize']);
|
||||
$url = isset($tag['url'])?$tag['url']:'';unset($tag['url']);
|
||||
// 支持用函数传数组
|
||||
$parseStr = '';
|
||||
$name = $this->buildvar($name,$parseStr,'name');
|
||||
$value = $this->buildvar($value,$parseStr,'value');
|
||||
$mimetype = $this->buildvar($mimetype,$mimetype,'mimetype');
|
||||
$multiple = $this->buildvar($multiple,$multiple,'multiple');
|
||||
$maxcount = $this->buildvar($maxcount,$maxcount,'maxcount');
|
||||
$maxsize = $this->buildvar($maxsize,$maxsize,'maxsize');
|
||||
$url = $this->buildvar($url,$url,'url');
|
||||
$extendData = $this->buildExtendData($tag);
|
||||
$parseStr .= '<input id="c-{'.$name.'}" class="form-control" size="50" name="{'.$name.'}" type="hidden" value="{'.$value.'}">';
|
||||
$parseStr .= '<ul class="list-inline clearfix lyear-uploads-pic" data-template="preview" id="p-{'.$name.'}">';
|
||||
$parseStr .= ' <li nodelete class="col-xs-4 col-sm-3 col-md-2">';
|
||||
$parseStr .= ' <a class="pic-add faupload" style="height: auto;border: 0;"';
|
||||
$parseStr .= ' permission="app.admin.upload.avatar" id="add-pic-btn" href="#!" title="点击上传"';
|
||||
$parseStr .= ' data-input-id="c-{'.$name.'}"';
|
||||
$parseStr .= ' data-mimetype="{'.$mimetype.'}"';
|
||||
$parseStr .= ' data-multiple="{'.$multiple.'}" ';
|
||||
$parseStr .= ' data-maxsize="{'.$maxsize.'}" ';
|
||||
$parseStr .= ' data-maxcount="{'.$maxcount.'}" ';
|
||||
$parseStr .= ' data-url="{'.$url.'}" ';
|
||||
$parseStr .= ' '.$extendData.' ';
|
||||
$parseStr .= ' data-preview-id="p-{'.$name.'}"></a>';
|
||||
$parseStr .= ' <a class="pic-add fachoose" style="height: auto;border: 0;"';
|
||||
$parseStr .= ' permission="app.admin.upload.attachment" ';
|
||||
$parseStr .= ' id="choose-pic-btn" href="#!" title="选择文件"';
|
||||
$parseStr .= ' data-input-id="c-{'.$name.'}" ';
|
||||
$parseStr .= ' data-mimetype="{'.$mimetype.'}" ';
|
||||
$parseStr .= ' data-multiple="{'.$multiple.'}" ';
|
||||
$parseStr .= ' data-maxsize="{'.$maxsize.'}" ';
|
||||
$parseStr .= ' data-maxcount="{'.$maxcount.'}" ';
|
||||
$parseStr .= ' data-url="{'.$url.'}" ';
|
||||
$parseStr .= ' '.$extendData.' ';
|
||||
$parseStr .= ' data-preview-id="p-{'.$name.'}"></a>';
|
||||
$parseStr .= ' </li>';
|
||||
$parseStr .= '</ul>';
|
||||
return $parseStr;
|
||||
}
|
||||
/**
|
||||
* switch
|
||||
*/
|
||||
public function tagSwitch($tag)
|
||||
{
|
||||
$yes = isset($tag['yes'])?$tag['yes']:1;unset($tag['yes']);
|
||||
$no = isset($tag['no'])?$tag['no']:0;unset($tag['no']);
|
||||
$value = isset($tag['value'])?$tag['value']:1;unset($tag['value']);
|
||||
$name = isset($tag['name'])?$tag['name']:'';unset($tag['name']);
|
||||
// 支持用函数传数组
|
||||
$parseStr = '';
|
||||
$name = $this->buildvar($name,$parseStr,'name');
|
||||
$value = $this->buildvar($value,$parseStr,'value');
|
||||
$no = $this->buildvar($no,$parseStr,'no',0);
|
||||
$yes = $this->buildvar($yes,$parseStr,'yes',1);
|
||||
$extendData = $this->buildExtendData($tag);
|
||||
$parseStr .= '<?php ';
|
||||
$parseStr .= $yes.'='.$yes.'??1;'.PHP_EOL;
|
||||
$parseStr .= $no.'='.$no.'??0;'.PHP_EOL;
|
||||
$parseStr .= $value.'='.$yes.'=='.$value .' ? '.$yes.' : '.$no.';'.PHP_EOL;
|
||||
$parseStr .= '?><label data-role="switcher" class="lyear-switch switch-success switch-light switch-outline">';
|
||||
$parseStr .= ' <input type="hidden" data-yes="{'.$yes.'}" data-no="{'.$no.'}" id="c-{'.$name.'}" name="{'.$name.'}" value="{'.$value.'}"/>';
|
||||
$parseStr .= ' <input id="c-{'.$name.'}-switch" type="checkbox" {if '.$yes .'=='. $value.'}checked{/if} '.$extendData.' />';
|
||||
$parseStr .= ' <span></span>';
|
||||
$parseStr .= '</label>';
|
||||
return $parseStr;
|
||||
}
|
||||
/**
|
||||
* radio
|
||||
*/
|
||||
public function tagRadio($tag)
|
||||
{
|
||||
$value = isset($tag['value'])?$tag['value']:1;unset($tag['value']);
|
||||
$name = isset($tag['name'])?$tag['name']:'';unset($tag['name']);
|
||||
$option = isset($tag['options'])?$tag['options']:[];unset($tag['options']);
|
||||
// 支持用函数传数组
|
||||
$parseStr = '';
|
||||
$name = $this->buildvar($name,$parseStr,'name');
|
||||
$value = $this->buildvar($value,$parseStr,'value');
|
||||
$options = $this->buildvar($option,$parseStr,'option');
|
||||
$extendData = $this->buildExtendData($tag);
|
||||
$parseStr .= '<?php foreach ('.$options. ' as $_key=>$_val){?>';
|
||||
$parseStr .= '<label class="lyear-radio radio-inline radio-primary">';
|
||||
$parseStr .= ' <input name="{'.$name.'}" value="{$_key}" id="c-{'.$name.'}-radio" type="radio" {if $_key=='.$value.'}checked{/if} '.$extendData.' />';
|
||||
$parseStr .= ' <span>{$_val}</span>';
|
||||
$parseStr .= '</label>';
|
||||
$parseStr .= '<?php }?>';
|
||||
return $parseStr;
|
||||
}
|
||||
/**
|
||||
* checkbox
|
||||
*/
|
||||
public function tagCheckbox($tag)
|
||||
{
|
||||
$value = isset($tag['value'])?$tag['value']:1;unset($tag['value']);
|
||||
$name = isset($tag['name'])?$tag['name']:'';unset($tag['name']);
|
||||
$option = isset($tag['options'])?$tag['options']:[];unset($tag['options']);
|
||||
// 支持用函数传数组
|
||||
$parseStr = '';
|
||||
$name = $this->buildvar($name,$parseStr,'name');
|
||||
$value = $this->buildvar($value,$parseStr,'value');
|
||||
$options = $this->buildvar($option,$parseStr,'option');
|
||||
$extendData = $this->buildExtendData($tag);
|
||||
$parseStr .= '<?php foreach ('.$options. ' as $_key=>$_val){?>';
|
||||
$parseStr .= '<label class="lyear-checkbox checkbox-inline checkbox-primary">';
|
||||
$parseStr .= ' <input name="{'.$name.'}[]" value="{$_key}" id="c-{'.$name.'}-{$_key}" type="checkbox" {if in_array($_key,'.$value.')}checked{/if} '.$extendData.' />';
|
||||
$parseStr .= ' <span>{$_val}</span>';
|
||||
$parseStr .= '</label>';
|
||||
$parseStr .= '<?php }?>';
|
||||
return $parseStr;
|
||||
}
|
||||
|
||||
protected function buildvar($name='',&$parseStr='',$defaultName='',$default=''){
|
||||
$var = '$'.$defaultName.'_' . uniqid();
|
||||
$parseStr .= '<?php ';
|
||||
if(null == $name){
|
||||
$parseStr .= $var . '="'.$default.'"; '.PHP_EOL;
|
||||
}elseif (in_array(substr($name, 0, 1),[':','$'])) {
|
||||
$name = $this->autoBuildVar($name);
|
||||
$parseStr .= $var . '=' . $name . '; '.PHP_EOL;
|
||||
}else{
|
||||
$parseStr .= $var . '="' . $name . '"; '.PHP_EOL;
|
||||
}
|
||||
$parseStr .='?>';
|
||||
return $var;
|
||||
}
|
||||
protected function buildExtendData($arr=[]){
|
||||
$parseStr = '';
|
||||
foreach($arr as $key=>$val){
|
||||
$parseStr .= $key . '="' . $val . '"';
|
||||
}
|
||||
return $parseStr;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
use Dotenv\Dotenv;
|
||||
use support\Log;
|
||||
use Webman\Bootstrap;
|
||||
use Webman\Config;
|
||||
use Webman\Middleware;
|
||||
use Webman\Route;
|
||||
use Webman\Util;
|
||||
use Workerman\Events\Select;
|
||||
use Workerman\Worker;
|
||||
|
||||
$worker = $worker ?? null;
|
||||
|
||||
if (empty(Worker::$eventLoopClass)) {
|
||||
Worker::$eventLoopClass = Select::class;
|
||||
}
|
||||
|
||||
set_error_handler(function ($level, $message, $file = '', $line = 0) {
|
||||
if (error_reporting() & $level) {
|
||||
throw new ErrorException($message, 0, $level, $file, $line);
|
||||
}
|
||||
});
|
||||
|
||||
if ($worker) {
|
||||
register_shutdown_function(function ($startTime) {
|
||||
if (time() - $startTime <= 0.1) {
|
||||
sleep(1);
|
||||
}
|
||||
}, time());
|
||||
}
|
||||
|
||||
if (class_exists('Dotenv\Dotenv') && file_exists(base_path(false) . '/.env')) {
|
||||
if (method_exists('Dotenv\Dotenv', 'createUnsafeMutable')) {
|
||||
Dotenv::createUnsafeMutable(base_path(false))->load();
|
||||
} else {
|
||||
Dotenv::createMutable(base_path(false))->load();
|
||||
}
|
||||
}
|
||||
|
||||
Config::clear();
|
||||
support\App::loadAllConfig(['route']);
|
||||
if ($timezone = config('app.default_timezone')) {
|
||||
date_default_timezone_set($timezone);
|
||||
}
|
||||
|
||||
foreach (config('autoload.files', []) as $file) {
|
||||
include_once $file;
|
||||
}
|
||||
foreach (config('plugin', []) as $firm => $projects) {
|
||||
foreach ($projects as $name => $project) {
|
||||
if (!is_array($project)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($project['autoload']['files'] ?? [] as $file) {
|
||||
include_once $file;
|
||||
}
|
||||
}
|
||||
foreach ($projects['autoload']['files'] ?? [] as $file) {
|
||||
include_once $file;
|
||||
}
|
||||
}
|
||||
|
||||
Middleware::load(config('middleware', []));
|
||||
foreach (config('plugin', []) as $firm => $projects) {
|
||||
foreach ($projects as $name => $project) {
|
||||
if (!is_array($project) || $name === 'static') {
|
||||
continue;
|
||||
}
|
||||
Middleware::load($project['middleware'] ?? []);
|
||||
}
|
||||
Middleware::load($projects['middleware'] ?? [], $firm);
|
||||
if ($staticMiddlewares = config("plugin.$firm.static.middleware")) {
|
||||
Middleware::load(['__static__' => $staticMiddlewares], $firm);
|
||||
}
|
||||
}
|
||||
Middleware::load(['__static__' => config('static.middleware', [])]);
|
||||
|
||||
foreach (config('bootstrap', []) as $className) {
|
||||
if (!class_exists($className)) {
|
||||
$log = "Warning: Class $className setting in config/bootstrap.php not found\r\n";
|
||||
echo $log;
|
||||
Log::error($log);
|
||||
continue;
|
||||
}
|
||||
/** @var Bootstrap $className */
|
||||
$className::start($worker);
|
||||
}
|
||||
|
||||
foreach (config('plugin', []) as $firm => $projects) {
|
||||
foreach ($projects as $name => $project) {
|
||||
if (!is_array($project)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($project['bootstrap'] ?? [] as $className) {
|
||||
if (!class_exists($className)) {
|
||||
$log = "Warning: Class $className setting in config/plugin/$firm/$name/bootstrap.php not found\r\n";
|
||||
echo $log;
|
||||
Log::error($log);
|
||||
continue;
|
||||
}
|
||||
/** @var Bootstrap $className */
|
||||
$className::start($worker);
|
||||
}
|
||||
}
|
||||
foreach ($projects['bootstrap'] ?? [] as $className) {
|
||||
/** @var string $className */
|
||||
if (!class_exists($className)) {
|
||||
$log = "Warning: Class $className setting in plugin/$firm/config/bootstrap.php not found\r\n";
|
||||
echo $log;
|
||||
Log::error($log);
|
||||
continue;
|
||||
}
|
||||
/** @var Bootstrap $className */
|
||||
$className::start($worker);
|
||||
}
|
||||
}
|
||||
|
||||
$directory = base_path() . '/plugin';
|
||||
$paths = [config_path()];
|
||||
foreach (Util::scanDir($directory) as $path) {
|
||||
if (is_dir($path = "$path/config")) {
|
||||
$paths[] = $path;
|
||||
}
|
||||
}
|
||||
Route::load($paths);
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
// This file is generated by Webman, please don't modify it.
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
Reference in New Issue
Block a user