这里写目录标题
一、服务器环境:
php:7.3.11
composer:2.0.8
mysql:5.7.27
redis:5.0.5
安装国内的 Composer 加速镜像
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
composer升级到2.0
composer selfupdate -vvv
composer -V
二、安装
1.安装laravel
composer create-project --prefer-dist laravel/laravel /www/wwwroot/laravel-swoole.xxx.com -vvv
2.nginx开启伪静态
location / {
try_files $uri $uri/ /index.php?$query_string;
}
3.安装 predis、JWT
composer require predis/predis
composer require tymon/jwt-auth
#生成 JWT_SECRET 写入.env(自动写入)
php artisan jwt:secret
修改.env
REDIS_CLIENT=predis
4.配置文件 config/app.php
//'providers'数组中添加如下代码
'providers'=>[
...
Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
],
//在'aliases'数组中给 JWT 以下两个类添加别名方便之后生成 token 时使用,(当然也可以使用 Auth 门面生成 token , 所以不添加也是可以的。)
'aliases' => [
...
'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class,
5. JWT 的配置文件
1)发布配置文件
#生成 JWT 的 jwt.php 配置文件 实际是 vendor/tymon/jwt-auth/config/config.php 这个文件copy
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
2)修改配置文件
//把 jwt.php 的 'providers'数组中的 token 生成类(Lcobucci)修改为 Namshi 代码如下:
'providers' => [
//'jwt' => Tymon\JWTAuth\Providers\JWT\Lcobucci::class, // 使用 attempt() 方法生成 token
'jwt' => Tymon\JWTAuth\Providers\JWT\Namshi::class, //使用 formUser() 方法生成 token
3)修改 config/auth.php 配置文件
//把 'defaults' 数组中的默认守卫改为'api'
'defaults' => [
// 'guard' => 'web',
'guard' => 'api',
'passwords' => 'users',
],
//把'guards' 数组中的 api 守卫的驱动改为'jwt'
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
// 'driver' => 'token',
'driver' => 'jwt',
'provider' => 'users',
'hash' => false,
],
],
6.新增phone字段
1)修改database/migrations/da2014_10_12_000000_create_users_table.php
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name')->nullable();
$table->char('phone', 11)->unique();
$table->string('email')->nullable();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->text('last_token')->nullable();
$table->rememberToken();
$table->timestamps();
});
}
2)修改 Models/User.php 如下:
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Tymon\JWTAuth\Contracts\JWTSubject;
class User extends Authenticatable implements JWTSubject
{
use HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name',
'phone', //注意新增phone 字段
'email',
'password',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
//实现 JWTSubject接口 两个函数
public function getJWTIdentifier(){
return $this->getKey();
}
public function getJWTCustomClaims(){
return [];
}
}
8.配置数据库执行数据迁移
php artisan migrate
9.注册、登录
1)新建统一响应数据文件
①新建app\Http\Helpers\ApiResponse.php
代码如下:
<?php
namespace App\Http\Helpers;
use Symfony\Component\HttpFoundation\Response as FoundationResponse;
trait ApiResponse
{
/**
* @var int
*/
protected $statusCode = FoundationResponse::HTTP_OK;
protected $token = '';
/**
* @return mixed
*/
public function getStatusCode()
{
return $this->statusCode;
}
/**
* @param $statusCode
* @return $this
*/
public function setStatusCode($statusCode)
{
$this->statusCode = $statusCode;
return $this;
}
/**
* @param $token
* @return $this
*/
public function setToken($token)
{
$this->token = $token;
return $this;
}
/**
* @param $data
* @return \Illuminate\Http\JsonResponse
*/
public function respond($data)
{
$response = response()->json($data, $this->getStatusCode());
if ($this->token) {
$response->headers->set('Authorization', 'Bearer ' . $this->token);
}
return $response;
}
/**
* @param $status
* @param array $data
* @param null $code
* @return mixed
*/
public function status($status, array $data, $code = null)
{
if ($code) {
$this->setStatusCode($code);
}
$status = [
'status' => $status,
'code' => $this->statusCode
];
$data = array_merge($status, $data);
return $this->respond($data);
}
/**
* @param $message
* @param int $code
* @param string $status
* @return mixed
*/
/*
* 格式
* data:
* code:422
* message:xxx
* status:'error'
*/
public function failed($message, $code = FoundationResponse::HTTP_BAD_REQUEST, $status = 'error')
{
return $this->setStatusCode($code)->message($message, $status);
}
/**
* @param $message
* @param string $status
* @return mixed
*/
public function message($message, $status = "success")
{
if(!is_array($message)) {
$message = [$message];
}
return $this->status($status, [
'message' => $message
]);
}
/**
* @param string $message
* @return mixed
*/
public function internalError($message = "Internal Error!")
{
return $this->failed($message, FoundationResponse::HTTP_INTERNAL_SERVER_ERROR);
}
/**
* @param string $message
* @return mixed
*/
public function created($message = "created")
{
return $this->setStatusCode(FoundationResponse::HTTP_CREATED)
->message($message);
}
/**
* @param $data
* @param string $status
* @return mixed
*/
public function success($data, $status = "success")
{
return $this->status($status, compact('data'));
}
/**
* @param string $message
* @return mixed
*/
public function notFond($message = 'Not Fond!')
{
return $this->failed($message, Foundationresponse::HTTP_NOT_FOUND);
}
}
②新建app\Http\Helpers\ExceptionReport.php
代码如下:
<?php
namespace App\Http\Helpers;
use ErrorException;
use Exception;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Database\QueryException;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
class ExceptionReport
{
use ApiResponse;
/**
* @var Exception
*/
public $exception;
/**
* @var Request
*/
public $request;
/**
* @var
*/
protected $report;
/**
* ExceptionReport constructor.
* @param Request $request
* @param Exception $exception
*/
function __construct(Request $request, Exception $exception)
{
$this->request = $request;
$this->exception = $exception;
}
/**
* @var array
*/
//当抛出这些异常时,可以使用我们定义的错误信息与HTTP状态码
//可以把常见异常放在这里
public $doReport = [
AuthenticationException::class => ['未登录或登录状态失效', 401],
ModelNotFoundException::class => ['该模型未找到', 404],
AuthorizationException::class => ['没有此权限', 403],
ValidationException::class => [],
UnauthorizedHttpException::class => ['未登录或登录状态失效', 401],
TokenInvalidException::class => ['未登录或登录状态失效', 401],
NotFoundHttpException::class => ['没有找到该页面', 404],
MethodNotAllowedHttpException::class => ['访问方式不正确', 405],
ErrorException::class => ['服务器内部错误', 500],
QueryException::class => ['参数错误', 400],
];
public function register($className, callable $callback)
{
$this->doReport[$className] = $callback;
}
/**
* @return bool
*/
public function shouldReturn()
{
foreach (array_keys($this->doReport) as $report) {
if ($this->exception instanceof $report) {
$this->report = $report;
return true;
}
}
return false;
}
/**
* @param Exception $e
* @return static
*/
public static function make(Exception $e)
{
return new static(\request(), $e);
}
/**
* @return mixed
*/
public function report()
{
if ($this->exception instanceof ValidationException) {
return $this->failed(current($this->exception->errors()), $this->exception->status);
}
$message = $this->doReport[$this->report];
return $this->failed($message[0], $message[1]);
}
public function prodReport()
{
return $this->failed('服务器错误', '500');
}
}
③修改App\Exceptions\Handler.php
<?php
namespace App\Exceptions;
use App\Http\Helpers\ExceptionReport;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that are not reported.
*
* @var array
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* @var array
*/
protected $dontFlash = [
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*
* @return void
*/
public function register()
{
$this->reportable(function (Throwable $e) {
//
});
}
public function render($request, Throwable $exception)
{
if($request->is("api/*")){
$reporter = ExceptionReport::make($exception);
if ($reporter->shouldReturn()){
return $reporter->report();
}
if(env('APP_DEBUG')){
//开发环境,则显示详细错误信息
return parent::render($request, $exception);
}else{
//线上环境,未知错误,则显示500
return $reporter->prodReport();
}
}
return parent::render($request, $exception);
}
}
2)新建基本控制
新建app\Http\Controllers\Api\Controller.php
php artisan make:controller Api\Controller
代码如下
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller as BaseController;
use App\Http\Helpers\ApiResponse;
class Controller extends BaseController
{
use ApiResponse;
}
3)新建控制器
①新建app\Http\Controllers\Api\AuthController.php
#创建控制器
php artisan make:controller Api\AuthController
代码如下
<?php
namespace App\Http\Controllers\Api;
use App\Http\Requests\Api\AuthRequest;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Facades\JWTAuth;
class AuthController extends Controller
{
/**
* @param AuthRequest $request
* @return mixed
*/
public function login(AuthRequest $request)
{
$user = User::query()->where(['phone' => $request->phone, 'password' => $request->password])->firstOrFail();
$token = Auth::guard('api')->fromUser($user);
if ($token) {
if ($user->last_token) {
try {
JWTAuth::setToken($user->last_token)->invalidate();
} catch (TokenExpiredException $e) {
//因为让一个过期的token再失效,会抛出异常,所以我们捕捉异常,不需要做任何处理
}
}
$user->last_token = $token;
$user->save();
return $this->setToken($token)->success('登录成功');
}
return $this->failed('账号或密码错误', 400);
}
/**
* 返回当前登录用户信息
* @return mixed
*/
public function info()
{
$user = Auth::guard('api')->user();
return $this->success($user);
}
/**
* 注册接口
* @param AuthRequest $request
* @return mixed
*/
public function register(AuthRequest $request)
{
$member = [
'password' => $request->password,
'phone' => $request->phone,
];
if(User::query()->create($member)) {
return $this->success('用户注册成功');
} else {
return $this->failed('用户注册失败');
}
}
}
②新建app\Http\Requests\Api\AuthRequest.php
#创建表单请求类
php artisan make:request AuthRequest
代码如下
<?php
namespace App\Http\Requests\Api;
class AuthRequest extends FormRequest
{
public function rules()
{
switch ($this->method()) {
case 'POST':
{
$return['password'] = ['bail', 'required', 'max:16', 'min:6'];
if(strpos($this->route()->uri, 'login')) {
$return['phone'] = ['required', 'numeric', 'regex:/^1[3456789][0-9]{9}$/'];
} else {
$return['phone'] = ['required', 'numeric', 'regex:/^1[3456789][0-9]{9}$/', 'unique:Users,phone'];
}
return $return;
default:
{
return [
];
}
}
}
public function messages()
{
return [
'phone.required' => '手机号不能为空',
'phone.numeric' => '手机号错误',
'phone.regex' => '手机号错误',
'phone.unique' => '手机号已被注册',
'password.required' => '密码不能为空',
'password.max' => '密码长度不能超过16个字符',
'password.min' => '密码长度不能小于6个字符'
];
}
}
4)修改app\Http\Middleware\Authenticate.php
注释下面方法
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* @param \Illuminate\Http\Request $request
* @return string|null
*/
// protected function redirectTo($request)
// {
// if (! $request->expectsJson()) {
// return route('login');
// }
// }
}
4)修改路由
修改routes/api.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\AuthController;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
// Route::middleware('auth:api')->get('/user', function (Request $request) {
// return $request->user();
// });
Route::namespace('Api')->group(function () {
Route::prefix('auth/')->group(function () {
//用户注册
Route::post('register', [AuthController::class, 'register']);
Route::post('login', [AuthController::class, 'login']);
Route::middleware('auth:api')->group(function () {
//当前用户信息
Route::get('info', [AuthController::class, 'info']);
});
});
});
10.解决请求跨域问题
在最新版本的laravel(8.*)框架中已经提供了解决请求跨域的CORS扩展包( 我这里使用: "fruitcake/laravel-cors": "^2.0" )
1) 在 config/app.php 的 providers 中添加 CORS 服务提供者
'providers' => [
...
Fruitcake\Cors\CorsServiceProvider::class,
]
2) 在 app/Http/Kernel.php 添加 cors 路由中间件:
#在以下记录再去添加 cors 路由中间件
protected $middleware = [
...
\Fruitcake\Cors\HandleCors::class,
]
protected $routeMiddleware = [
...
'cors' => \Fruitcake\Cors\HandleCors::class,
];
3) 在 routes/api.php 中添加路由 cors 中间件:
Route::namespace('Api')->middleware('cors')->group(function () {
Route::prefix('auth/')->group(function () {
//用户注册
Route::post('register', [AuthController::class, 'register']);
Route::post('login', [AuthController::class, 'login']);
Route::middleware('auth:api')->group(function () {
//当前用户信息
Route::get('info', [AuthController::class, 'info']);
});
});
});
4) 在 config/cors.php 配置跨域相关信息:
<?php
return [
/*
|--------------------------------------------------------------------------
| Cross-Origin Resource Sharing (CORS) Configuration
|--------------------------------------------------------------------------
|
| Here you may configure your settings for cross-origin resource sharing
| or "CORS". This determines what cross-origin operations may execute
| in web browsers. You are free to adjust these settings as needed.
|
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
*/
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => false,
];
11.加入git
git init .
git add .
git commit -m "初始化"
git remote add origin git@gitee.com:***.git
git push -u origin master
git忽略文件权限
git config core.filemode false // 当前版本库
git config --global core.fileMode false // 所有版本库