Write the Code. Change the World.

分类目录
8月 08

参考链接

https://www.cnblogs.com/xuexianqi/p/12797170.html

按照上班的链接的方法,以及其提供的安装包是可以完美破解使用的。只是有一点需要注意,在使用破解机的时候 patch 一定只点击一次。如果次数多了,在破解的时候就会出现 Rsa Public Key not Find 这样的错误。事情既然已经发生了,那还可以挽救么。当然可以。

下边这篇就是挽救的办法。卸载,清理注册表。从新操作一波。

https://blog.csdn.net/weixin_44452446/article/details/106470981

7月 31

前边写了一个 laravel mix vue 的基本构造使用。虽然那种结构能跑起来,可受限 laravel 自己的功能。这次,要利用 vue 的优势,elemeui 的优势,独立去构建项目。

前边一部分操作是一样的。

从零开始操作

这里以 laravel7 为例子,来使用 vue。

先安装 laravel 7

composer create-project laravel/laravel --prefer-dist testvue.cn "7.*"

cd testvue.cn

# 查看 laravel 的版本(发现是 7.22.4)
php artisan -V

# 创建 git 仓库
git init
git add .
git commit -m 'laravel 7.22.4 initialize'

浏览器访问 testvue.cn, 发现能正常访问到 laravel 基础页面(之前已经配合域名映射这些)。
继续阅读

7月 31

laravel 初始化是不带 ui 这些的,可它天生支持 bootstrap,vue,react 这些。只需要安装配置下即可。

从零开始操作

这里以 laravel7 为例子,来使用 vue。

先安装 laravel 7

composer create-project laravel/laravel --prefer-dist testvue.cn "7.*"

cd testvue.cn

# 查看 laravel 的版本(发现是 7.22.4)
php artisan -V

# 创建 git 仓库
git init
git add .
git commit -m 'laravel 7.22.4 initialize'

浏览器访问 testvue.cn, 发现能正常访问到 laravel 基础页面(之前已经配合域名映射这些)。
继续阅读

7月 30

laravel 写接口的时候,通常会建立 request。在 request 中验证一些参数。这个时候用 postman 或其他请求时候,会发生返回 html 的情况,非 json 格式。该么处理呢。

简单操作一下

TestController.php

public function store(TestRequest $request)
{
    $result = $request->only(['name', 'num']);

    return response()->json($result)->setStatusCode(201);
}

继续阅读

7月 01

一、建表规约

1.【强制】表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint( 1 表示是,0 表示否),此规则同样适用于 odps 建表。
说明:任何字段如果为非负数,必须是 unsigned。

2.【强制】表名、字段名必须使用小写字母或数字;禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
正例:getter_admin,task_config,level3_name
反例:GetterAdmin,taskConfig,level_3_name

3.【强制】表名不使用复数名词。
说明:表名应该仅仅表示表里面的实体内容,不应该表示实体数量,对应于 DO 类名也是单数形式,符合表达习惯。

4.【强制】禁用保留字,如 desc、range、match、delayed 等,请参考 MySQL 官方保留字。
继续阅读

6月 04

在某些时候,为了数据的安全和保密性,需要对来回的数据进行加密处理。如果是 laravel 框架,中间件绝对是个好东西。

一波操作

laravel 中间件包含一个完整的工作流,即请求前中间件(request)和请求后中间件(response)。在我们这次的场景中,刚好很好的用到了整个流程。

操作开始:
继续阅读

2月 09

typescript 是 js 的超集。用了 ant design pro,用了 ts。那先看最首要解决的问题。注册,模板相关的问题。

点点滴滴

1月 31

web 页面,以及后台接口,权限基础架构已经完成。现在再多增点东西。比如发短信,比如模型事件。

先看模型事件,用模型事件一方面是减少耦合,另一方面如果模型多出 Create ,并在 Created 后要做一系列逻辑。这个时候用模型事件就比较好。否则有重复代码,或你用 trait 去处理。反正就是模型事件好。

模型事件文档

# 我们先给 User 用户创建一个观察者
php artisan make:observer UserObserver --model=Models/User

# 创建成功后,删除掉其他的事件,只保留 created 
# 在你希望观察的模型上使用 observe 方法注册观察者。也可以在服务提供者的 boot 方法注册观察者。这里在 AppServiceProvider 中注册观察者。

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Models\User;
use App\Observers\UserObserver;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        User::observe(UserObserver::class);
    }
}

# 这样,当 User created 的时候,就会调用到事件侦听

再来处理短信相关的。

短信提供商有很多,我们选择用阿里云的。因为短信价格都差不多,服务器,域名都在阿里下。所以短信也选择阿里的。这里使用 overtrue 的 easy-sms 来做。

composer require overtrue/easy-sms

vim config/easysms.php

<?php

return [
    // HTTP 请求的超时时间(秒)
    'timeout' => 10.0,

    // 默认发送配置
    'default' => [
        // 网关调用策略,默认:顺序调用
        'strategy' => \Overtrue\EasySms\Strategies\OrderStrategy::class,

        // 默认可用的发送网关
        'gateways' => [
            'aliyun',
        ],
    ],
    // 可用的网关配置
    'gateways' => [
        'errorlog' => [
            'file' => storage_path('logs/easy-sms/easy-sms.log'),
        ],
        'aliyun' => [
            'access_key_id' => env('SMS_ALIYUN_ACCESS_KEY_ID'),
            'access_key_secret' => env('SMS_ALIYUN_ACCESS_KEY_SECRET'),
            'sign_name' => '约拍宝',
            'templates' => [
                'register' => env('SMS_ALIYUN_TEMPLATE_REGISTER'),
            ]
        ],
    ],
];

# 再来创建 serverprovider

php artisan make:provider EasySmsServiceProvider

<?php

namespace App\Providers;

use Overtrue\EasySms\EasySms;
use Illuminate\Support\ServiceProvider;

class EasySmsServiceProvider extends ServiceProvider
{
    public function boot()
    {

    }

    public function register()
    {
        $this->app->singleton(EasySms::class, function ($app) {
            return new EasySms(config('easysms'));
        });

        $this->app->alias(EasySms::class, 'easysms');
    }
}

# 在 config/app.php 中加入 
App\Providers\EasySmsServiceProvider::class,

# 最后,env 中配置好对应的值即可

使用

use Overtrue\EasySms\EasySms;

public function send(EasySms $easySms)
    {
        $phone = 13888888888;
            // 生成4位随机数,左侧补0
        $code = str_pad(random_int(1, 9999), 4, 0, STR_PAD_LEFT);

        try {
            $result = $easySms->send($phone, [
                'template' => config('easysms.gateways.aliyun.templates.register'),
                'data' => [
                    'code' => $code
                ],
            ]);
        } catch (\Overtrue\EasySms\Exceptions\NoGatewayAvailableException $exception) {
            $message = $exception->getException('aliyun')->getMessage();
            abort(500, $message ?: '短信发送异常');
        }
    }

先到这里,真实场景再使用。下一步就是开始后台了。这里使用阿里的 ant design pro。

顺便再说一下。env 中加入的配置,我们先要在 env example
中先加一份。 .env 是不加入版本控制的。掉了就没了。 .env.example 是加入了版本控制,但敏感数据空着就好,空留key即可。

1月 27

完成了 web 的布局以及权限配置,现在要着手接口了。因为使用 ant design pro,进行前后端分离的项目。之前做了 User 模型的权限控制,这里想改成 Admin 模型,然后 jwt 基于 User 模型和 Admin 模型实现两套接口。这样之前的模型需要修改,以及需要创建 Admin 模型。

实现过程

# 创建模型,迁移以及控制器
php artisan make:model Models/Admin -mc

# 制作迁移文件,执行迁移,给 Admin 加白名单,并把之前 User 模型中的 hasRole 移到 Admin 模型中。最终的数据见下边

# CreateAdminsTable 迁移
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateAdminsTable extends Migration
{
    public function up()
    {
        Schema::create('admins', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedBigInteger('user_id')->unique();
            $table->string('nickname', 128);
            $table->string('phone', 16);
            $table->string('address', 128)->nullable();
            $table->string('remark', 128)->nullable();
            $table->unsignedBigInteger('operator_user_id');
            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
            $table->nullableTimestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('admins');
    }
}

# Admin 模型
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Spatie\Permission\Traits\HasRoles;

class Admin extends Model
{
    use HasRoles;

    protected $guard_name = 'admin';

    protected $fillable = ['user_id', 'nickname', 'phone', 'address', 'remark', 'operator_user_id'];

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

虽然上边这么做了。但是细节还是蛮多的。下边一一说明。

  1. 在 Admin 模型中,加入 `protected $guard_name = ‘admin’;

  2. 在 config/permission.php 中加入 'guard_name' => 'admin',。seeder 的时候会从这里取值。

  3. 在 config/auth.php 中 加入 guard 配置。

# 复制一份 web,修改 provider
        'admin' => [
            'driver' => 'session',
            'provider' => 'admins',
        ],

# 定义 provider
'admins' => [
            'driver' => 'eloquent',
            'model' => App\Models\Admin::class,
        ],

# 暂时先这样,然后执行迁移回滚再seeder。调用 tinker 看 role 就晓得了。

php artisan migrate:refresh --seed

php artisan tinker

$user = User::first();
# 这里就可以看到全部权限了
$user->admin->getAllPermissions ();

# 在 seeder 文件中,我们创建 role 以及 permision 的时候,都指定了具体的 guard_name 为 admin。

$guard_name = config('permission.guard_name') ?? 'web';

# 先这样吧,下一步, 我们安装 jwt,然后修改 guard。 这个时候 jwt 后台会使用到,将来接口也使用到。想要两者兼容,可以看下边的链接。

https://learnku.com/articles/28881

安装 jwt

这里操作。

# 安装 jwt
composer require tymon/jwt-auth:1.0.0-rc.5

# 生成 jwt secret
php artisan jwt:secret

# 修改 config/auth.php 中的 guard,指定 admin 的 driver 为jwt

        'admin' => [
            'driver' => 'jwt',
            'provider' => 'admins',
        ],

# 然后修改 Admin 模型,实现 JWTSubject

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Spatie\Permission\Traits\HasRoles;
use Tymon\JWTAuth\Contracts\JWTSubject;

class Admin extends Model implements JWTSubject
{
    use HasRoles;

    protected $guard_name = 'admin';

    protected $fillable = ['user_id', 'nickname', 'phone', 'address', 'remark', 'operator_user_id'];

    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    public function getJWTCustomClaims()
    {
        return ['role' => 'admin'];
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

# 将来用到接口的时候 User 模型一样要这波操作。

看起来好像好了,那么我们到 tinker 中操作一波试试。

php artisan tinker;

use App\Models\Admin;
use Auth;

$admin = Admin::first();
$token = Auth::guard('admin')->login($admin);

# 这个时候,发现报错了。原来是 Admin 这个模型模型没有继承 Auth,我们复制 User 模型这的 Auth 过来,然后继承一下。完整如下。

<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Spatie\Permission\Traits\HasRoles;
use Tymon\JWTAuth\Contracts\JWTSubject;

class Admin extends Authenticatable implements JWTSubject
{
    use HasRoles;

    protected $guard_name = 'admin';

    protected $fillable = ['user_id', 'nickname', 'phone', 'address', 'remark', 'operator_user_id'];

    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    public function getJWTCustomClaims()
    {
        return ['role' => 'admin'];
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}


# 修改好之后,我们再一波操作。发现生成的 token 是 eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9hZG1pbi5jb20iLCJpYXQiOjE1ODAzMTI1MTUsImV4cCI6MTU4MDMxNjExNSwibmJmIjoxNTgwMzEyNTE1LCJqdGkiOiJsR25sZmlmcW9iSWFwQnc5Iiwic3ViIjoxLCJwcnYiOiJkZjg4M2RiOTdiZDA1ZWY4ZmY4NTA4MmQ2ODZjNDVlODMyZTU5M2E5Iiwicm9sZSI6ImFkbWluIn0.AOU3iiIgGHE6XuaioCDo3Ce8mZmRe63WLYKL_BDj1_w

# 我们复制 token 到 https://jwt.io/ 中,进行 decode,就可以还原出具体的数据 

    {
 typ: "JWT",
 alg: "HS256"
}.
{
 iss: "http:\/\/admin.com",
 iat: 1580312515,
 exp: 1580316115,
 nbf: 1580312515,
 jti: "lGnlfifqobIapBw9",
 sub: 1,
 prv: "df883db97bd05ef8ff85082d686c45e832e593a9",
 role: "admin"
}.
[signature]

# jwt 组成的 三部分都在

到了这里,部分细节要说一遍。

  1. 建立 Admin 模型。安装 jwt ,以及之前已经安装的 laravel permission。让 Admin 模型适应 jwt 以及 laravel permission。
  2. 新建 中间件。以区分 token 是授权 api 的 还是 admin 的。后边会列出来。
  3. 建立路由。默认有 web 和 api 两个路由。可以建一个 admin 的路由。然后在 RouteServiceProvider 中复制一份 api 的修改成 admin 的即可。

列出来

# 新增路由配置
    // 新增 admin 路由
    protected function mapAdminRoutes()
    {
        Route::prefix('admin')
            ->middleware('api')
            ->namespace($this->namespace)
            ->group(base_path('routes/admin.php'));
    }

# 中间件
<?php

namespace App\Http\Middleware;

use Closure;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;

class JWTRoleAuth extends BaseMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param $request
     * @param Closure $next
     * @param null $role
     * @return mixed
     */
    public function handle($request, Closure $next, $role = null)
    {
        try {
            // 解析token角色
            $token_role = $this->auth->parseToken()->getClaim('role');
        } catch (JWTException $e) {
            /**
             * token解析失败,说明请求中没有可用的token。
             * 为了可以全局使用(不需要token的请求也可通过),这里让请求继续。
             * 因为这个中间件的责职只是校验token里的角色。
             */
            return $next($request);
        }

        // 判断token角色。
        if ($token_role != $role) {
            throw new UnauthorizedHttpException('jwt-auth', 'User role error');
        }

        return $next($request);
    }
}

# 并且要在 Kernel 的 routeMiddleware 中进行注册。
'jwt.role' => \App\Http\Middleware\JWTRoleAuth::class,

上边这些弄好了,就开始处理路由。

# 路由 admin.php
<?php

Route::namespace('Admin')->group(function(){

    Route::post('authorizations', 'AuthorizationsController@store')->name('api.authorizations.store');

    Route::group([
        'middleware' => ['jwt.role:admin', 'jwt.auth'],
    ], function () {
        // 刷新token
        Route::put('authorizations/current', 'AuthorizationsController@update')->name('authorizations.update');

        // 删除token
        Route::delete('authorizations/current', 'AuthorizationsController@destroy')->name('authorizations.destroy');
    });
});

测试发现,返回的是 html代码,并非是 json 数据。这个时候需要手动添加 Head 头 指定 Accept 为 application/json 才返回 json 格式。既然这样,就加一个中间件来返回 json 数据。

中间件

<?php

namespace App\Http\Middleware;

use Closure;

class AcceptHeader
{
    public function handle($request, Closure $next)
    {
        $request->headers->set('Accept', 'application/json');

        return $next($request);
    }
}

并添加到 api 中间件组中,

        'api' => [
            \App\Http\Middleware\AcceptHeader::class,
            'throttle:60,1',
            'bindings',
        ],

# admin 路由 和 api 都将会用到这个中间件组。

这样在请求中,就不必每次指定 Header 头的 Accept 值。