Laravel学習ノート(27)laravel 6認証と認証(OAuth 2.0認証コードモード例)

33001 ワード

参考サイト
  • は、サードパーティサーバをシミュレートする2つのサーバ、1つのシミュレート認可サーバ
  • を用意します.
  • ライセンスサーバ(mushishi.com)
  • ComposerパッケージマネージャによるPassportのインストール:laravel/passportでOAuth認証サービスを提供
    composer require laravel/passport
    

    クライアントとトークンを格納するデータテーブルを作成します.
    php artisan migrate
    

    次に、passport:installコマンドを実行して、セキュリティ・アクセス・トークンの生成に必要な暗号鍵を作成します.また、このコマンドは、アクセス・トークンの生成に使用される「パーソナル・アクセス」クライアント(Personal Access Client)と「パスワード認可」クライアント(Password Grant Client):
    php artisan passport:install
    
    //     
    php artisan passport:keys			//       access_token
    php artisan passport:client			//         ,  client ID   client secret
    

    PS:生成されたアクセスtoken位置-storage/oauth-private.key
    上記のコマンドを実行したら、LaravelPassportHasApiTokens TraitをAppUserモデルに追加してください.このTraitは、認証されたユーザーのトークンと使用範囲を確認するためのモデルに補助関数を提供します.
    use HasApiTokens, Notifiable;
    

    次に、AuthServiceProviderのbootメソッドでPassport::routes登録トークンに関連するルーティングを呼び出します.
    Passport::routes();
    
    // Laravel   refresh_token       access_token     ,
    // refresh_token        access_token     ,      , access_token   ,  refresh_token    ,    refresh_token    access_token
    Passport::tokensExpireIn(now()->addDays(15)); // access_token     
    Passport::refreshTokensExpireIn(now()->addDays(60)); // refresh_token     
    

    最後に、プロファイルconfig/auth.phpで看守guardsを許可するapiのdriverオプションをpassportに変更します.この調整により、アプリケーションは、受信したAPIの要求を検証するときにPassportのTokenGuardを使用して処理できます.
    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
    
        'api' => [
            'driver' => 'passport',
            'provider' => 'users',
        ],
    ],
    
  • サードパーティサーバ(B局)(mushi.com)
  • 第一歩は、Aサイトにリンクを提供し、ユーザーがクリックするとBサイトにジャンプし、ユーザーデータをAサイトに使用することを許可する.次はAサイトがBサイトをジャンプする概略リンクです.
    https://b.com/oauth/authorize?
      response_type=code&
      client_id=CLIENT_ID&
      redirect_uri=CALLBACK_URL&
      scope=read
    

    コード実装:
    (1)ルーティング
    //     
    Route::view('/login', 'login');
    

    (2)ビューresources/views/login.blade.php
    //    /mushishi/login,      ,    
    <a href="/mushishi/login">    </a>
    

    (3)ルーティング
    CSRF攻撃方式http_build_query
    $clientId = 1;	//         client_id,       
    $clientSecret = 'T51YiRBezu2QZzmodyTGPKdNJZ4MpeiGPv5PmjiJ';	//         client_secret,       
    
    //              
    Route::get('/mushishi/login',
        function (\Illuminate\Http\Request $request) use ($clientId) {
        	// state            ,             。        csrf  ,           session    
            $request->session()->put('state', $state = Str::random(40));
    
            $query = http_build_query([
                'client_id' => $clientId,	//         client_id,       
                'redirect_uri' => 'http://www.mushi.com/auth/callback',	//         redirect,             ,                   ,       ,
                'response_type' => 'code',	//  
                'scope' => '*',	//        
                'state' => session('state'),
            ]);
    
    		//                      
            return redirect('http://mushishi.com/oauth/authorize?'.$query);
        });
    

    注意:ここに大きな穴があります!!!リダイレクトされたURL(redirect_uri)は、ジャンプしてwwwではなくhttp://mushishi.com/auth/callbackであれば、認証サーバ側が認証を確認してクライアントlaravelページにリダイレクトした後、http://mushishi.com/auth/callbackページにとどまることなく、先に入ってから302が発生し、http://mushishi.comページにリダイレクトする.だからredirect_uriの値は必ずwww(ローカルテストなのでドメイン名を買わなかったので、このような状況になった)
    第2ステップでは、ユーザがジャンプすると、Bサイトはユーザにログインを要求し、Aサイトへの権限付与に同意するかどうかを尋ねる.ユーザーが同意した場合(ここではこのステップを無視し、デフォルトでは同意した)Bサイトはredirect_に戻ります.uriパラメータで指定したURL(コールバックアドレス).ジャンプすると、次のように認証コードcodeが返されます.
    http://www.mushi.com/auth/callback?code=def50200bb1682081717ff3dec96188294458f971642950dedaf7f7eae&state=elXKJEW6EqmPbAZDnlppmNCE98Uw23HTutPY7poZ
    

    ステップ3では、Aサイトがライセンスコードを手に入れた後、バックエンドでBサイトにトークンを要求することができます.ルーティング'/auth/callback'
    //     ,   code,        token   
    Route::view('/auth/callback', 'auth_callback');
    

    コールバックページ
    ここではaxiosメソッドを用い,axios中国語ドキュメントを参照しurlのパラメータを取り出してerrorの有無を判断し,問題なく非同期ネットワーク要求を行い,パラメータpostをバックグラウンドにデータ交換を行う.
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        function GetRequest() {
            var url = location.search; //  url "?"     
            var theRequest = {};
            if (url.indexOf("?") !== -1) {
                var str = url.substr(1);
                strs = str.split("&");
                for (var i = 0; i < strs.length; i++) {
                    theRequest[strs[i].split("=")[0]] = decodeURI(strs[i].split("=")[1]);
                }
            }
            return theRequest;
        }
    
        //  
        var Request = GetRequest();
    
        if (Request['error']) {
            //        
            alert(Request['error']);
        }else
        {
            var code = Request['code'];
            var state = Request['state'];
    
            axios.post('/get/token', {
                params: {
                    code,
                    state
                }
            })
                .then(function (response) {
                    console.log(response.data);
                });
        }
    </script>
    

    axios.post使用例リファレンス:
    // post   
    let postData = { username: 'user1', password: '123' }
     
    axios.post('/login', postData)
        .then(response => {
            // post   ,response.data       
            // console.log()      ‘   ’    。
            console.log(response.data)
        })
        .catch(error => {
            //     
            console.log(error)
        })
    

    ルーティング'/get/token'
    Guzzleを使用してライセンスサーバとバックグラウンドデータのインタラクションを行う
    Route::post('/get/token', function (\Illuminate\Http\Request $request) use (
        $clientId,
        $clientSecret
    ) {
        // csrf     
        //     state
        $state = $request->session()->get('state');
        //      false,   , throw_if  
        throw_unless(
            strlen($state) > 0 && $state === $request->params['state'],
            InvalidArgumentException::class
        );
    
        $response
            = (new \GuzzleHttp\Client())->post('http://mushishi.com/oauth/token', [
            'form_params' => [
                'grant_type' => 'authorization_code',
                'client_id' => $clientId,
                'client_secret' => $clientSecret,
                'redirect_uri' => 'http://www.mushi.com/auth/callback',
                'code' => $request->params['code'],
            ],
        ]);
    
        return json_decode((string)$response->getBody(), true);
    });
    
  • アクセスのリフレッシュtoken
  • //    token
    Route::view('/refresh/page', 'refresh_page');
    
    Route::post('/refresh', function (\Illuminate\Http\Request $request) use (
        $clientId,
        $clientSecret
    ) {
        $http = new GuzzleHttp\Client;
        $response = $http->post('http://lishen.com/oauth/token', [
            'form_params' => [
                'grant_type' => 'refresh_token',
                'refresh_token' => $request->params['refresh_token'],
                'client_id' => $clientId,
                'client_secret' => $clientSecret,
            ],
        ]);
    
        return json_decode((string)$response->getBody(), true);
    });
    
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        axios.post('/refresh', {
            params: {
            	//     access_token
                refresh_token: "def502009e634dd59ac4dcd4843be50c3a7a6c76fe0c26a6a948d45b99e393cdf99d1a212a8752d0ce02f4cbc25008972b524336f23b60dfc4198e5413b7e43250126b0d1780afb85443edc1579870e823eedea4313448ffcbc" 
            }
        })
            .then(function (response) {
                console.log(response.data);
            });
    </script>