commit b134b73709f9aeea671500d0016656eda42c54be Author: Jason Date: Fri Dec 2 11:52:48 2022 +0800 init diff --git a/Config/config.php b/Config/config.php new file mode 100644 index 0000000..6aa765c --- /dev/null +++ b/Config/config.php @@ -0,0 +1,23 @@ + 'Google2FA', + + /** + * 生成的密钥长度 + */ + 'key_length' => 32, + + /** + * 密钥默认加密方式 + */ + 'algorithm' => Constants::SHA1, + + /** + * 密钥再生间隔 + */ + 'key_interval' => 30 + +]; diff --git a/Contracts/CanUseGoogle2FA.php b/Contracts/CanUseGoogle2FA.php new file mode 100644 index 0000000..eda4987 --- /dev/null +++ b/Contracts/CanUseGoogle2FA.php @@ -0,0 +1,28 @@ + + * @return string + */ + public function getUsername(): string; + + /** + * Notes : 获取用户昵称 + * + * @Date : 2022/12/1 13:47 + * @Author : + * @return string + */ + public function getNickname(): string; +} \ No newline at end of file diff --git a/Database/Migrations/2022_12_01_120619_create_google2fas_table.php b/Database/Migrations/2022_12_01_120619_create_google2fas_table.php new file mode 100644 index 0000000..21bb845 --- /dev/null +++ b/Database/Migrations/2022_12_01_120619_create_google2fas_table.php @@ -0,0 +1,34 @@ +id(); + $table->morphs('subscriber'); + $table->text('google2fa_secret')->nullable(); + $table->boolean('status')->default(0); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('google2fas'); + } +} diff --git a/Google2FA.php b/Google2FA.php new file mode 100644 index 0000000..098cd5b --- /dev/null +++ b/Google2FA.php @@ -0,0 +1,21 @@ + 'modules/Google2FA/Database/Migrations', + ]); + } + + public static function uninstall() + { + } +} \ No newline at end of file diff --git a/Http/Controllers/Api/SecretController.php b/Http/Controllers/Api/SecretController.php new file mode 100644 index 0000000..9786c03 --- /dev/null +++ b/Http/Controllers/Api/SecretController.php @@ -0,0 +1,125 @@ +google2fa = Api::user()->google2fa; + if (blank($this->google2fa)) { + throw new Exception('必须先初始化密钥'); + } + } + + /** + * Notes : 获取密钥 + * + * @Date : 2022/12/1 14:31 + * @Author : + * @param Request $request + * @return JsonResponse + * @throws Exception + */ + public function index(Request $request): JsonResponse + { + $this->needInitialize(); + + return $this->success($this->google2fa->google2fa_secret); + } + + /** + * Notes : 获取密钥二维码地址 + * + * @Date : 2022/12/1 14:31 + * @Author : + * @return JsonResponse + * @throws Exception + */ + public function qrCodeUrl(): JsonResponse + { + $this->needInitialize(); + + return $this->success($this->google2fa->getQrCodeUrl()); + } + + /** + * Notes : 开启两步验证 + * + * @Date : 2022/12/1 15:42 + * @Author : + * @return JsonResponse + * @throws Exception + */ + public function open(): JsonResponse + { + $this->needInitialize(); + + if ($this->google2fa->open()) { + return $this->success('两步验证开启成功'); + } else { + return $this->failed('开启失败'); + } + } + + /** + * Notes : 关闭两步验证 + * + * @Date : 2022/12/1 15:43 + * @Author : + */ + public function close(Request $request) + { + $this->needInitialize(); + $verify = $request->verify; + + if (strlen($verify) != 6) { + return $this->failed('请输入动态口令'); + } + if (! $this->google2fa->verify($verify)) { + return $this->failed('动态口令不正确'); + } + + if ($this->google2fa->close()) { + return $this->success('更新成功'); + } else { + return $this->failed('更新失败'); + } + } + + /** + * Notes : 更新密钥 + * + * @Date : 2022/12/1 15:29 + * @Author : + * @param Request $request + * @return JsonResponse + * @throws Exception + */ + public function update(Request $request): JsonResponse + { + // 短信验证码 + $verify = $request->verify; + + $this->needInitialize(); + + if ($this->google2fa->upgrade()) { + return $this->success('更新成功'); + } else { + return $this->failed('更新失败'); + } + } + +} \ No newline at end of file diff --git a/Models/Google2FA.php b/Models/Google2FA.php new file mode 100644 index 0000000..5f616ec --- /dev/null +++ b/Models/Google2FA.php @@ -0,0 +1,142 @@ + 'boolean', + ]; + + protected static function boot() + { + parent::boot(); + + self::creating(function ($model) { + $model->google2fa_secret = app('g2fa')->generateSecretKey(config('google2fa.key_length')); + }); + } + + /** + * Notes : 所属模型 + * + * @Date : 2022/12/1 13:28 + * @Author : + * @return MorphTo + */ + public function subscriber(): MorphTo + { + return $this->morphTo(__FUNCTION__, 'subscriber_type', 'subscriber_id'); + } + + /** + * Notes : 更新密钥 + * + * @Date : 2022/12/1 13:26 + * @Author : + * @return bool + */ + public function upgrade(): bool + { + $this->google2fa_secret = app('g2fa')->generateSecretKey(config('google2fa.key_length')); + return $this->save(); + } + + /** + * Notes : 开启两步验证 + * + * @Date : 2022/12/1 15:45 + * @Author : + * @return bool + */ + public function open(): bool + { + $this->status = 1; + return $this->save(); + } + + /** + * Notes : 关闭两步验证 + * + * @Date : 2022/12/1 15:44 + * @Author : + * @return bool + */ + public function close(): bool + { + $this->status = 0; + return $this->save(); + } + + /** + * Notes : + * + * @Date : 2022/12/1 13:31 + * @Author : + * @return string + */ + public function getQRCodeUrl(): string + { + return app('g2fa')->getQRCodeUrl( + $this->subscriber->getUsername(), + $this->subscriber->getNickname().'@'.config('app.name'), + $this->google2fa_secret + ); + } + + /** + * Notes : 验证 + * + * @Date : 2022/11/30 20:49 + * @Author : + * @param string $value + * @return bool + */ + public function verify(string $value): bool + { + return app('g2fa')->verifyGoogle2FA($this->google2fa_secret, $value); + } + + /** + * Notes : 获取当前验证码 + * + * @Date : 2022/12/2 11:49 + * @Author : + * @return mixed + */ + public function getCurrentOtp() + { + return app('g2fa')->getCurrentOtp($this->google2fa_secret); + } + + /** + * Notes : 存储到数据库的数据加密 + * + * @Date : 2022/12/2 11:49 + * @Author : + * @param $value + */ + public function setGoogle2faSecretAttribute($value): void + { + $this->attributes['google2fa_secret'] = Crypt::encrypt($value); + } + + /** + * Notes : 解密 + * + * @Date : 2022/12/2 11:49 + * @Author : + * @param $value + * @return string + */ + public function getGoogle2faSecretAttribute($value): string + { + return Crypt::decrypt($value); + } +} diff --git a/Providers/Google2FAServiceProvider.php b/Providers/Google2FAServiceProvider.php new file mode 100644 index 0000000..6bbc2ce --- /dev/null +++ b/Providers/Google2FAServiceProvider.php @@ -0,0 +1,72 @@ +registerConfig(); + $this->loadMigrationsFrom(module_path($this->moduleName, 'Database/Migrations')); + } + + /** + * Register the service provider. + * + * @return void + */ + public function register(): void + { + $this->app->register(RouteServiceProvider::class); + + $this->app->singleton('g2fa', function (Application $app) { + $google2fa = app('pragmarx.google2fa'); + $google2fa->setAlgorithm($app->make('config')->get('google2fa.algorithm')); + $google2fa->setKeyRegeneration($app->make('config')->get('google2fa.key_interval')); + return $google2fa; + }); + } + + /** + * Register config. + * + * @return void + */ + protected function registerConfig(): void + { + $this->publishes([ + module_path($this->moduleName, 'Config/config.php') => config_path($this->moduleNameLower.'.php'), + ], 'config'); + $this->mergeConfigFrom( + module_path($this->moduleName, 'Config/config.php'), $this->moduleNameLower + ); + } + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides(): array + { + return ['g2fa']; + } +} diff --git a/Providers/RouteServiceProvider.php b/Providers/RouteServiceProvider.php new file mode 100644 index 0000000..82a1217 --- /dev/null +++ b/Providers/RouteServiceProvider.php @@ -0,0 +1,28 @@ +mapApiRoutes(); + } + + protected function mapApiRoutes() + { + Route::as(config('api.route.as')) + ->domain(config('api.route.domain')) + ->middleware(config('api.route.middleware')) + ->namespace($this->moduleNamespace.'\Api') + ->prefix(config('api.route.prefix').'/'.strtolower($this->moduleName)) + ->group(module_path($this->moduleName, 'Routes/api.php')); + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..840c1a0 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# 谷歌两步验证 + +## 1. 必须同步服务器时间,NTP \ No newline at end of file diff --git a/Routes/api.php b/Routes/api.php new file mode 100644 index 0000000..af33f22 --- /dev/null +++ b/Routes/api.php @@ -0,0 +1,18 @@ +config('api.route.middleware_auth'), +], function (Router $router) { + $router->get('secret', 'SecretController@index'); + $router->get('secret/qr_code_url', 'SecretController@qrCodeUrl'); + + $router->post('secret/open', 'SecretController@open'); + $router->post('secret/close', 'SecretController@close'); + /** + * 更新密钥 + */ + $router->put('secret', 'SecretController@update'); +}); diff --git a/Traits/WithGoogle2FA.php b/Traits/WithGoogle2FA.php new file mode 100644 index 0000000..427d8e7 --- /dev/null +++ b/Traits/WithGoogle2FA.php @@ -0,0 +1,54 @@ +google2fa()->create(); + }); + } + + /** + * Notes : 模型关联 + * + * @Date : 2022/12/1 14:15 + * @Author : + * @return MorphOne + */ + public function google2fa(): MorphOne + { + return $this->morphOne(Google2FA::class, 'subscriber'); + } + + /** + * Notes : 获取用户名 + * + * @override + * @Date : 2022/12/1 14:15 + * @Author : + * @return string + */ + public function getUsername(): string + { + return 'USER-NAME'; + } + + /** + * Notes : 获取昵称 + * + * @override + * @Date : 2022/12/1 14:15 + * @Author : + * @return string + */ + public function getNickname(): string + { + return 'NICK-NAME'; + } +} \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..6aa8aa5 --- /dev/null +++ b/composer.json @@ -0,0 +1,24 @@ +{ + "name": "uztech/google2fa-module", + "description": "", + "type": "laravel-module", + "authors": [ + { + "name": "Jason.Chen", + "email": "chenjxlg@163.com" + } + ], + "require": { + "pragmarx/google2fa-laravel": "^2.0", + "simplesoftwareio/simple-qrcode": "^4.2" + }, + "extra": { + "module-dir": "modules" + }, + "autoload": { + "psr-4": { + "Modules\\Google2FA\\": "" + } + } +} + diff --git a/module.json b/module.json new file mode 100644 index 0000000..5c76a21 --- /dev/null +++ b/module.json @@ -0,0 +1,15 @@ +{ + "name": "Google2FA", + "alias": "google2fa", + "description": "谷歌两步验证", + "keywords": [], + "priority": 0, + "providers": [ + "Modules\\Google2FA\\Providers\\Google2FAServiceProvider" + ], + "aliases": {}, + "files": [], + "requires": [], + "version": "1.0.0", + "author": "Jason.Chen" +}