├── config └── signature.php ├── src ├── Middleware │ └── SignatureAuthenticate.php ├── SignatureProvider.php ├── SignatureGuard.php └── Signature.php ├── composer.json ├── database └── migrations │ └── 2021_06_25_000000_update_users_add_field.php └── README.md /config/signature.php: -------------------------------------------------------------------------------- 1 | env('TOKEN_EXPIRED_AT', '86400'), 6 | // 请求有效期 7 | 'request_period' => env('REQUEST_PERIOD', '30') 8 | ]; 9 | -------------------------------------------------------------------------------- /src/Middleware/SignatureAuthenticate.php: -------------------------------------------------------------------------------- 1 | validated()) { 22 | return response()->json([ 23 | 'code' => $guard->getErrCode(), 24 | 'message' => $guard->getErrMessage() 25 | ]); 26 | } 27 | 28 | return $next($request); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "whcoding/signature", 3 | "description": "api signature", 4 | "keywords": [ 5 | "api", 6 | "auth", 7 | "signature" 8 | ], 9 | "license": "MIT", 10 | "minimum-stability": "dev", 11 | "authors": [ 12 | { 13 | "name": "wenhao", 14 | "email": "840346763@qq.com" 15 | } 16 | ], 17 | "require": { 18 | "php": "^7.3|^8.0", 19 | "laravel/framework": "^8.40" 20 | }, 21 | "require-dev": { 22 | "php": "^7.3|^8.0", 23 | "laravel/framework": "^8.40" 24 | }, 25 | "autoload": { 26 | "psr-4": { 27 | "Signature\\": "src/" 28 | } 29 | }, 30 | "extra": { 31 | "laravel": { 32 | "providers": [ 33 | "Signature\\SignatureProvider" 34 | ] 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /database/migrations/2021_06_25_000000_update_users_add_field.php: -------------------------------------------------------------------------------- 1 | string('api_token')->unique()->nullable(); 18 | $table->integer('token_expired_at')->nullable(); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | * 25 | * @return void 26 | */ 27 | public function down() 28 | { 29 | Schema::table('users', function (Blueprint $table) { 30 | $table->dropColumn('api_token'); 31 | $table->dropColumn('token_expired_at'); 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### laravel-signature 2 | 3 | laravel-signature 是一个接口签名验证包, 其中包括了对token生成, token过期时间, 请求的有效期, sign生成 4 | 5 | #### 运行环境 6 | - php >=7.3 7 | - composer 8 | - laravel >= 8.40 9 | - .env 开启 debug 会返回正确sign, 生产环境记得关闭. 10 | 11 | #### 使用方法 12 | `composer require whcoding/signature` 13 | 14 | 设置 auth.php 15 | ``` 16 | 'defaults' => [ 17 | 'guard' => 'api', // laravel 默认 session 更改为 api 18 | 'passwords' => 'users', 19 | ], 20 | 21 | 'guards' => [ 22 | ..... 23 | 24 | 'api' => [ 25 | 'driver' => 'signature', // 更改为 signature 26 | 'provider' => 'users', 27 | 'hash' => false, 28 | ], 29 | ], 30 | 31 | ``` 32 | 33 | #### 配置文件 34 | ` 35 | php artisan vendor:publish --provider="Signature\SignatureProvider" 36 | ` 37 | 38 | #### 执行 migration 39 | ` 40 | php artisan migrate 41 | ` 42 | 43 | #### 前端请求需要携带的参数 44 | ``` 45 | { 46 | "ts" : "时间戳(请求时间)", 47 | "user_id" : "用户id", 48 | "sign": "前端根据该包的sign算法生成的(参考 Signature.php 中的 makeSign 方法)" 49 | } 50 | ``` 51 | -------------------------------------------------------------------------------- /src/SignatureProvider.php: -------------------------------------------------------------------------------- 1 | SignatureAuthenticate::class 14 | ]; 15 | 16 | public function register() 17 | { 18 | 19 | } 20 | 21 | public function boot() 22 | { 23 | $this->loadMigrationsFrom(__DIR__ . '/../database/migrations'); 24 | $this->publishes([ 25 | __DIR__ . '/../config/signature.php' => config_path('signature.php'), 26 | ]); 27 | $this->aliasMiddlewares(); 28 | $this->registerGuards(); 29 | } 30 | 31 | protected function aliasMiddlewares() 32 | { 33 | $router = $this->app['router']; 34 | $method = method_exists($router, 'aliasMiddleware') ? 'aliasMiddleware' : 'middleware'; 35 | foreach ($this->middlewares as $alias => $middleware) { 36 | $router->$method($alias, $middleware); 37 | } 38 | } 39 | 40 | protected function registerGuards() 41 | { 42 | Auth::extend('signature', function ($app, $name, $config) { 43 | return new SignatureGuard( 44 | new Signature(), 45 | $app['auth']->createUserProvider($config['provider']), 46 | $app['request'] 47 | ); 48 | }); 49 | } 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/SignatureGuard.php: -------------------------------------------------------------------------------- 1 | signature = $signature; 33 | $this->provider = $provider; 34 | $this->request = $request; 35 | $this->signature->setRequest($this->request); 36 | $this->signature->setProvider($this->provider); 37 | } 38 | 39 | 40 | public function user(): ?Authenticatable 41 | { 42 | if ($this->user != null) { 43 | return $this->user; 44 | } 45 | 46 | $user = null; 47 | if ($this->signature->validated()) { 48 | $user = $this->provider->retrieveById($this->signature->getUserId()); 49 | } 50 | 51 | return $this->user = $user; 52 | } 53 | 54 | public function validate(array $credentials = []) 55 | { 56 | return $this->attempt($credentials, false); 57 | } 58 | 59 | 60 | public function attempt($credentials, $login = true) 61 | { 62 | $user = $this->provider->retrieveByCredentials($credentials); 63 | if ($this->hasVaildCredentials($user, $credentials)) { 64 | return $this->login($user); 65 | } 66 | return false; 67 | } 68 | 69 | 70 | public function login($user) 71 | { 72 | $token = (!$user->api_token || $user->token_expored_at < time()) ? $this->refreshToken($user) : $user->api_token; 73 | $this->user = $user; 74 | return $token; 75 | } 76 | 77 | protected function refreshToken(User $user) 78 | { 79 | $user->api_token = Str::random(60); 80 | $user->token_expired_at = time() + config('signature.token_expired_at'); 81 | $user->save(); 82 | return $user->api_token; 83 | } 84 | 85 | protected function hasVaildCredentials($user, $credentials) 86 | { 87 | return $user !== null && $this->provider->validateCredentials($user, $credentials); 88 | } 89 | 90 | public function __call($method, $parameters) 91 | { 92 | if (method_exists($this->signature, $method)) { 93 | return call_user_func_array([$this->signature, $method], $parameters); 94 | } 95 | throw new \BadMethodCallException("Method [$method], does not exist"); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/Signature.php: -------------------------------------------------------------------------------- 1 | provider = $provider; 33 | return $this; 34 | } 35 | 36 | public function setRequest($request) 37 | { 38 | $this->request = $request; 39 | return $this; 40 | } 41 | 42 | public function validated() 43 | { 44 | $input = $this->request->all(); 45 | $method = $this->request->method(); 46 | $path = $this->request->path(); 47 | 48 | return $this->validateRequest($input) 49 | && $this->validateRequestTime($input['ts']) 50 | && $this->validateUser($input['user_id']) 51 | && $this->validateLoginStatus() 52 | && $this->validateSign($input, $method, $path); 53 | } 54 | 55 | /** 56 | * 验证请求 57 | * @param $input 58 | * @return bool 59 | */ 60 | public function validateRequest($input): bool 61 | { 62 | foreach (self::MUST_REQUEST_PARAMS as $params) { 63 | if (!isset($input[$params])) { 64 | $this->errCode = 40301; 65 | $this->errMessage = '缺少必要参数' . $params; 66 | return false; 67 | } 68 | } 69 | return true; 70 | } 71 | 72 | /** 73 | * 验证请求时间 74 | * @param $ts 75 | * @return bool 76 | */ 77 | public function validateRequestTime($ts): bool 78 | { 79 | if (time() - $ts > config('signature.request_period')) { 80 | $this->errCode = 40302; 81 | $this->errMessage = '请求过期'; 82 | return false; 83 | } 84 | return true; 85 | } 86 | 87 | /** 88 | * 验证用户 89 | * @param $userId 90 | * @return bool 91 | */ 92 | public function validateUser($userId): bool 93 | { 94 | if (!$this->getUser($userId)) { 95 | $this->errCode = 40303; 96 | $this->errMessage = '用户不存在'; 97 | return false; 98 | } 99 | return true; 100 | 101 | } 102 | 103 | 104 | public function getUser($userId) 105 | { 106 | return $this->user = $this->provider->retrieveById($userId); 107 | } 108 | 109 | /** 110 | * 验证登录是否过期 111 | * @return bool 112 | */ 113 | public function validateLoginStatus(): bool 114 | { 115 | if ($this->user->token_expired_at < time()) { 116 | $this->errCode = 40304; 117 | $this->errMessage = '登录已过期'; 118 | return false; 119 | } 120 | return true; 121 | } 122 | 123 | public function getUserId() 124 | { 125 | return $this->user->id; 126 | } 127 | 128 | 129 | public function validateSign($input, $method, $path) 130 | { 131 | $sign = $this->makeSign($input, $method, $path); 132 | if ($input['sign'] != $sign) { 133 | $this->errCode = 40305; 134 | if (env('APP_DEBUG')) { 135 | $this->errMessage = '签名验证失败 ' . $sign; 136 | } else { 137 | $this->errMessage = '签名验证失败'; 138 | } 139 | return false; 140 | } 141 | return true; 142 | } 143 | 144 | /** 145 | * 生成sign 146 | * @param $input 147 | * @param $method 148 | * @param $path 149 | * @return string 150 | */ 151 | public function makeSign($input, $method, $path) 152 | { 153 | unset($input['sign']); 154 | ksort($input); 155 | $str = ''; 156 | foreach ($input as $key => $value) { 157 | $str .= $key . $value; 158 | } 159 | 160 | $str .= $this->user->api_token; 161 | $str .= $method; 162 | $str .= $path; 163 | 164 | return sha1($str); 165 | } 166 | 167 | public function getErrMessage() 168 | { 169 | return $this->errMessage; 170 | } 171 | 172 | public function getErrCode() 173 | { 174 | return $this->errCode; 175 | } 176 | 177 | 178 | } 179 | --------------------------------------------------------------------------------