Sivo 悉见

Laravel5框架使用Oauth2.0

匿名 · 更新于 2018/1/4

Laravel5:一款享誉国内外的PHP框架。

Oauth2.0 : 一款耳熟能详的作为令牌验证机制的开源软件。

 

为什么会将这两者搭配起来?

主要的动机是我需要做一个Android客户端,这必然涉及到与服务器的交互,虽然自己去完成Oauth的Token验证功能并不是太难,但是既然人家Oauth已经这么成熟了,何苦去自己写呢?另外,因为我的Web项目就是使用Laravel5开发的,所以也就出现了我现在需要将Oauth融入到我的Laravel框架中。

 

如何实现?

其实如果单纯的将Oauth2.0融入到Laravel中其实并没有直接的途径,比如框架千千万万,而Oauth也并不会为每一种框架都准备使用教程对吧?所以只能去github上找找出路。

 

好在github上真的有解决方案,当然方案还不止一种,但我以我的亲身经历证明,这一款比较牛逼,提前声明的是这一款是使用Laravel5内置的Middleware来实现的,当然我认为这样的话会更加灵活。

解决方案在此 : https://bshaffer.github.io/oauth2-server-php-docs/cookbook/laravel/

 

==================================================================================================================================

华丽丽的分割线

你可以按照上面链接按照步骤一步一步安装,也可以跟着我往下走。但还是墙裂推荐跟着我往下走噢!

==================================================================================================================================

 

1.使用composer安装oauth2-server-php

 

[php] view plain copy
 
  1. composer require bshaffer/oauth2-server-php   
  2. composer require bshaffer/oauth2-server-httpfoundation-bridge  

由于国外镜像被墙,国内镜像还不稳定,据我所知国内现在有两个镜像可以用,如果一个不行用另一个。点击此处学习如何使用国内镜像

 

 

2.创建Oauth相关的表迁移

在项目根目录下执行

 

[html] view plain copy
 
  1. php artisan db:migrate create_oauth_tables
  2. //Laravel5.5使用以下命令
  3. php artisan make:migration create_oauth_tables​  

执行完该命令后,会在database/migrations/目录下生成一个以当前日期为开头的以create_oauth_tables结尾的.php文件,打开它,输入以下代码,并保存。
<?php

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

class CreateOauthTables extends Migration {     /**      * Run the migrations.      *      * @return void      /     public function up()     {         $schema=<<<SCHEMA             CREATE TABLE oauth_clients (client_id VARCHAR(80) NOT NULL, client_secret VARCHAR(80) NOT NULL, redirect_uri VARCHAR(2000) NOT NULL, grant_types VARCHAR(80), scope VARCHAR(100), user_id VARCHAR(80), CONSTRAINT client_id_pk PRIMARY KEY (client_id));             CREATE TABLE oauth_access_tokens (access_token VARCHAR(40) NOT NULL, client_id VARCHAR(80) NOT NULL, user_id VARCHAR(191), expires TIMESTAMP NOT NULL, scope VARCHAR(2000), CONSTRAINT access_token_pk PRIMARY KEY (access_token));             CREATE TABLE oauth_authorization_codes (authorization_code VARCHAR(40) NOT NULL, client_id VARCHAR(80) NOT NULL, user_id VARCHAR(191), redirect_uri VARCHAR(2000), expires TIMESTAMP NOT NULL, scope VARCHAR(2000), CONSTRAINT auth_code_pk PRIMARY KEY (authorization_code));             CREATE TABLE oauth_refresh_tokens (refresh_token VARCHAR(40) NOT NULL, client_id VARCHAR(80) NOT NULL, user_id VARCHAR(191), expires TIMESTAMP NOT NULL, scope VARCHAR(2000), CONSTRAINT refresh_token_pk PRIMARY KEY (refresh_token));             CREATE TABLE oauth_users (username VARCHAR(191) NOT NULL, password VARCHAR(2000), first_name VARCHAR(191), last_name VARCHAR(191), CONSTRAINT username_pk PRIMARY KEY (username));             CREATE TABLE oauth_scopes (scope TEXT, is_default BOOLEAN);             CREATE TABLE oauth_jwt (client_id VARCHAR(80) NOT NULL, subject VARCHAR(80), public_key VARCHAR(2000), CONSTRAINT client_id_pk PRIMARY KEY (client_id)); SCHEMA;                    foreach (explode("\n", $schema) as $statement) {               DB::statement($statement);           }       }          /**        Reverse the migrations.               @return void       */       public function down()       {           //           DB::statement('DROP TABLE oauth_clients, oauth_access_tokens, oauth_authorization_codes, oauth_refresh_tokens, oauth_users, oauth_scopes, oauth_jwt');       }   }


 
 

 

保存后,terminal切换到项目根目录,执行如下命令。

 

[html] view plain copy
 
  1. php artisan migrate  

执行完命令后会发现,数据库中会出现好多oauth开头的数据表。

 

 

3.新建seeds

这时,oauth_users 和 oauth_clients 数据表还没有任何记录,如果数据库中找不到对应的模型实例,会自动生成 HTTP 404 响应,提示页面不存在,
所以我们需要在这张表中插入一条记录,这里我们基于 Laravel 强大的数据库填充器来快速完成数据填充功能,
首先通过如下命令在database/seeds/目录下生成 
OAuthUsersSeeder.php  和 OAuthClientsSeeder.php 

php artisan make:seeder OAuthUsersSeeder
php artisan make:seeder OAuthClientsSeeder

然后分别写入如下代码

OAuthClientsSeeder.php

 

[php] view plain copy
 
  1. <?php  
  2. use Illuminate\Database\Seeder;  
  3. class OAuthClientsSeeder extends Seeder  
  4. {  
  5.     public function run()  
  6.     {  
  7.         DB::table('oauth_clients')->insert(array(  
  8.             'client_id' => "testclient",  
  9.             'client_secret' => "testpass",  
  10.             'redirect_uri' => "http://fake/",  
  11.         ));  
  12.     }  
  13. }  
 

 

OAuthUsersSeeder.php

 

[php] view plain copy
 
  1. <?php  
  2. use Illuminate\Database\Seeder;  
  3. class OAuthUsersSeeder extends Seeder  
  4. {  
  5.     public function run()  
  6.     {  
  7.         DB::table('oauth_users')->insert(array(  
  8.             'username' => "bshaffer",  
  9.             'password' => sha1("brent123"),  
  10.             'first_name' => "Brent",  
  11.             'last_name' => "Shaffer",  
  12.         ));  
  13.     }  
  14. }  
 
然后编辑同目录下的 DatabaseSeeder.php 文件如下(增加调用数据表填充器前代码):




 

保存退出后,切换到项目根目录,执行以下命令,自动加载下composer的配置

 

[php] view plain copy
 
  1. composer dump-autoload  




再执行下面这条命令,将刚刚新建的两个种子OAuthClientsSeeder.php 和 OAuthUsersSeeder.php插入一些数据进入数据库

 

 

[php] view plain copy
 
  1. php artisan db:seed  

这是会发现oauth_users和oauth_clients这两个表各插入了一条数据。

 

 

4.routeMiddleware配置

打开app/Http/Kernel.php,将'App\Http\Middleware\VerifyCsrfToken',这一行注释,原因在于客户端是无状态的,也没有HTML表单中的_token变量,所以需要禁用掉Csrf的验证。

添加中间件Oauth.php

 

php artisan make:middleware Oauth


 
 

 这个命令会在 app/Http/Middleware 目录下创建一个新的中间件类 Oauth
然后再oauth.php文件中加入以下代码

<?php

namespace App\Http\Middleware;

use Closure;

class Oauth {     /**      * Handle an incoming request.      *      * @param  \Illuminate\Http\Request  $request      * @param  \Closure  $next      * @return mixed      */     public function handle($request, Closure $next)     {         if(!$request->input('access_token')){             return response( 'Token not found', 422);         }         $req = \Symfony\Component\HttpFoundation\Request::createFromGlobals();         $bridgeRequest = OAuthRequest::createFromRequest($req);         $bridgeResponse = new \OAuth2\HttpFoundationBridge\Response();

        if(!$token = App::make('oauth2')->getAccessTokenData($bridgeRequest, $bridgeResponse)){             $response = App::make('oauth2')->getResponse();             if($response -> getParameter('error') == 'expired_token'){                 return response('The access token provided has expired', 401);             }             return response('Invalid Token.', 422);         } else {             $request['user_id'] = $token['user_id'];         }         return $next($request);     } }


 
然后在$routeMiddleware增加一行, 'oauth'=> \App\Http\Middleware\Oauth::class; 因为本教程实现的就是希望能够在request和controller中间加一层middleware来实现Token的验证机制,所以当我们新增了这一行之后,下面还需要添加OauthMiddleware这个中间件才行。

修改后的Kernel.php文件为:
 

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel {     /**      * The application's global HTTP middleware stack.      *      * These middleware are run during every request to your application.      *      * @var array      */     protected $middleware = [         \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,         \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,         \App\Http\Middleware\TrimStrings::class,         \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,         \App\Http\Middleware\TrustProxies::class,     ];

    /**      * The application's route middleware groups.      *      * @var array      */     protected $middlewareGroups = [         'web' => [             \App\Http\Middleware\EncryptCookies::class,             \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,             \Illuminate\Session\Middleware\StartSession::class,             // \Illuminate\Session\Middleware\AuthenticateSession::class,             \Illuminate\View\Middleware\ShareErrorsFromSession::class, //            \App\Http\Middleware\VerifyCsrfToken::class,             \Illuminate\Routing\Middleware\SubstituteBindings::class,         ],

        'api' => [             'throttle:60,1',             'bindings',         ],     ];

    /**      * The application's route middleware.      *      * These middleware may be assigned to groups or used individually.      *      * @var array      */     protected $routeMiddleware = [         'auth' => \Illuminate\Auth\Middleware\Authenticate::class,         'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,         'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,         'can' => \Illuminate\Auth\Middleware\Authorize::class,         'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,         'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,         'oauth'=> \App\Http\Middleware\Oauth::class     ]; }

 

5.route路由配置

打开app.Http/routes.php中新增如下代码,注意更改Pdo连接数据库的username和password

 

[php] view plain copy
 
  1. App::singleton('oauth2'function() {  
  2.      $storage = new OAuth2\Storage\Pdo(array(  
  3.         'dsn' => 'mysql:dbname=ishare_school;host=localhost''username' => 'root''password' => env('DB_PASSWORD''')));  
  4.      $server = new OAuth2\Server($storage);  
  5.      $server->addGrantType(new OAuth2\GrantType\ClientCredentials($storage));  
  6.      $server->addGrantType(new OAuth2\GrantType\UserCredentials($storage));  
  7.      return $server;  
  8. });  
  9. Route::get('oauth/token'function()  
  10. {     
  11.     $bridgedRequest  = OAuth2\HttpFoundationBridge\Request::createFromRequest(Request::instance());  
  12.     $bridgedResponse = new OAuth2\HttpFoundationBridge\Response();  
  13.       
  14.     $bridgedResponse = App::make('oauth2')->handleTokenRequest($bridgedRequest$bridgedResponse);  
  15.     return $bridgedResponse;  
  16. });  
 

 

 

==================================================================================================================================

又是华丽丽的分割线

OK,上面就是基本的配置了,下面就需要来使用了。

==================================================================================================================================

 
1.获取Token

  请求服务器,路由为oauth/token来获得Token

 

2.有些API需要验证Token,有些API不需要验证Token怎么办?

别忘了我们新增了一个middleware啊。

不需要验证Token的路由怎么写?

 

[php] view plain copy
 
  1. Route::post('veirfycode''Api\UserController@veirfyCode');  

需要验证Token的路由怎么写?

 

 

[php] view plain copy
 
  1. Route::post('addpasswd'  , ['middleware' => 'oauth''uses'=>'Api\UserController@addPassword']);  
 

 

 

 

 

==================================================================================================================================

又是华丽丽的分割线

缺点!!!!!请注意!!!!!

==================================================================================================================================

 
1.毕竟这个Oauth2.0是个国外的产品,有许多提示信息还是英文,需要做的可能是要更改oauth2-server-php的源码

2.除了语言层面,还有一点是Oauth2.0的验证方式并非很适合已经有一定用户的情况,所以需要将网站已有用户的数据同步到oauth_access等数据表中才可以。

 

 

 

若转载,请注明出处,谢谢配合。