先建立一個yii2專案,然後安裝這個extension。
底下為Yii2設定的部份,是設定一個 module以及在bootstrap階段就執行oauth2 module,和extension的說明有些不太一樣。
'bootstrap' => ['oauth2'],
'components'=>[
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
'POST oauth2/<action:(.+)>' => 'oauth2/rest/<action>',
],
],
],
'modules' => [
'oauth2' => [
'class' => 'filsh\yii2\oauth2server\Module',
'tokenParamName' => 'access_token',
'tokenAccessLifetime' => 3600 * 24,
'storageMap' => [
'user_credentials' => 'app\models\OauthUsers',
'user_claims'=> 'app\models\OauthUsers',
],
'grantTypes' => [
'user_credentials' => [
'class' => 'OAuth2\GrantType\UserCredentials',
],
'authorization_code' => [
'class' => 'OAuth2\GrantType\AuthorizationCode',
],
'refresh_token' => [
'class' => 'OAuth2\GrantType\RefreshToken',
'always_issue_new_refresh_token' => true
]
]
]
]
storageMap裡的user_credentials和user_claims都是指向 app\models\OauthUsers ,app\models\OauthUsers是對應專案裡使用者資料表的ActiveRecord,user_credentials是用來認證使用者身份,需在app\models\OauthUsers實作OAuth2\Storage\UserCredentialsInterface界面;user_claims是用來取得使用者資料,需在app\models\OauthUsers實作 OAuth2\OpenID\Storage\UserClaimsInterface界面。
grantTypes裡的 authorization_code,是取得code之後要交換access_token用的,原始的extension說明裡沒有提到,需加上才能正確運作。
'authorization_code' => [
'class' => 'OAuth2\GrantType\AuthorizationCode',
],
設定完成之後,這個套件已經提供資料表結構可供匯入,請在命令列執行
yii migrate --migrationPath=@vendor/hosannahighertech/yii2-oauth2-server/migrations
匯入的資料表有這些:
oauth_access_tokens - 儲存access token
oauth_authorization_codes 儲存authorize code
oauth_clients - 儲存client
oauth_jwt - jwt認證資料
oauth_public_keys -
oauth_refresh_tokens -
oauth_scopes - 儲存 scope
oauth_users - 儲存使用者資料
在串接facebook登入或是google登入時,我們都需要先申請appid (或client_id) 和secret,同時要設定redirect_uri, 這個資料就存放在 oauth_clients,設定的界面我們需要自己完成,但測試時可以直接從資料庫修改資料即可。可以直接利用預設提供的testclient, testpass作為client id及client secret,也可以另外新增記錄,grant_types是認證的方式,你果你不確定這是什麼,新增記錄時最好讓它跟第一筆記錄裡的內容一樣,以免影響你實作。
oauth_users是使用者資料表,登入的使用者帳密就是放在這個資料表,在實際登入前,請先增加一筆你要登入使用的帳號和密碼。預設的主鍵是username,我自己再新增一個欄位叫id,改主鍵為id,如果沒 改,後面利用access token取得使用者資料的欄位就要改。
實作Oauth2 Server
接下來Service端的實作有三個項目要做,分別是認證、交換access_token,以及取得使用者資料,認證和交換access_token可以規劃在同一個controller實作,而取得使用者資料因為要利用取得的access_token來認證身份,會利用yii\filters\auth\CompositeAuth這個controller filter來認證身份,建議是另設controller來實作。
認證和交換access token的實作如下:
use Yii;
use yii\helpers\ArrayHelper;
use yii\helpers\VarDumper;
use yii\web\Controller;
use yii\web\Response;
use app\models\LoginForm;
class SiteController extends Controller
{
public $enableCsrfValidation = false;
/**
* Login action.
*
* @return Response|string
*/
public function actionLogin()
{
if (!Yii::$app->user->isGuest) {
return $this->goHome();
}
$model = new LoginForm();
if ($model->load(Yii::$app->request->post()) && $model->login()) {
return $this->redirect(ArrayHelper::merge(['site/authorize'],Yii::$app->request->queryParams)); //登入完成後要重新導向authorize頁面,網址參數也一併帶過去。
}
$model->password = '';
return $this->render('login', [
'model' => $model,
]);
}
public function actionAuthorize()
{
if (Yii::$app->getUser()->getIsGuest())
return $this->redirect(ArrayHelper::merge(['login'],Yii::$app->request->queryParams)); //未登入使用者需先登入,帶過來的網址參數也要一併帶過去。
/** @var $module \filsh\yii2\oauth2server\Module */
$module = Yii::$app->getModule('oauth2');
/** @var \OAuth2\Response $response */
$response = $module->getServer()->handleAuthorizeRequest(null,null,!Yii::$app->getUser()->getIsGuest(), Yii::$app->getUser()->getId());
$headers = $response->getHttpHeaders();
return $this->redirect($headers['Location']);
}
public function actionToken()
{
/** @var $module \filsh\yii2\oauth2server\Module */
$module = Yii::$app->getModule('oauth2');
/** @var \OAuth2\Response $response */
$response = $module->getServer()->handleTokenRequest(null,null,!Yii::$app->getUser()->getIsGuest(), Yii::$app->getUser()->getId());
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
Yii::$app->response->content = $response->getResponseBody();
return Yii::$app->response;
}
}
接下來要做可取得使用者資料的Api Controller
use yii\filters\auth\HttpBearerAuth;
use yii\filters\auth\QueryParamAuth;
class ApiController extends \yii\rest\Controller {
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['authenticator'] = ArrayHelper::merge($behaviors['authenticator'], [
'authMethods' => [
['class' => HttpBearerAuth::className()],
['class' => QueryParamAuth::className(), 'tokenParam' => 'access_token'],
]]);
\Yii::debug('behaviors:'.VarDumper::export($behaviors));
return $behaviors;
}
public function actionUserInfo(){
$access_token = \Yii::$app->request->get("access_token");//如果用Yii預設的傳送token方式,是利用get的方式。
$tokenModel = \app\models\OauthAccessTokens::find()->where(['access_token'=>$access_token])->one();
$user = \app\models\OauthUsers::find()->where(['id'=>$tokenModel->user_id])->one();
return $user->toArray();//實際上這裡可能要稍微處理一下,因為整個user裡面的資料可能有一些敏感不應輸出的內容,像是密碼。
}
}
如果你的urlManager有照前述的config的設定
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
],
假設你的網址是http://myauth.server
認證的網址是: http://myauth.server/site/authorize
交換access token的網址是: http://myauth.server/site/token
取得使用者資料的網址是: http://myauth.server/api/user-info
如果沒有設定urlManager,那網址應該會變成
認證的網址是: http://myauth.server/index.php?r=site/authorize
交換access token的網址是: http://myauth.server/index.php?r=site/token
取得使用者資料的網址是: http://myauth.server/index.php?r=api/user-info
以上是稍後實作client認證時會用到的網址。
實作Oauth2 client
client的部份請另建yii2專案。
Yii2已經將大部份的工作都做了,實作時只要新增一個繼承 yii\authclient\OAuth2 的類別即可,例如:
use yii\authclient\OAuth2;
class MyAuthClient extends Oauth2
{
public $authUrl = 'http://myauth.server/site/authorize';
public $apiBaseUrl = 'http://myauth.server/api';
public $tokenUrl = 'http://myauth.server/site/token';
public $scope = 'openid'; //如果要利用套件自帶的api取得使用者資料,這裡請一定要填openid
protected function initUserAttributes()
{
//$token = $this->getAccessToken();
//$headers = ['Authorization' => 'Bearer '.$token->token];
$userInfo = $this->api('user-info', 'POST', [], $headers);
return $userInfo;
}
}
另外client端的設定如下:
'components'=>[
'authClientCollection' => [
'class' => 'yii\authclient\Collection',
'clients' => [
'my-auth-client' => [
'class' => 'app\components\authclient\MyAuthClient',
'clientId' => 'testclient',
'clientSecret' => 'testpass',
'returnUrl' => 'http://myauth.client/site/auth?authclient=my-auth-client',
],
// etc.
],
],
],
上述的 clientId, clientSecret和returnUrl請務必設在service端的 oauth_clients 資料表裡。
接下來在SiteController裡設定Auth Action
class SiteController extends Controller
{
public function actions()
{
return [
'auth' => [
'class' => 'yii\authclient\AuthAction',
'successCallback' => [$this, 'authSuccess'],
],
];
}
public function authSuccess(\yii\authclient\OAuth2 $client){
$userAttrs = $client->getUserAttributes();
//todo: 實作登入細節
}
}
最後就是將登入按鈕做出來,在登入頁的view裡呼叫下列敘述即可:
<?= yii\authclient\widgets\AuthChoice::widget([
'baseAuthUrl' => ['site/auth']
]); ?>