laravel的oauth2-server相关开发的备忘

开发当中踩了不少坑,做个备忘。

代码环境:

laravel 5.1

oauth2-server服务组件:

lucadegasperi/oauth2-server-laravel 也就是 thephpleague/oauth2-server 的laravel 包装版本

目前插件的版本是~4.1

需要注意的点是:

一般而言最常见的应用场景是grant_type为authorization_code的情景,

thephpleague的oauth2-server要求的数据提交必须是POST数据编码方式是application/x-www-form-urlencoded,默认情况下如果你用的是curl组件会以multipart/form-data模式编码提交的post数据,所以后端提交请求的时候注意一下, 如果你用的是curl,需要设置:

1
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));

如果你用的是GuzzleHttp的组件:参考官方的说明

1
2
3
4
5
6
7
8
9
$response = $client->post('http://httpbin.org/post', [
'form_params' => [
'field_name' => 'abc',
'other_field' => '123',
'nested_field' => [
'nested' => 'hello'
]
]
]);

其他的基础配置设定,插件作者的wiki中已经有了说明,我这里做了一些自己的设定:

  • 不想关闭全局的csrf保护咋办? 如果你是直接安装的laravel 5.1版不要关闭全局$middleware的csrf:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    protected $middleware = array(
    \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
    \LucaDegasperi\OAuth2Server\Middleware\OAuthExceptionHandlerMiddleware::class,
    \App\Http\Middleware\EncryptCookies::class,
    \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
    \Illuminate\Session\Middleware\StartSession::class,
    \Illuminate\View\Middleware\ShareErrorsFromSession::class,
    \App\Http\Middleware\VerifyCsrfToken::class, // 不要关闭
    );

按wiki中的说明添加$routeMiddleware中的设定:

1
2
3
4
5
6
7
8
9
protected $routeMiddleware = [
'csrf' => \App\Http\Middleware\VerifyCsrfToken::class, // 添加 csrf配置
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'oauth' => \LucaDegasperi\OAuth2Server\Middleware\OAuthMiddleware::class,
'oauth-owner' => \LucaDegasperi\OAuth2Server\Middleware\OAuthOwnerMiddleware::class,
'check-authorization-params' => \LucaDegasperi\OAuth2Server\Middleware\CheckAuthCodeRequestMiddleware::class,
];

在你的\App\Http\Middleware\VerifyCsrfToken类中的$except变量添加:

1
2
3
4
5
6
protected $except = [
//
'api',
'api/*',
'oauth/access_token',
];

也就是:

  1. 你的oauth服务获取access_token的入口地址,如果你换了地址修改这里对应的设置即可。
  2. 你使用oauth中间件保护的服务接口也不需要csrf做多余的防护,在此排除掉 api/* 这对应的前缀即可 如果是5.0之类的升级上来的,VerifyCsrfToken可能还是老的写法,不支持$except,自己改造一下符合新版规范:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?php namespace App\Http\Middleware;
use Closure;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
use Illuminate\Session\TokenMismatchException;
class VerifyCsrfToken extends BaseVerifier {
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected $except = [
//
'api/*',
'oauth/access_token',
];
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($this-&gt;isReading($request) || $this-&gt;shouldPassThrough($request) || $this-&gt;tokensMatch($request)) {
return $this-&gt;addCookieToResponse($request, $next($request));
}
throw new TokenMismatchException;
//return parent::handle($request, $next);
}
/**
* Determine if the request has a URI that should pass through CSRF verification.
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
protected function shouldPassThrough($request)
{
foreach ($this-&gt;except as $except) {
if ($request-&gt;is($except)) {
return true;
}
}
return false;
}
}

  • 本地登录授权的页面(View::make('oauth.authorization-form'))该怎么写? 原来官方的wiki中没有,放狗找了一圈的issue list才凑合着写了一个放了上去,作者插件的wiki里我已改过了:

注意提交的form原先GET请求中的querystring是需要一并post的 这个坑要注意一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@extends('app')
@section('content')
&lt;div class="row"&gt;
{!! Form::open(['method' =&gt; 'POST','class'=&gt;'form-horizontal', 'url'=&gt; route('oauth.authorize.post',$params)]) !!}
&lt;div class="form-group"&gt;
&lt;dl class="dl-horizontal"&gt;
&lt;dt&gt;Client Name&lt;/dt&gt;
&lt;dd&gt;{{$client-&gt;getName()}}&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
{!! Form::hidden('client_id', $params['client_id']) !!}
{!! Form::hidden('redirect_uri', $params['redirect_uri']) !!}
{!! Form::hidden('response_type', $params['response_type']) !!}
{!! Form::hidden('state', $params['state']) !!}
{!! Form::submit('Approve', ['name'=&gt;'approve', 'value'=&gt;1, 'class'=&gt;'btn btn-success']) !!}
{!! Form::submit('Deny', ['name'=&gt;'deny', 'value'=&gt;1, 'class'=&gt;'btn bg-danger']) !!}
{!! Form::close() !!}
&lt;/div&gt;
@endsection

而对应的$params在controller中的设置:

1
2
3
4
$authParams = Authorizer::getAuthCodeRequestParams();
$formParams = array_except($authParams,'client');
$formParams['client_id'] = $authParams['client']-&gt;getId();
return View::make('oauth.authorization-form', ['params'=&gt;$formParams,'client'=&gt;$authParams['client']]);

最后在你被oauth保护的api接口中你就可以获得到对应的当前用户id了:

1
$uid = Authorizer::getResourceOwnerId();

坚持原创技术分享,您的支持将鼓励我继续创作!