开发当中踩了不少坑,做个备忘。
代码环境:
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
10
| $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();
|