- Prepare two servers, a third-party server simulation, a mock authorization server
- The authorization server (micro-channel)
Passport Composer installed by the package manager:
which laravel / passport provide OAuth authentication service
composer require laravel/passport
And the token is created to store the client data sheet:
php artisan migrate
Next, run the passport: install command to create an encryption key for generating the security access token, at the same time, this command will create a "personal visit" to generate an access token client (Personal Access Client) and "password authentication" The client (password Grant client):
php artisan passport:install
// 或者使用
php artisan passport:keys // 加密生成的 access_token
php artisan passport:client // 创建自定义客户端,生成client ID 和 client secret
PS: access_token generated position --storage / oauth-private.key
After the above command is executed, please Laravel \ Passport \ HasApiTokens Trait added to the App \ User model, this model provides Trait will give you some auxiliary functions for checking the authenticated user's token and use of:
use HasApiTokens, Notifiable;
Next, call the Passport :: routes registered token associated routing method in the boot of AuthServiceProvider
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 过期时间
Finally, the configuration file config / auth.php an authorized guard guards of the driver api option to passport. This adjustment will make your application in TokenGuard using the Passport when validating incoming request to process API:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
- Third-party server (B station)
The first step, A website provides a link, it will jump to the B site after the user clicks, the user authorization data to the A-site uses. Here is a schematic Jump A site B links to site.
https://b.com/oauth/authorize?
response_type=code&
client_id=CLIENT_ID&
redirect_uri=CALLBACK_URL&
scope=read
Code:
(1) Routing
// 登录页面
Route::view('/login', 'login');
(2) Viewresources/views/login.blade.php
// 跳转到/mushishi/login,根据示意链接,发送链接
<a href="/mushishi/login">微信登陆</a>
(3) routing
On CSRF attack
http_build_query
$clientId = 1; // 授权服务器生成的client_id,要与数据库一致
$clientSecret = 'T51YiRBezu2QZzmodyTGPKdNJZ4MpeiGPv5PmjiJ'; // 授权服务器生成的client_secret,要与数据库一致
Route::get('/lishen/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.mushishi.com/auth/callback', // 授权服务器生成的redirect,要与数据库一致
'response_type' => 'code', //固定
'scope' => '*', // 应用授权作用域
'state' => session('state'),
]);
return redirect('http://lishen.com/oauth/authorize?'.$query);
});
Note: here encountered a pit ! ! ! Redirection URL (redirect_uri) If the jump without the www, but http://mushishi.com/auth/callback
the authorization server-side redirect to authorize and confirm the client laravel page, and not stay in http://mushishi.com/auth/callback
the page, but first into the occurrence of 302 and redirected to the http://mushishi.com
page . So redirect_uri value must be with the www (specific reasons for the trouble I was chicken dishes, they did not know, know, leave a message ~ ~)
The second step, after the user to jump, B sites require users to log in and then asked if agreed to give the A site license. The user agrees, then the site will jump back to the B redirect_uri parameter specifies the URL. Jump, returns an authorization code code, like this below.
http://www.mushi.com/auth/callback?code=def50200bb1682081717ff3dec96188294458f971642950dedaf7f7eae&state=elXKJEW6EqmPbAZDnlppmNCE98Uw23HTutPY7poZ
The third step, A to get the website after the authorization code, can be at a rear end, a request token to the B-site.
Routing '/ auth / callback'
// 回调地址,获取 code,并随后发出获取 token 请求
Route::view('/auth/callback', 'auth_callback');
Callback page
Axios method used here, reference axios Chinese document after, url parameters in determining whether to remove the error, no problem asynchronous network requests, to the back post parameters for data exchange
<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 Use Case Reference:
// 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)
})
Routing '/ get / token'
Use Guzzle and authorization server for background data exchange
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);
});
- Refresh access_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>