├── .gitignore ├── LICENSE ├── README.md ├── composer.json └── src ├── Api.php ├── Auther.php ├── AutherException.php ├── Router.php ├── config └── jwt.php └── middleware └── AutherMiddleware.php /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .idea/ 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 1M84 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # thinkphp-auth 2 | 基于thinkphp6开发的一个JWT权限验证插件 3 | 4 | # 使用方法 5 | 6 | ### 1. 安装 7 | 8 | ``` 9 | composer require cncoders/thinkphp-auth * 10 | ``` 11 | 12 | ### 2. 配置文件 13 | 14 | 将安装包内的config/jwt.php复制到thinkphp配置目录 15 | 16 | ### 3. 异常处理 17 | 18 | 如果需要对异常处理成JSON接口可用的格式可以在app\ExceptionHandle.php增加相应的异常处理示例如下: 19 | 20 | ``` 21 | 22 | if ($e instanceof AutherException) { 23 | exit( json_encode(['code' => $e->getCode(), 'message' => $e->getMessage()]) ); 24 | } 25 | 26 | ``` 27 | 28 | ### 4.使用示例: 29 | 30 | 路由使用 单独使用版本判断可以不用配置jwt 31 | 32 | 版本判断不支持路由缓存 33 | 34 | ``` 35 | 36 | allows(['[<]1.0.0'])->includes(__DIR__ . '/version1/router.php'); 48 | 49 | $router->allows('[>=]1.0.1')->callback(function(){ 50 | Route::rule('/auth', '/auth/index'); 51 | }); 52 | 53 | $router->boot('1.0.2', function(){ 54 | Route::group(...); 55 | }); 56 | 57 | ``` 58 | auther使用 59 | 60 | ``` 61 | Auther::make()->token(); //生成TOKEN 62 | Auther::make()->refreshToken(); //刷新TOKEN 63 | AutherMiddleware中间件处理对鉴权的判断 64 | 65 | ``` 66 | 67 | 68 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cncoders/thinkphp-auth", 3 | "description": "a simple api auth with thinkphp6", 4 | "homepage": "https://github.com/cncoders/thinkphp-auth.git", 5 | "license": "MIT", 6 | "keywords": ["auth", "thinkphp", "tp", "tp6", "php"], 7 | "authors": [ 8 | { 9 | "name": "fuwei", 10 | "email": "542928955@qq.com" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=5.6", 15 | "firebase/php-jwt": "5.*" 16 | }, 17 | "type": "library", 18 | "autoload": { 19 | "psr-4": {"cncoders\\auth\\": "src"} 20 | } 21 | } -------------------------------------------------------------------------------- /src/Api.php: -------------------------------------------------------------------------------- 1 | version = $version; 30 | } 31 | 32 | /** 33 | * 判断版本是否允许走指定路由 34 | * 35 | * @param array $versions 36 | * @param string $rule 37 | * @param null $route 38 | * @return \think\route\RuleGroup 39 | */ 40 | public function route($versions = [], $rule = '', $route = null) 41 | { 42 | if (!empty($versions)) { 43 | if ( is_string($versions) && $versions == $this->version) { 44 | return Route::group($rule, $route)->middleware($this->middleware); 45 | } 46 | 47 | if ( is_array($versions) && in_array($this->version ,$versions)) { 48 | return Route::group($rule, $route)->middleware($this->middleware); 49 | } 50 | } else { 51 | return Route::group($rule, $route)->middleware($this->middleware); 52 | } 53 | } 54 | 55 | /** 56 | * 判断版本并且用JWT校验来源合法性 57 | * 58 | * @param array $versions 59 | * @param string $rule 60 | * @param null $route 61 | * @return \think\route\RuleGroup 62 | */ 63 | public function routeWithAuther($versions = [], $rule = '', $route = null) 64 | { 65 | $this->middleware = [\cncoders\auth\middleware\AutherMiddleware::class]; 66 | return $this->route($versions, $rule, $route); 67 | } 68 | } -------------------------------------------------------------------------------- /src/Auther.php: -------------------------------------------------------------------------------- 1 | jwt_secret = $secret; 48 | $this->jwt_ttl = $jwt_ttl; 49 | 50 | if ( !empty($jwt_alg) ) { 51 | $this->jwt_alg = $jwt_alg; 52 | } 53 | 54 | } 55 | 56 | /** 57 | * 单例模式运行 58 | * @return Auther|null 59 | * @throws \Exception 60 | */ 61 | public static function make() 62 | { 63 | if ( is_null( self::$instance ) ) { 64 | self::$instance = new self(); 65 | } 66 | return self::$instance; 67 | } 68 | 69 | /** 70 | * 设置额外的加密参数 71 | * 72 | * @param array $headers 73 | * @return $this 74 | */ 75 | public function setHeaders( array $headers = [] ) 76 | { 77 | $this->headers = $headers; 78 | return $this; 79 | } 80 | 81 | /** 82 | * 获取TOKEN 83 | * @param array $data 84 | * @return string 85 | */ 86 | public function token(array $data = array()) 87 | { 88 | $hmc_data = []; 89 | $hmc_data['timestamp'] = time(); 90 | $hmc_data['verifyTime'] = time() + $this->jwt_ttl; 91 | $hmc_data['alg'] = $this->jwt_alg; 92 | $hmc_data['data'] = $data; 93 | return JWT::encode($hmc_data, $this->jwt_secret, $this->jwt_alg, NULL, $this->headers); 94 | } 95 | 96 | /** 97 | * 校验TOKEN 98 | * @param $token 99 | * @return array 100 | * @throws \Exception 101 | */ 102 | public function verfiyToken() 103 | { 104 | $token = $this->getToken(); 105 | 106 | if (empty($token) ) { 107 | throw new AutherException('The token is Empty!', 401); 108 | } 109 | 110 | $decodeData = $this->JwtDecodeData($token); 111 | if (!isset($decodeData->timestamp) || !isset( $decodeData->verifyTime ) ) { 112 | throw new AutherException('Token is Invaild!',401); 113 | } 114 | 115 | if (time() > $decodeData->verifyTime) { 116 | throw new AutherException('The token is Expired!', 401); 117 | } 118 | 119 | $this->tokenData = $decodeData; 120 | return true; 121 | } 122 | 123 | /** 124 | * 解密TOKEN 125 | * 126 | * @param $token 127 | * @return object 128 | */ 129 | private function JwtDecodeData($token) 130 | { 131 | if ( empty($token) ) { 132 | throw new AutherException('TOKEN is empty!', 401); 133 | } 134 | return JWT::decode($token, $this->jwt_secret,['HS256','HS384','HS512','RS256','RS384','RS512']); 135 | } 136 | 137 | /** 138 | * 获取传递的header数据 139 | * 140 | * @return object 141 | */ 142 | public function header() 143 | { 144 | $jwt = $this->getToken(); 145 | $tks = explode('.', $jwt); 146 | list($headb64, $bodyb64, $cryptob64) = $tks; 147 | $header = JWT::jsonDecode(JWT::urlsafeB64Decode($headb64)); 148 | return $header; 149 | } 150 | 151 | /** 152 | * 刷新TOKEN 153 | * @return string 154 | */ 155 | public function refreshToken() 156 | { 157 | $decodeData = $this->JwtDecodeData( $this->getToken() ); 158 | 159 | if (time() - $decodeData->verifyTime > Config::get('jwt.jwt_allow_ttl')) { 160 | throw new AutherException('已经超过最大刷新时间', -10002); 161 | } 162 | 163 | $this->setHeaders( $this->objectToArray( $this->header() ) ); 164 | return $this->token( $this->objectToArray( $decodeData->data ) ); 165 | } 166 | 167 | /** 168 | * 对象转数组 169 | * 170 | * @param $data 171 | * @return mixed 172 | */ 173 | public function objectToArray($data) 174 | { 175 | return json_decode( json_encode($data) , true ); 176 | } 177 | 178 | /** 179 | * 获取TOKEN 180 | * 181 | * @return mixed 182 | */ 183 | public function getToken() 184 | { 185 | $request = Request::instance(); 186 | if ( Config::get('jwt.jwt_request_type') === 'header') 187 | return $request->header( strtolower( Config::get('jwt.jwt_request_field') ) ); 188 | else 189 | return $request->param( Config::get('jwt.jwt_request_field') ); 190 | } 191 | 192 | /** 193 | * 获取加密的内容 194 | * 195 | * @return null 196 | */ 197 | public function decodeData() 198 | { 199 | return isset( $this->tokenData->data ) ? $this->tokenData->data : null; 200 | } 201 | 202 | /** 203 | * 获取所有的加密数据 204 | * 205 | * @return null 206 | */ 207 | public function all() 208 | { 209 | return $this->tokenData; 210 | } 211 | } -------------------------------------------------------------------------------- /src/AutherException.php: -------------------------------------------------------------------------------- 1 | version = $version; 28 | $this->isCom = $is_com; 29 | } 30 | 31 | /** 32 | * @param string $allowVersion 33 | * @param null $callback 34 | * @return bool|mixed 35 | */ 36 | public function boot($allowVersion = '', $callback = null) 37 | { 38 | if ( is_string($allowVersion) ) $allowVersion = [$allowVersion]; 39 | $total = count($allowVersion); 40 | for( $i = 0; $i < $total; $i++ ) { 41 | if ( false === $this->compareVersions($allowVersion[$i], $callback)) { 42 | continue; 43 | } else { 44 | return $this->compareVersions($allowVersion[$i], $callback); 45 | } 46 | } 47 | unset($total,$i,$allowVersion); 48 | return false; 49 | } 50 | 51 | /** 52 | * @param string $allowVersion 53 | * @param null $callback 54 | * @return bool|mixed 55 | */ 56 | private function compareVersions($allowVersion = '', $callback = null) 57 | { 58 | if ( empty($allowVersion) || $this->isCom === true ) { 59 | return call_user_func($callback); 60 | } 61 | $capare = false; 62 | if ( stripos($allowVersion, '[<]') !== false ) { 63 | $capare = true; 64 | if (Helper::compareVersion($this->version , $allowVersion) !== -1) { 65 | return false; 66 | } 67 | } 68 | if ( stripos($allowVersion, '[<=]') !== false ) { 69 | $capare = true; 70 | if (Helper::compareVersion($this->version , $allowVersion) === 1) { 71 | return false; 72 | } 73 | } 74 | if ( stripos($allowVersion, '[>]') !== false ) { 75 | $capare = true; 76 | if (Helper::compareVersion($this->version, $allowVersion) !== 1) { 77 | return false; 78 | } 79 | } 80 | if ( stripos($allowVersion, '[>=]') !== false ) { 81 | $capare = true; 82 | if (Helper::compareVersion($this->version, $allowVersion) === -1) { 83 | return false; 84 | } 85 | } 86 | if ($capare === false && Helper::compareVersion($this->version, $allowVersion) !== 0) { 87 | return false; 88 | } 89 | return call_user_func($callback); 90 | } 91 | 92 | /** 93 | * 设置允许访问的版本 94 | * @param $allowVersions 95 | * @return $this 96 | */ 97 | public function allows($allowVersions) 98 | { 99 | $this->allowVersions = is_string($allowVersions) ? [$allowVersions] : $allowVersions; 100 | return $this; 101 | } 102 | 103 | /** 104 | * 加载路由文件 105 | * @param $file 106 | */ 107 | public function includes($file) 108 | { 109 | if ( !file_exists($file) ) { 110 | return false; 111 | } 112 | return $this->boot($this->allowVersions, function() use ($file){ 113 | include_once $file; 114 | }); 115 | } 116 | 117 | /** 118 | * 以回调函数执行 119 | * @param null $callback 120 | * @return bool 121 | */ 122 | public function callback($callback = null) 123 | { 124 | return $this->boot($this->allowVersions, $callback); 125 | } 126 | } -------------------------------------------------------------------------------- /src/config/jwt.php: -------------------------------------------------------------------------------- 1 | MD5('JWT-TEST-AUTHER-SECRET'), 6 | 7 | //加密的有效期 8 | 'jwt_ttl' => 180, 9 | 10 | //过期多久以内允许刷新 11 | 'jwt_allow_ttl' => 360, 12 | 13 | //密文传输方式 header or body 14 | 'jwt_request_type' => 'header', 15 | 16 | //密文传输的字段名称 17 | 'jwt_request_field' => 'Authorization', 18 | 19 | //加密方式 'HS256','HS384','HS512','RS256','RS384','RS512' 20 | 'jwt_alg' => 'HS512' 21 | 22 | ]; -------------------------------------------------------------------------------- /src/middleware/AutherMiddleware.php: -------------------------------------------------------------------------------- 1 | verfiyToken(); 17 | 18 | bind('auther', function(){ 19 | return Auther::make()->all()->data; 20 | }); 21 | 22 | bind('aheader', function(){ 23 | return Auther::make()->header(); 24 | }); 25 | 26 | return $next($request); 27 | } 28 | } --------------------------------------------------------------------------------