Add jwt and admin token to build API.

Add jwt to build API.

返回当前客户

后台添加,通过token获取后台用户信息

fixed token api

fixed order list and order detail

fixed product api

fixed categoryies API

add brands

fixed brands

add products

添加订单接口
This commit is contained in:
Edward Yang 2023-04-10 14:54:50 +08:00
parent 67d1b85952
commit c1d6c48f5b
24 changed files with 1577 additions and 18 deletions

View File

@ -64,11 +64,18 @@ class Kernel extends HttpKernel
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'admin_api' => [
\App\Http\Middleware\AdminApiAuthenticate::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
/**

View File

@ -0,0 +1,99 @@
<?php
/**
* AdminAuthenticate.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2023-04-20 14:44:54
* @modified 2023-04-20 14:44:54
*/
namespace App\Http\Middleware;
use Beike\Repositories\AdminUserTokenRepo;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Validation\UnauthorizedException;
class AdminApiAuthenticate
{
public const ADMIN_API_PREFIX = 'admin_api.';
/**
* Handle an incoming request.
*
* @param Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, \Closure $next)
{
$token = $request->header('token');
if (empty($token)) {
$token = $request->get('token');
}
$token = AdminUserTokenRepo::getAdminUserTokenByToken($token);
if (empty($token)) {
throw new UnauthorizedException(trans('customer.unauthorized_without_token'));
}
$adminUser = $token->adminUser;
if (! $this->hasPermission($adminUser)) {
throw new UnauthorizedException(trans('customer.unauthorized_without_permission'));
}
register('admin_user', $adminUser);
return $next($request);
}
private function hasPermission($adminUser)
{
// $routeUri = Route::current()->uri();
$routeName = Route::currentRouteName();
$routePath = str_replace(self::ADMIN_API_PREFIX, '', $routeName);
if ($routePath == 'me') {
return true;
}
$permissionName = $this->mapPermissionByRoute($routePath);
if (empty($permissionName)) {
return false;
}
return $adminUser->can($permissionName);
}
private function mapPermissionByRoute($routePath)
{
$maps = [
'categories.index' => 'categories_index',
'categories.show' => 'categories_show',
'categories.create' => 'categories_create',
'categories.update' => 'categories_update',
'categories.delete' => 'categories_delete',
'brands.index' => 'brands_index',
'brands.show' => 'brands_show',
'brands.create' => 'brands_create',
'brands.update' => 'brands_update',
'brands.delete' => 'brands_delete',
'orders.index' => 'orders_index',
'orders.show' => 'orders_show',
'orders.update_status' => 'orders_update_status',
'orders.update_shipment' => 'orders_update_status',
'products.index' => 'products_index',
'products.show' => 'products_show',
'products.create' => 'products_create',
'products.update' => 'products_update',
'products.delete' => 'products_delete',
];
return $maps[$routePath] ?? '';
}
}

View File

@ -0,0 +1,100 @@
<?php
/**
* BrandController.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2023-04-20 17:19:51
* @modified 2023-04-20 17:19:51
*/
namespace Beike\API\Controllers\Admin;
use Beike\Models\Brand;
use Beike\Repositories\BrandRepo;
use Illuminate\Http\Request;
class BrandController
{
/**
* 显示品牌列表
* @param Request $request
* @return mixed
*/
public function index(Request $request)
{
$brands = BrandRepo::list($request->only('name', 'first', 'status'));
$data = [
'brands' => $brands,
];
return hook_filter('admin_api.brand.index.data', $data);
}
/**
* 创建品牌
*
* @param Request $request
* @param Brand $brand
* @return Brand
*/
public function show(Request $request, Brand $brand): Brand
{
return hook_filter('admin_api.brand.show.data', $brand);
}
/**
* 创建品牌
*
* @param Request $request
* @return array
*/
public function store(Request $request): array
{
$requestData = $request->all();
$data = [
'request_data' => $requestData,
];
hook_action('admin_api.brand.store.before', $data);
$brand = BrandRepo::create($requestData);
hook_action('admin_api.brand.store.after', ['brand' => $brand, 'request_data' => $requestData]);
return json_success(trans('common.created_success'), $brand);
}
/**
* @param Request $request
* @param Brand $brand
* @return array
* @throws \Exception
*/
public function update(Request $request, Brand $brand): array
{
$requestData = $request->all();
$data = [
'brand_id' => $brand,
'request_data' => $requestData,
];
hook_action('admin_api.brand.update.before', $data);
$brand = BrandRepo::update($brand, $requestData);
hook_action('admin_api.brand.update.after', $data);
return json_success(trans('common.updated_success'), $brand);
}
/**
* @param Request $request
* @param Brand $brand
* @return array
*/
public function destroy(Request $request, Brand $brand): array
{
hook_action('admin_api.brand.destroy.before', $brand);
BrandRepo::delete($brand);
hook_action('admin_api.brand.destroy.after', $brand);
return json_success(trans('common.deleted_success'));
}
}

View File

@ -0,0 +1,112 @@
<?php
/**
* CategoryController.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2023-04-20 17:00:40
* @modified 2023-04-20 17:00:40
*/
namespace Beike\API\Controllers\Admin;
use Beike\Admin\Http\Requests\CategoryRequest;
use Beike\Admin\Http\Resources\CategoryResource;
use Beike\Admin\Services\CategoryService;
use Beike\Models\Category;
use Beike\Repositories\CategoryRepo;
use Illuminate\Http\Request;
class CategoryController
{
/**
* 商品分类列表
*
* @return mixed
*/
public function index()
{
$categories = CategoryRepo::getAdminList();
$data = [
'categories' => CategoryResource::collection($categories),
];
return hook_filter('admin_api.category.index.data', $data);
}
/**
* 单个商品分类
*
* @param Request $request
* @param Category $category
* @return mixed
*/
public function show(Request $request, Category $category)
{
if (! $category->active) {
return [];
}
$category->load('description');
return hook_filter('admin_api.category.show.data', $category);
}
/**
* 保存商品分类
*
* @param CategoryRequest $request
* @return array
* @throws \Exception
*/
public function store(CategoryRequest $request)
{
$requestData = $request->all();
$category = (new CategoryService())->createOrUpdate($requestData, null);
$data = [
'category' => $category,
'request_data' => $requestData,
];
hook_action('admin_api.category.save.after', $data);
return $data;
}
/**
* 更新产品分类
*
* @param CategoryRequest $request
* @param Category $category
* @return array
* @throws \Exception
*/
public function update(CategoryRequest $request, Category $category)
{
$requestData = $request->all();
$category = (new CategoryService())->createOrUpdate($requestData, $category);
$data = [
'category' => $category,
'request_data' => $requestData,
];
hook_action('admin_api.category.save.after', $data);
return $data;
}
/**
* 删除分类
* @param Request $request
* @param Category $category
* @return array
* @throws \Exception
*/
public function destroy(Request $request, Category $category): array
{
CategoryRepo::delete($category);
hook_action('admin.category.destroy.after', $category);
return json_success(trans('common.deleted_success'));
}
}

View File

@ -0,0 +1,95 @@
<?php
/**
* OrderController.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2023-04-20 16:40:35
* @modified 2023-04-20 16:40:35
*/
namespace Beike\API\Controllers\Admin;
use Beike\Models\Order;
use Beike\Models\OrderShipment;
use Beike\Repositories\OrderRepo;
use Beike\Services\ShipmentService;
use Beike\Services\StateMachineService;
use Illuminate\Http\Request;
class OrderController
{
/**
* 获取订单列表
*
* @param Request $request
* @return mixed
* @throws \Exception
*/
public function index(Request $request)
{
$orders = OrderRepo::filterOrders($request->all());
return hook_filter('admin_api.order.index.data', $orders);
}
/**
* 查看单个订单
*
* @param Request $request
* @param Order $order
* @return mixed
* @throws \Exception
*/
public function show(Request $request, Order $order)
{
$order->load(['orderTotals', 'orderHistories', 'orderShipments']);
$data = hook_filter('admin.order.show.data', ['order' => $order, 'html_items' => []]);
$data['statuses'] = StateMachineService::getInstance($order)->nextBackendStatuses();
return hook_filter('admin_api.order.show.data', $data);
}
/**
* 更新订单状态,添加订单更新日志
*
* @param Request $request
* @param Order $order
* @return array
* @throws \Throwable
*/
public function updateStatus(Request $request, Order $order)
{
$status = $request->get('status');
$comment = $request->get('comment');
$notify = $request->get('notify');
$shipment = ShipmentService::handleShipment(\request('express_code'), \request('express_number'));
$stateMachine = new StateMachineService($order);
$stateMachine->setShipment($shipment)->changeStatus($status, $comment, $notify);
$orderStatusData = $request->all();
hook_action('admin.order.update_status.after', $orderStatusData);
return json_success(trans('common.updated_success'));
}
/**
* 更新发货信息
*/
public function updateShipment(Request $request, Order $order, int $orderShipmentId): array
{
$data = $request->all();
$orderShipment = OrderShipment::query()->where('order_id', $order->id)->findOrFail($orderShipmentId);
ShipmentService::updateShipment($orderShipment, $data);
hook_action('admin.order.update_shipment.after', [
'request_data' => $data,
'shipment' => $orderShipment,
]);
return json_success(trans('common.updated_success'));
}
}

View File

@ -0,0 +1,125 @@
<?php
/**
* ProductController.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2023-04-20 16:48:47
* @modified 2023-04-20 16:48:47
*/
namespace Beike\API\Controllers\Admin;
use Beike\Admin\Http\Requests\ProductRequest;
use Beike\Admin\Http\Resources\ProductResource;
use Beike\Admin\Services\ProductService;
use Beike\Models\Product;
use Beike\Repositories\ProductRepo;
use Beike\Shop\Http\Resources\ProductDetail;
use Illuminate\Http\Request;
class ProductController
{
/**
* 产品列表
*
* @param Request $request
* @return mixed
*/
public function index(Request $request)
{
$requestData = $request->all();
if (! isset($requestData['sort'])) {
$requestData['sort'] = 'products.updated_at';
}
$productList = ProductRepo::list($requestData);
$products = ProductResource::collection($productList);
return hook_filter('admin_api.product.index.data', $products);
}
/**
* 产品列表
*
* @param Request $request
* @param Product $product
* @return mixed
*/
public function show(Request $request, Product $product)
{
$relationIds = $product->relations->pluck('id')->toArray();
$product = ProductRepo::getProductDetail($product);
$data = [
'product' => (new ProductDetail($product))->jsonSerialize(),
'relations' => ProductRepo::getProductsByIds($relationIds)->jsonSerialize(),
];
return hook_filter('admin_api.product.show.data', $data);
}
/**
* 创建商品
*
* @param ProductRequest $request
* @return array
*/
public function store(ProductRequest $request)
{
try {
$requestData = $request->all();
$product = (new ProductService)->create($requestData);
$data = [
'request_data' => $requestData,
'product' => $product,
];
hook_action('admin_api.product.store.after', $data);
return json_success(trans('common.created_success'));
} catch (\Exception $e) {
return json_fail($e->getMessage());
}
}
/**
* 编辑商品
*
* @param ProductRequest $request
* @param Product $product
* @return array
*/
public function update(ProductRequest $request, Product $product)
{
try {
$requestData = $request->all();
$product = (new ProductService)->update($product, $requestData);
$data = [
'request_data' => $requestData,
'product' => $product,
];
hook_action('admin_api.product.update.after', $data);
return json_success(trans('common.updated_success'));
} catch (\Exception $e) {
return json_fail($e->getMessage());
}
}
/**
* 删除商品
*
* @param Request $request
* @param Product $product
* @return array
*/
public function destroy(Request $request, Product $product)
{
$product->delete();
hook_action('admin_api.product.destroy.after', $product);
return json_success(trans('common.deleted_success'));
}
}

View File

@ -0,0 +1,20 @@
<?php
/**
* UserController.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2023-04-20 14:51:26
* @modified 2023-04-20 14:51:26
*/
namespace Beike\API\Controllers\Admin;
class UserController
{
public function me()
{
return registry('admin_user');
}
}

View File

@ -0,0 +1,94 @@
<?php
/**
* AuthController.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2023-04-11 17:44:26
* @modified 2023-04-11 17:44:26
*/
namespace Beike\API\Controllers;
use App\Http\Controllers\Controller;
use Beike\Shop\Http\Resources\CustomerResource;
class AuthController extends Controller
{
/**
* Create a new AuthController instance.
*
* @return void
*/
public function __construct()
{
// $this->middleware('auth:api', ['except' => ['login']]);
}
/**
* Get a JWT via given credentials.
*
* @return \Illuminate\Http\JsonResponse
*/
public function login()
{
$credentials = request(['email', 'password']);
if (! $token = auth('api_customer')->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
/**
* Get the authenticated User.
*
* @return \Illuminate\Http\JsonResponse
*/
public function me()
{
$customer = auth('api_customer')->user();
return response()->json(new CustomerResource($customer));
}
/**
* Log the user out (Invalidate the token).
*
* @return \Illuminate\Http\JsonResponse
*/
public function logout()
{
auth('api_customer')->logout();
return response()->json(['message' => 'Successfully logged out']);
}
/**
* Refresh a token.
*
* @return \Illuminate\Http\JsonResponse
*/
public function refresh()
{
return $this->respondWithToken(auth('api_customer')->refresh());
}
/**
* Get the token array structure.
*
* @param string $token
*
* @return \Illuminate\Http\JsonResponse
*/
protected function respondWithToken($token)
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth('api_customer')->factory()->getTTL() * 60,
]);
}
}

View File

@ -0,0 +1,38 @@
<?php
/**
* APIServiceProvider.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2023-04-11 16:08:04
* @modified 2023-04-11 16:08:04
*/
namespace Beike\API\Providers;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\ServiceProvider;
class APIServiceProvider extends ServiceProvider
{
public function boot()
{
if (is_installer()) {
return;
}
$this->loadRoutesFrom(__DIR__ . '/../Routes/api.php');
$this->registerGuard();
}
/**
* 注册后台用户 guard
*/
protected function registerGuard()
{
Config::set('auth.guards.api_customer', [
'driver' => 'jwt',
'provider' => 'shop_customer',
]);
}
}

52
beike/API/Routes/api.php Normal file
View File

@ -0,0 +1,52 @@
<?php
/**
* api.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2023-04-11 17:36:05
* @modified 2023-04-11 17:36:05
*/
use Illuminate\Support\Facades\Route;
Route::prefix('api')
->middleware(['api'])
->name('api.')
->group(function () {
Route::post('login', [\Beike\API\Controllers\AuthController::class, 'login']);
Route::post('logout', [\Beike\API\Controllers\AuthController::class, 'logout']);
Route::post('refresh', [\Beike\API\Controllers\AuthController::class, 'refresh']);
Route::get('me', [\Beike\API\Controllers\AuthController::class, 'me']);
});
Route::prefix('admin_api')
->middleware(['admin_api'])
->name('admin_api.')
->group(function () {
Route::get('brands', [\Beike\API\Controllers\Admin\BrandController::class, 'index'])->name('brands.index');
Route::get('brands/{brand}', [\Beike\API\Controllers\Admin\BrandController::class, 'show'])->name('brands.show');
Route::post('brands', [\Beike\API\Controllers\Admin\BrandController::class, 'store'])->name('brands.create');
Route::put('brands/{brand}', [\Beike\API\Controllers\Admin\BrandController::class, 'update'])->name('brands.update');
Route::delete('brands/{brand}', [\Beike\API\Controllers\Admin\BrandController::class, 'destroy'])->name('brands.delete');
Route::get('categories', [\Beike\API\Controllers\Admin\CategoryController::class, 'index'])->name('categories.index');
Route::get('categories/{category}', [\Beike\API\Controllers\Admin\CategoryController::class, 'show'])->name('categories.show');
Route::post('categories', [\Beike\API\Controllers\Admin\CategoryController::class, 'store'])->name('categories.create');
Route::put('categories/{category}', [\Beike\API\Controllers\Admin\CategoryController::class, 'update'])->name('categories.update');
Route::delete('categories/{category}', [\Beike\API\Controllers\Admin\CategoryController::class, 'destroy'])->name('categories.delete');
Route::get('me', [\Beike\API\Controllers\Admin\UserController::class, 'me'])->name('me');
Route::get('orders', [\Beike\API\Controllers\Admin\OrderController::class, 'index'])->name('orders.index');
Route::get('orders/{order}', [\Beike\API\Controllers\Admin\OrderController::class, 'show'])->name('orders.show');
Route::put('orders/{order}/status', [\Beike\API\Controllers\Admin\OrderController::class, 'updateStatus'])->name('orders.update_status');
Route::put('orders/{order}/shipments/{shipment}', [\Beike\API\Controllers\Admin\OrderController::class, 'updateShipment'])->name('orders.update_shipment');
Route::get('products', [\Beike\API\Controllers\Admin\ProductController::class, 'index'])->name('products.index');
Route::get('products/{product}', [\Beike\API\Controllers\Admin\ProductController::class, 'show'])->name('products.show');
Route::post('products', [\Beike\API\Controllers\Admin\ProductController::class, 'store'])->name('products.create');
Route::put('products/{product}', [\Beike\API\Controllers\Admin\ProductController::class, 'update'])->name('products.update');
Route::delete('products/{product}', [\Beike\API\Controllers\Admin\ProductController::class, 'destroy'])->name('products.delete');
});

View File

@ -22,19 +22,19 @@ class AccountController extends Controller
$user = current_user();
$data = [
'current_user' => $user,
'tokens' => AdminUserTokenRepo::getTokenByAdminUser($user)->pluck('token')->toArray()
'tokens' => AdminUserTokenRepo::getTokenByAdminUser($user)->pluck('token')->toArray(),
];
return view('admin::pages.account.index', $data);
}
public function update(Request $request)
{
$user = current_user();
$adminUserData = $request->all();
AdminUserRepo::updateAdminUser($user->id, $adminUserData);
return response()->redirectTo('admin/account')->with('success', trans('common.updated_success'));
}
}

View File

@ -53,6 +53,7 @@ class AdminServiceProvider extends ServiceProvider
load_settings();
$this->loadRoutesFrom(__DIR__ . '/../Routes/admin.php');
$this->registerGuard();
$adminName = admin_name();
if (! Str::startsWith($uri, "/{$adminName}")) {
@ -69,8 +70,6 @@ class AdminServiceProvider extends ServiceProvider
$this->loadAdminViewComponents();
$this->registerGuard();
Config::set('filesystems.disks.catalog', [
'driver' => 'local',
'root' => public_path('catalog'),

View File

@ -209,7 +209,12 @@ function equal_route($routeName): bool
*/
function current_user(): ?AdminUser
{
return auth()->guard(AdminUser::AUTH_GUARD)->user();
$user = auth()->guard(AdminUser::AUTH_GUARD)->user();
if (empty($user)) {
$user = registry('admin_user');
}
return $user;
}
/**
@ -718,3 +723,23 @@ function perPage(): int
{
return (int) system_setting('base.product_per_page', 20);
}
/**
* @param $key
* @param $value
* @param bool $force
*/
function register($key, $value, bool $force = false)
{
\Beike\Libraries\Registry::set($key, $value, $force);
}
/**
* @param $key
* @param null $default
* @return mixed
*/
function registry($key, $default = null): mixed
{
return \Beike\Libraries\Registry::get($key, $default);
}

View File

@ -0,0 +1,84 @@
<?php
/**
* Registry.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2023-04-20 16:29:54
* @modified 2023-04-20 16:29:54
*/
namespace Beike\Libraries;
class Registry
{
private array $data = [];
private static $registry;
public static function getSingleton(): self
{
if (self::$registry instanceof self) {
return self::$registry;
}
return self::$registry = new self();
}
/**
* @param $key
* @param null $default
* @return mixed
*/
public static function get($key, $default = null): mixed
{
return self::getSingleton()->getValue($key, $default);
}
/**
* @param $key
* @param $value
* @param bool $force
*/
public static function set($key, $value, bool $force = false)
{
if (self::getSingleton()->has($key) && ! $force) {
return;
}
self::getSingleton()->setValue($key, $value);
}
public function destroy()
{
self::$registry = null;
}
/**
* @param $key
* @param null $default
* @return mixed
*/
public function getValue($key, $default = null): mixed
{
return $this->data[$key] ?? $default;
}
/**
* @param $key
* @param $value
*/
public function setValue($key, $value)
{
$this->data[$key] = $value;
}
/**
* @param $key
* @return bool
*/
public function has($key): bool
{
return isset($this->data[$key]);
}
}

View File

@ -8,8 +8,9 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as AuthUser;
use Illuminate\Notifications\Notifiable;
use Spatie\Permission\Traits\HasRoles;
use Tymon\JWTAuth\Contracts\JWTSubject;
class AdminUser extends AuthUser
class AdminUser extends AuthUser implements JWTSubject
{
use HasFactory, HasRoles;
use Notifiable;
@ -32,4 +33,24 @@ class AdminUser extends AuthUser
$this->notifyNow(new AdminForgottenNotification($this, $code));
}
}
/**
* Get the identifier that will be stored in the subject claim of the JWT.
*
* @return mixed
*/
public function getJWTIdentifier()
{
return $this->getKey();
}
/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* @return array
*/
public function getJWTCustomClaims()
{
return [];
}
}

View File

@ -18,10 +18,8 @@ class AdminUserToken extends Model
{
protected $fillable = ['admin_user_id', 'token'];
public function adminUser(): BelongsTo
{
return $this->belongsTo(AdminUser::class);
}
}

View File

@ -11,8 +11,9 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Tymon\JWTAuth\Contracts\JWTSubject;
class Customer extends Authenticatable
class Customer extends Authenticatable implements JWTSubject
{
use HasFactory;
use SoftDeletes;
@ -66,4 +67,24 @@ class Customer extends Authenticatable
$this->notifyNow(new ForgottenNotification($this, $code));
}
}
/**
* Get the identifier that will be stored in the subject claim of the JWT.
*
* @return mixed
*/
public function getJWTIdentifier()
{
return $this->getKey();
}
/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* @return array
*/
public function getJWTCustomClaims()
{
return [];
}
}

View File

@ -16,37 +16,58 @@ use Beike\Models\AdminUserToken;
class AdminUserTokenRepo
{
/**
* @param $adminUser
* @return mixed
*/
public static function getTokenByAdminUser($adminUser)
{
$adminUserId = self::getAdminUserId($adminUser);
if (empty($adminUserId)) {
return null;
}
return AdminUserToken::query()->where('admin_user_id', $adminUserId)->get();
}
/**
* @param $token
* @return mixed
*/
public static function getAdminUserTokenByToken($token)
{
return AdminUserToken::query()->where('token', $token)->first();
}
/**
* @param $adminUser
* @param $tokens
* @return void
*/
public static function updateTokensByUser($adminUser, $tokens)
{
$adminUserId = self::getAdminUserId($adminUser);
if (empty($adminUserId)) {
return null;
return;
}
AdminUserToken::query()->where('admin_user_id', $adminUserId)->delete();
if (empty($tokens)) {
return null;
return;
}
foreach ($tokens as $token) {
AdminUserToken::query()->create([
'admin_user_id' => $adminUserId,
'token' => $token
'token' => $token,
]);
}
}
/**
* @param $adminUser
* @return int|mixed
*/
private static function getAdminUserId($adminUser)
{
$adminUserId = 0;
@ -55,6 +76,7 @@ class AdminUserTokenRepo
} elseif (is_int($adminUser)) {
$adminUserId = $adminUser;
}
return $adminUserId;
}
}

View File

@ -7,8 +7,8 @@
"require": {
"php": "^8.0.2",
"ext-curl": "*",
"ext-iconv": "*",
"ext-fileinfo": "*",
"ext-iconv": "*",
"ext-json": "*",
"ext-pdo": "*",
"ext-simplexml": "*",
@ -26,6 +26,7 @@
"srmklive/paypal": "^3.0",
"stripe/stripe-php": "^8.8",
"tormjens/eventy": "^0.8.0",
"tymon/jwt-auth": "^2.0",
"ultrono/laravel-sitemap": "^9.1",
"unisharp/doc-us": "^1.3",
"zanysoft/laravel-zip": "^2.0"

348
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "36d52cf08b3c0a6bcfc69644de7ff3aa",
"content-hash": "db85c224767389a98090ed325dbdd7ce",
"packages": [
{
"name": "brick/math",
@ -2124,6 +2124,153 @@
},
"time": "2023-02-15T16:40:09+00:00"
},
{
"name": "lcobucci/clock",
"version": "2.2.0",
"source": {
"type": "git",
"url": "https://github.com/lcobucci/clock.git",
"reference": "fb533e093fd61321bfcbac08b131ce805fe183d3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/lcobucci/clock/zipball/fb533e093fd61321bfcbac08b131ce805fe183d3",
"reference": "fb533e093fd61321bfcbac08b131ce805fe183d3",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": "^8.0",
"stella-maris/clock": "^0.1.4"
},
"require-dev": {
"infection/infection": "^0.26",
"lcobucci/coding-standard": "^8.0",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^0.12",
"phpstan/phpstan-deprecation-rules": "^0.12",
"phpstan/phpstan-phpunit": "^0.12",
"phpstan/phpstan-strict-rules": "^0.12",
"phpunit/phpunit": "^9.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Lcobucci\\Clock\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Luís Cobucci",
"email": "lcobucci@gmail.com"
}
],
"description": "Yet another clock abstraction",
"support": {
"issues": "https://github.com/lcobucci/clock/issues",
"source": "https://github.com/lcobucci/clock/tree/2.2.0"
},
"funding": [
{
"url": "https://github.com/lcobucci",
"type": "github"
},
{
"url": "https://www.patreon.com/lcobucci",
"type": "patreon"
}
],
"time": "2022-04-19T19:34:17+00:00"
},
{
"name": "lcobucci/jwt",
"version": "4.3.0",
"source": {
"type": "git",
"url": "https://github.com/lcobucci/jwt.git",
"reference": "4d7de2fe0d51a96418c0d04004986e410e87f6b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/4d7de2fe0d51a96418c0d04004986e410e87f6b4",
"reference": "4d7de2fe0d51a96418c0d04004986e410e87f6b4",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"ext-hash": "*",
"ext-json": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
"ext-sodium": "*",
"lcobucci/clock": "^2.0 || ^3.0",
"php": "^7.4 || ^8.0"
},
"require-dev": {
"infection/infection": "^0.21",
"lcobucci/coding-standard": "^6.0",
"mikey179/vfsstream": "^1.6.7",
"phpbench/phpbench": "^1.2",
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^1.4",
"phpstan/phpstan-deprecation-rules": "^1.0",
"phpstan/phpstan-phpunit": "^1.0",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/php-invoker": "^3.1",
"phpunit/phpunit": "^9.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Lcobucci\\JWT\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Luís Cobucci",
"email": "lcobucci@gmail.com",
"role": "Developer"
}
],
"description": "A simple library to work with JSON Web Token and JSON Web Signature",
"keywords": [
"JWS",
"jwt"
],
"support": {
"issues": "https://github.com/lcobucci/jwt/issues",
"source": "https://github.com/lcobucci/jwt/tree/4.3.0"
},
"funding": [
{
"url": "https://github.com/lcobucci",
"type": "github"
},
{
"url": "https://www.patreon.com/lcobucci",
"type": "patreon"
}
],
"time": "2023-01-02T13:28:00+00:00"
},
{
"name": "league/commonmark",
"version": "2.3.8",
@ -3744,6 +3891,60 @@
},
"time": "2021-02-03T23:26:27+00:00"
},
{
"name": "psr/clock",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/clock.git",
"reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d",
"reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": "^7.0 || ^8.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Psr\\Clock\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for reading the clock.",
"homepage": "https://github.com/php-fig/clock",
"keywords": [
"clock",
"now",
"psr",
"psr-20",
"time"
],
"support": {
"issues": "https://github.com/php-fig/clock/issues",
"source": "https://github.com/php-fig/clock/tree/1.0.0"
},
"time": "2022-11-25T14:36:26+00:00"
},
{
"name": "psr/container",
"version": "2.0.2",
@ -4635,6 +4836,59 @@
},
"time": "2023-02-20T14:35:50+00:00"
},
{
"name": "stella-maris/clock",
"version": "0.1.7",
"source": {
"type": "git",
"url": "https://github.com/stella-maris-solutions/clock.git",
"reference": "fa23ce16019289a18bb3446fdecd45befcdd94f8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/stella-maris-solutions/clock/zipball/fa23ce16019289a18bb3446fdecd45befcdd94f8",
"reference": "fa23ce16019289a18bb3446fdecd45befcdd94f8",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"php": "^7.0|^8.0",
"psr/clock": "^1.0"
},
"type": "library",
"autoload": {
"psr-4": {
"StellaMaris\\Clock\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Andreas Heigl",
"role": "Maintainer"
}
],
"description": "A pre-release of the proposed PSR-20 Clock-Interface",
"homepage": "https://gitlab.com/stella-maris/clock",
"keywords": [
"clock",
"datetime",
"point in time",
"psr20"
],
"support": {
"source": "https://github.com/stella-maris-solutions/clock/tree/0.1.7"
},
"time": "2022-11-25T16:15:06+00:00"
},
{
"name": "stripe/stripe-php",
"version": "v8.12.0",
@ -7253,6 +7507,96 @@
},
"time": "2021-10-22T08:33:10+00:00"
},
{
"name": "tymon/jwt-auth",
"version": "2.0.0",
"source": {
"type": "git",
"url": "https://github.com/tymondesigns/jwt-auth.git",
"reference": "b0868a5b00801889a9e0c81a737963d8004e708c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/tymondesigns/jwt-auth/zipball/b0868a5b00801889a9e0c81a737963d8004e708c",
"reference": "b0868a5b00801889a9e0c81a737963d8004e708c",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"illuminate/auth": "^9.0|^10.0",
"illuminate/contracts": "^9.0|^10.0",
"illuminate/http": "^9.0|^10.0",
"illuminate/support": "^9.0|^10.0",
"lcobucci/jwt": "^4.0",
"nesbot/carbon": "^2.0",
"php": "^8.0"
},
"require-dev": {
"illuminate/console": "^9.0|^10.0",
"illuminate/database": "^9.0|^10.0",
"illuminate/routing": "^9.0|^10.0",
"mockery/mockery": ">=0.9.9",
"phpunit/phpunit": "^9.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-develop": "1.0-dev",
"dev-2.x": "2.0-dev"
},
"laravel": {
"aliases": {
"JWTAuth": "Tymon\\JWTAuth\\Facades\\JWTAuth",
"JWTFactory": "Tymon\\JWTAuth\\Facades\\JWTFactory"
},
"providers": [
"Tymon\\JWTAuth\\Providers\\LaravelServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Tymon\\JWTAuth\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Sean Tymon",
"email": "tymon148@gmail.com",
"homepage": "https://tymon.xyz",
"role": "Developer"
}
],
"description": "JSON Web Token Authentication for Laravel and Lumen",
"homepage": "https://github.com/tymondesigns/jwt-auth",
"keywords": [
"Authentication",
"JSON Web Token",
"auth",
"jwt",
"laravel"
],
"support": {
"issues": "https://github.com/tymondesigns/jwt-auth/issues",
"source": "https://github.com/tymondesigns/jwt-auth"
},
"funding": [
{
"url": "https://www.patreon.com/seantymon",
"type": "patreon"
}
],
"time": "2023-02-16T16:29:41+00:00"
},
{
"name": "ultrono/laravel-sitemap",
"version": "9.3.0",
@ -11175,8 +11519,8 @@
"platform": {
"php": "^8.0.2",
"ext-curl": "*",
"ext-iconv": "*",
"ext-fileinfo": "*",
"ext-iconv": "*",
"ext-json": "*",
"ext-pdo": "*",
"ext-simplexml": "*",

View File

@ -180,6 +180,7 @@ return [
Beike\Shop\Providers\ShopServiceProvider::class,
Beike\Shop\Providers\PluginServiceProvider::class,
Beike\Installer\Providers\InstallerServiceProvider::class,
Beike\API\Providers\APIServiceProvider::class,
Beike\Hook\HookServiceProvider::class,
],

301
config/jwt.php Normal file
View File

@ -0,0 +1,301 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) Sean Tymon <tymon148@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
return [
/*
|--------------------------------------------------------------------------
| JWT Authentication Secret
|--------------------------------------------------------------------------
|
| Don't forget to set this in your .env file, as it will be used to sign
| your tokens. A helper command is provided for this:
| `php artisan jwt:secret`
|
| Note: This will be used for Symmetric algorithms only (HMAC),
| since RSA and ECDSA use a private/public key combo (See below).
|
*/
'secret' => env('JWT_SECRET'),
/*
|--------------------------------------------------------------------------
| JWT Authentication Keys
|--------------------------------------------------------------------------
|
| The algorithm you are using, will determine whether your tokens are
| signed with a random string (defined in `JWT_SECRET`) or using the
| following public & private keys.
|
| Symmetric Algorithms:
| HS256, HS384 & HS512 will use `JWT_SECRET`.
|
| Asymmetric Algorithms:
| RS256, RS384 & RS512 / ES256, ES384 & ES512 will use the keys below.
|
*/
'keys' => [
/*
|--------------------------------------------------------------------------
| Public Key
|--------------------------------------------------------------------------
|
| A path or resource to your public key.
|
| E.g. 'file://path/to/public/key'
|
*/
'public' => env('JWT_PUBLIC_KEY'),
/*
|--------------------------------------------------------------------------
| Private Key
|--------------------------------------------------------------------------
|
| A path or resource to your private key.
|
| E.g. 'file://path/to/private/key'
|
*/
'private' => env('JWT_PRIVATE_KEY'),
/*
|--------------------------------------------------------------------------
| Passphrase
|--------------------------------------------------------------------------
|
| The passphrase for your private key. Can be null if none set.
|
*/
'passphrase' => env('JWT_PASSPHRASE'),
],
/*
|--------------------------------------------------------------------------
| JWT time to live
|--------------------------------------------------------------------------
|
| Specify the length of time (in minutes) that the token will be valid for.
| Defaults to 1 hour.
|
| You can also set this to null, to yield a never expiring token.
| Some people may want this behaviour for e.g. a mobile app.
| This is not particularly recommended, so make sure you have appropriate
| systems in place to revoke the token if necessary.
| Notice: If you set this to null you should remove 'exp' element from 'required_claims' list.
|
*/
'ttl' => env('JWT_TTL', 60),
/*
|--------------------------------------------------------------------------
| Refresh time to live
|--------------------------------------------------------------------------
|
| Specify the length of time (in minutes) that the token can be refreshed
| within. I.E. The user can refresh their token within a 2 week window of
| the original token being created until they must re-authenticate.
| Defaults to 2 weeks.
|
| You can also set this to null, to yield an infinite refresh time.
| Some may want this instead of never expiring tokens for e.g. a mobile app.
| This is not particularly recommended, so make sure you have appropriate
| systems in place to revoke the token if necessary.
|
*/
'refresh_ttl' => env('JWT_REFRESH_TTL', 20160),
/*
|--------------------------------------------------------------------------
| JWT hashing algorithm
|--------------------------------------------------------------------------
|
| Specify the hashing algorithm that will be used to sign the token.
|
*/
'algo' => env('JWT_ALGO', Tymon\JWTAuth\Providers\JWT\Provider::ALGO_HS256),
/*
|--------------------------------------------------------------------------
| Required Claims
|--------------------------------------------------------------------------
|
| Specify the required claims that must exist in any token.
| A TokenInvalidException will be thrown if any of these claims are not
| present in the payload.
|
*/
'required_claims' => [
'iss',
'iat',
'exp',
'nbf',
'sub',
'jti',
],
/*
|--------------------------------------------------------------------------
| Persistent Claims
|--------------------------------------------------------------------------
|
| Specify the claim keys to be persisted when refreshing a token.
| `sub` and `iat` will automatically be persisted, in
| addition to the these claims.
|
| Note: If a claim does not exist then it will be ignored.
|
*/
'persistent_claims' => [
// 'foo',
// 'bar',
],
/*
|--------------------------------------------------------------------------
| Lock Subject
|--------------------------------------------------------------------------
|
| This will determine whether a `prv` claim is automatically added to
| the token. The purpose of this is to ensure that if you have multiple
| authentication models e.g. `App\User` & `App\OtherPerson`, then we
| should prevent one authentication request from impersonating another,
| if 2 tokens happen to have the same id across the 2 different models.
|
| Under specific circumstances, you may want to disable this behaviour
| e.g. if you only have one authentication model, then you would save
| a little on token size.
|
*/
'lock_subject' => true,
/*
|--------------------------------------------------------------------------
| Leeway
|--------------------------------------------------------------------------
|
| This property gives the jwt timestamp claims some "leeway".
| Meaning that if you have any unavoidable slight clock skew on
| any of your servers then this will afford you some level of cushioning.
|
| This applies to the claims `iat`, `nbf` and `exp`.
|
| Specify in seconds - only if you know you need it.
|
*/
'leeway' => env('JWT_LEEWAY', 0),
/*
|--------------------------------------------------------------------------
| Blacklist Enabled
|--------------------------------------------------------------------------
|
| In order to invalidate tokens, you must have the blacklist enabled.
| If you do not want or need this functionality, then set this to false.
|
*/
'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),
/*
| -------------------------------------------------------------------------
| Blacklist Grace Period
| -------------------------------------------------------------------------
|
| When multiple concurrent requests are made with the same JWT,
| it is possible that some of them fail, due to token regeneration
| on every request.
|
| Set grace period in seconds to prevent parallel request failure.
|
*/
'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0),
/*
|--------------------------------------------------------------------------
| Cookies encryption
|--------------------------------------------------------------------------
|
| By default Laravel encrypt cookies for security reason.
| If you decide to not decrypt cookies, you will have to configure Laravel
| to not encrypt your cookie token by adding its name into the $except
| array available in the middleware "EncryptCookies" provided by Laravel.
| see https://laravel.com/docs/master/responses#cookies-and-encryption
| for details.
|
| Set it to true if you want to decrypt cookies.
|
*/
'decrypt_cookies' => false,
/*
|--------------------------------------------------------------------------
| Providers
|--------------------------------------------------------------------------
|
| Specify the various providers used throughout the package.
|
*/
'providers' => [
/*
|--------------------------------------------------------------------------
| JWT Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to create and decode the tokens.
|
*/
'jwt' => Tymon\JWTAuth\Providers\JWT\Lcobucci::class,
/*
|--------------------------------------------------------------------------
| Authentication Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to authenticate users.
|
*/
'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class,
/*
|--------------------------------------------------------------------------
| Storage Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to store tokens in the blacklist.
|
*/
'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class,
],
];

View File

@ -10,6 +10,6 @@
*/
return [
'create_token' => 'Create Token',
'create_token' => 'Create Token',
'password_text' => 'If the password is left blank, it will not be modified',
];

View File

@ -10,6 +10,6 @@
*/
return [
'create_token' => '生成 Token',
'create_token' => '生成 Token',
'password_text' => '密码留空则不修改',
];