开发当中踩了不少坑,做个备忘。
代码环境:
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',
    ];
  | 
 
也就是:
- 你的oauth服务获取access_token的入口地址,如果你换了地址修改这里对应的设置即可。
 
- 你使用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->isReading($request) || $this->shouldPassThrough($request) || $this->tokensMatch($request)) {
            return $this->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->except as $except) {
            if ($request->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')
    <div class="row">
        {!! Form::open(['method' => 'POST','class'=>'form-horizontal', 'url'=> route('oauth.authorize.post',$params)]) !!}
        <div class="form-group">
            <dl class="dl-horizontal">
                <dt>Client Name</dt>
                <dd>{{$client->getName()}}</dd>
            </dl>
        </div>
        {!! 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'=>'approve', 'value'=>1, 'class'=>'btn btn-success']) !!}
        {!! Form::submit('Deny', ['name'=>'deny', 'value'=>1, 'class'=>'btn bg-danger']) !!}
        {!! Form::close() !!}
    </div>
@endsection
  | 
 
而对应的$params在controller中的设置:
1
2
3
4
  | 
        $authParams = Authorizer::getAuthCodeRequestParams();
        $formParams = array_except($authParams,'client');
        $formParams['client_id'] = $authParams['client']->getId();
        return View::make('oauth.authorization-form', ['params'=>$formParams,'client'=>$authParams['client']]);
  | 
 
最后在你被oauth保护的api接口中你就可以获得到对应的当前用户id了:
1
  | 
$uid = Authorizer::getResourceOwnerId();
  |