Dingoapi
模型注入
app/Http/Kernel.php
protected $routeMiddleware = [
......
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
];
去除data
https://packagist.org/packages/liyu/dingo-serializer-switch
$api->version('v1',
['middleware' => 'serializer:array'],
function ($api) {
});
Laravel API 认证:JWT 认证
- 为什么要用JWT
- JWT 是什么
什么是 jwt ?
JWT 全称叫 JSON Web Token, 是一个非常轻巧的规范。这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息。
安装 jwt-auth
composer require tymon/jwt-auth:1.0.x-dev
发布配置
运行以下命令以发布包配置文件:
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
您现在应该有一个config/jwt.php
文件,允许您配置此包的基础知识。
生成密钥
我已经包含了一个帮助命令来为你生成一个密钥:
php artisan jwt:secret
更新您的用户模型
首先,您需要Tymon\JWTAuth\Contracts\JWTSubject
在 User 模型上实现合同,这需要您实现 2 个方法getJWTIdentifier()
和getJWTCustomClaims()
.
下面的示例应该让您了解它的外观。显然,您应该根据自己的需要进行任何更改。
<?php
namespace App;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable implements JWTSubject
{
use Notifiable;
// Rest omitted for brevity
/**
* 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 [];
}
}
配置身份验证保护
在该config/auth.php
文件中,您需要进行一些更改以配置 Laravel 以使用jwt
防护来支持您的应用程序身份验证。
对文件进行以下更改:
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
...
'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
这里我们告诉api
守卫使用jwt
驱动程序,我们将api
守卫设置为默认值。
我们现在可以使用 Laravel 内置的 Auth 系统,由 jwt-auth 在幕后完成工作!
因为我们用的dingoapi 我们还需要在 config/api.php 里面修改:
'auth' => [
'jwt' => 'Dingo\Api\Auth\Provider\JWT',
],
创建路由文件
<?php
use App\Http\Controllers\Api\UserController;
use App\Http\Controllers\Auth\LoginController;
use App\Http\Controllers\Auth\RegisterController;
$api = app('Dingo\Api\Routing\Router');
$api->version(['v1','v2'], ['middleware' => 'api.throttle', 'limit' => 100, 'expires' => 5],function ($api) {
$api->group(['prefix' => 'auth'], function ($api) {
// 注册
$api->post('register', [RegisterController::class, 'store']);
$api->post('login', [LoginController::class, 'login']);
});
});
创建控制器
php artisan make:controller Auth/LoginController
php artisan make:controller Auth/RegisterController
RegisterController
/**
* 用户注册
*/
public function store(RegisterRequest $request)
{
$user = new User();
$user->name = $request->input('name');
$user->email = $request->input('email');
$user->password = bcrypt($request->input('password'));
if ($request->input('openid')) $user->openid = $request->input('openid');
if ($request->input('avatar')) $user->avatar = $request->input('avatar');
$user->save();
return $this->response->created();
}
RegisterRequest
创建数据注册数据验证
php artisan make:request Auth/RegisterRequest
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => 'required|max:16',
'email' => 'required|email|unique:users',
'password' => 'required|min:6|max:16|confirmed',
'openid' => 'sometimes|required|unique:users,openid'
];
}
public function messages()
{
return [
'name.required' => '昵称 不能为空',
'name.max' => '昵称 不能超过16个字符',
'openid.required' => 'openid 不能为空',
'openid.unique' => 'openid 已绑定其他用户',
];
}
完成登录
LoginController
因为我们用户有禁用的功能 所以我们还需要调整一下
/**
* Get a JWT via given credentials.
*
* @return \Illuminate\Http\JsonResponse
*/
public function login()
{
$credentials = request(['email', 'password']);
if (! $token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
/**
* Get the authenticated User.
*
* @return \Illuminate\Http\JsonResponse
*/
public function me()
{
return response()->json(auth()->user());
}
/**
* Log the user out (Invalidate the token).
*
* @return \Illuminate\Http\JsonResponse
*/
public function logout()
{
auth()->logout();
return response()->json(['message' => 'Successfully logged out']);
}
/**
* Refresh a token.
*
* @return \Illuminate\Http\JsonResponse
*/
public function refresh()
{
return $this->respondWithToken(auth()->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()->factory()->getTTL() * 60
]);
}
/**
* 登录
*/
public function login(LoginRequest $request)
{
$credentials = request(['email', 'password']);
if (!$token = auth('api')->attempt($credentials)) {
return $this->response->errorUnauthorized();
}
// 检查用户状态
$user = auth('api')->user();
if ($user->is_locked == 1) {
return $this->response->errorForbidden('被锁定');
}
return $this->respondWithToken($token);
}
LoginRequest
php artisan make:request Auth/LoginRequest
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'email' => 'required|email',
'password' => 'required|min:6|max:16',
];
}
退出登录
路由
$api->group(['middleware' => 'api.auth'], function ($api) {
$api->post('logout', [LoginController::class, 'logout']);
});
/**
* 退出登录
*/
public function logout()
{
auth('api')->logout();
return $this->response->noContent();
}
刷新TOKEN
// 刷新token
$api->post('refresh', [LoginController::class, 'refresh']);
/**
* 刷新token
*/
public function refresh()
{
return $this->respondWithToken(auth('api')->refresh());
}
辅助函数
自定义全局辅助函数
在 app
目录下新建 helpers.php
文件, 并修改 composer.json
, 加入到自动加载中:
"autoload": {
"psr-4": {
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
},
"files": [
"app/helpers.php"
]
},
刷新自动加载:
$ composer dump-autoload
后台用户列表
创建后台控制器
php artisan make:controller Admin/UserController --api
路由
$api->group(['prefix' => 'admin'], function ($api){
// 当前登录用户详情
$api->get('user', [\App\Http\Controllers\Admin\UserController::class, 'userInfo'])->name('user.info');
// 禁用用户/启用用户
$api->patch('users/{user}/lock', [\App\Http\Controllers\Admin\UserController::class, 'lock'])->name('users.lock');
// 用户管理资源路由
$api->resource('users', \App\Http\Controllers\Admin\UserController::class, [
'only' => ['index', 'show', 'store', 'update']
]);
});
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\BaseController;
use App\Models\User;
use App\Transformers\UserTransformer;
use Illuminate\Http\Request;
class UserController extends BaseController
{
/**
* 用户个人信息详情
*/
public function userInfo()
{
return $this->response->item(auth('api')->user(), new UserTransformer());
}
/**
* 用户列表
*/
public function index(Request $request)
{
$name = $request->input('name');
$email = $request->input('email');
$phone = $request->input('phone');
$users = User::when($name, function ($query) use ($name) {
$query->where('name', 'like', "%$name%");
})
->when($email, function ($query) use ($email) {
$query->where('email', $email);
})
->when($phone, function ($query) use ($phone) {
$query->where('phone', $phone);
})
->orderBy('updated_at', 'desc')
->paginate($request->query('pageSize', 10), ['*'], 'current');
return $this->response->paginator($users, new UserTransformer());
}
/**
* 添加用户
*/
public function store(Request $request)
{
$request->validate([
'name' => 'required|max:16',
'email' => 'required|email|unique:users',
'password' => 'required|min:6',
]);
User::create($request->all());
return $this->response->created();
}
/**
* 更新用户
*/
public function update(Request $request, User $user)
{
$request->validate([
'name' => 'required|max:16',
'email' => 'required|email',
]);
if ($user->id == 1 || $user->id == 2) return $this->response->errorBadRequest('禁止操作系统数据');
$user->update($request->all());
return $this->response->noContent();
}
/**
* 用户详情
*/
public function show(User $user)
{
return $this->response->item($user, new UserTransformer());
}
/**
* 禁用和启用用户
*/
public function lock(User $user)
{
if ($user->id == 1 || $user->id == 2) return $this->response->errorBadRequest('禁止操作系统数据');
$user->is_locked = $user->is_locked == 0 ? 1 : 0;
$user->save();
return $this->response->noContent();
}
}
Transformers
<?php
namespace App\Transformers;
use App\Models\User;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
public function transform(User $user)
{
return [
'id' => $user->id,
'name' => $user->name,
'email' => $user->email,
'phone' => $user->phohe,
'avatar' => $user->avatar,
'openid' => $user->openid,
];
}
}
分类管理
/**
* 分类管理
*/
// 分类禁用和启用
$api->patch('category/{category}/status', [\App\Http\Controllers\Admin\CategoryController::class, 'status'])->name('category.status');
// 分类管理资源路由
$api->resource('category', \App\Http\Controllers\Admin\CategoryController::class, [
'except' => ['destroy']
]);
控制器
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\BaseController;
use App\Models\Category;
use Illuminate\Http\Request;
class CategoryController extends BaseController
{
/**
* 分类列表
*/
public function index(Request $request)
{
$type = $request->input('type');
if ($type == 'all') {
return cache_category_all();
} else {
return cache_category();
}
}
/**
* 添加分类 最大2级分类
*/
public function store(Request $request)
{
$insertData = $this->checkInput($request);
if (!is_array($insertData)) return $insertData;
Category::create($insertData);
return $this->response->created();
}
/**
* 分类详情
*/
public function show(Category $category)
{
return $category;
}
/**
* 更新分类
*/
public function update(Request $request, Category $category)
{
if ($category->id < 42) return $this->response->errorBadRequest('系统数据禁止编辑, 请自行创建数据');
$updateData = $this->checkInput($request);
if (!is_array($updateData)) return $updateData;
$category->update($updateData);
return $this->response->noContent();
}
/**
* 验证提交的参数
*/
protected function checkInput($request)
{
// 验证参数
$request->validate([
'name' => 'required|max:16'
], [
'name.required' => '分类名称 不能为空'
]);
// 获取分组
$group = $request->input('group', 'goods');
// 获取pid
$pid = $request->input('pid', 0);
// 计算level
$level = $pid == 0 ? 1 : (Category::find($pid)->level + 1);
// 不能超过3级分类
if ($level > 2) {
return $this->response->errorBadRequest('不能超过二级分类');
}
return [
'name' => $request->input('name'),
'pid' => $pid,
'level' => $level,
'group' => $group
];
}
/**
* 状态禁用和启用
*/
public function status(Category $category)
{
if ($category->id < 42) return $this->response->errorBadRequest('系统数据禁止编辑, 请自行创建数据');
$category->status = $category->status == 1 ? 0 : 1;
$category->save();
return $this->response->noContent();
}
}
Helpers.php
/**
* 缓存没被禁用的分类
*/
if (!function_exists('cache_category')) {
function cache_category ()
{
return cache()->rememberForever('cache_category', function () {
return categoryTree('goods', 1);
});
}
}
/**
* 缓存所有的分类
*/
if (!function_exists('cache_category_all')) {
function cache_category_all ()
{
return cache()->rememberForever('cache_category_all', function () {
return categoryTree('goods');
});
}
}
/**
* 清空分类缓存
*/
if (!function_exists('forget_cache_category')) {
function forget_cache_category ()
{
cache()->forget('cache_category');
cache()->forget('cache_category_all');
cache()->forget('cache_category_menu');
cache()->forget('cache_category_menu_all');
}
}
class CategoryTransformer extends TransformerAbstract
{
public function transform(Category $category)
{
return [
'id' => $category->id,
'pid' => $category->pid,
'name' => $category->name,
];
}
}