それらのいくつかは、セッションに基づいてのみ実装される単純なものであり、ACLなどの複雑なものもあります。 それぞれに長所と短所があります。 単純なシステムは理解しやすく使いやすいですが、特権の数と動的な変更の必要性が増加すると、対応する困難が発生しますが、ACLはかなり扱いにくく、柔軟性がなく理解しにくいものです。 長い間、両方のシステムを使用して、次の機能を持つ独自のアクセス制御システムを開発する必要があるという結論に達しました。
- 理解のしやすさと実行のしやすさ
- グループ/ロールの動的な共同作成およびロール/グループによるユーザーの移動
- ユーザーは、任意の数のグループ\ロールに所属できます
- 簡単、迅速、簡単なアクセス変更
- システム使用時のコードの最小化
- アクセス制御に関連するテーブルのサイズの最小化
- データベースクエリの数を最小限に抑える
アクセス共有システムに関する既存のトピックを読んでいると、「ACLがある場合に庭をフェンスで囲む理由」というコメントにしばしば出くわしました。 すぐに、ACLが私に合わないものに答えます。
- 理解困難
- ユーザーは同時に複数のグループに属することはできません
- 個々のユーザーへのアクセスを制限する必要がある場合、タブレットは膨張します(各ユーザーのIDを保存する必要があります)
- グループ内のユーザーの移動が困難
- キャッシングの欠如
- グループと権利を扱うために視覚化を行う必要性
- 自分のベストは体に近い(コードを維持しやすいという意味で)
それでは、理論から始めましょう。
テーブル構造
テーブルの説明
ユーザー -ここではすべてが簡単です。このテーブルはユーザーの保存に使用され、キーフィールドはid、lgn、pwdです。 このテーブルでは、id = 0の最初のエントリがデフォルトのユーザー、つまり訪問者になります。
グループはグループです(例:ユーザー、管理者、マネージャー)
ステータス -各グループはステータスで構成されます。 大まかに言って、各ユーザーは特定のステータスを持つ特定のグループに含まれています。 ユーザーは異なるステータスの複数のグループに属することができますが、単一のグループ内でユーザーが持つことができるステータスは1つだけです。 たとえば、次のグループのステータスはUser-visitor、active、newです。 管理者-アクティブ、スーパー管理者、削除済み; マネージャー-アクティブ、ブロック済み; 訪問者(id = 0)はユーザーグループに属し、訪問者ステータスになります。 各グループにはデフォルトのステータス-defstats_id-があり、他のステータスはデフォルトのステータスからアクセス権を継承でき、特定のオブジェクトに対する独自の権限を持っている場合があります。
Users_statuses-ユーザーとステータスをリンクするためのテーブル
オブジェクト -すべてのセキュリティオブジェクトを格納します(たとえば、セキュリティボタンは登録ボタンであり、訪問者のみが表示します)
objects_categories-このシステムを操作した最初の経験では、セキュリティオブジェクトは何らかの形で整理する必要があることが示されました。 このために、追加のテーブルが導入されました。このテーブルは、セキュリティオブジェクトの整理(表示時)にのみ使用されます。 たとえば、セキュリティオブジェクトには、そのようなカテゴリがあります-デフォルト-すべての新しいオブジェクトはこのカテゴリに分類され(管理者はオブジェクトを別のカテゴリに転送します)、ボタン-ボタンに関連付けられたすべてのオブジェクト、リンク-リンクに関連付けられたオブジェクト想像力と仕事が許す限り。
アクセスはシステムの重要なテーブルです。 セキュリティオブジェクト、ステータス、特権をリンクします。 この表には、要求されたアクセスのタイプを区別するための5つの主要な列があります。
c-作成するアクセス権(たとえば、新しいブログエントリを作成するアクセス権を確認する)
u-変更へのアクセス(たとえば、ブログエントリを編集するためのアクセスの確認)
r-読み取りアクセス(たとえば、ブログエントリを読み取るためのアクセスチェック)
d-削除するアクセス(たとえば、ブログエントリを削除するアクセスを確認する)
l-リストを表示するアクセス権(たとえば、すべてのブログエントリを表示するアクセス権を確認する、またはすべてのユーザーを表示する)
これらのフィールドは次の値を取ることができます。
0-アクセスが拒否されました
1-アクセスは所有者のみに許可されます
2-すべてへのアクセスが許可されます
この表では、各グループのデフォルトステータスについて説明します。他のすべてのステータスは、グループのデフォルトステータスからアクセス権を継承します。 ステータスに独自のアクセス権が必要な場合(たとえば、ブロックされたステータスには、新しいブログエントリを追加できないことを除いて、アクティブステータスのデフォルト権限がすべてあります)、対応するstatus_idの行がアクセステーブルに追加されます。
システムの仕組み
システムのロジックを分析しましょう。 アクセス検証はgetAccess関数(オブジェクト、アクセス、所有者)によって実行されます。
関数パラメーター
object-アクセスが要求されるオブジェクトの名前。
アクセス-アクセスのタイプ(with、u、r、d、l);
ownersはオプションのパラメーターで、タイプint-ユーザーのIDの値、またはユーザーIDのリストのいずれかです。 現時点では、このオプションのパラメータは無人のままにしてください-後でシステムによってどのように使用されるかが明らかになります。
現在のユーザーのIDを常に知っていることを予約する価値があります-権限のないユーザーのためにセッションに保存します-これはid = 0のユーザーであり、ステータス訪問者を持つユーザーグループで構成されます。
そして、システムはどのように機能しますか。
1.オブジェクトテーブルにこの名前のオブジェクトが存在するかどうかを確認し、存在しない場合は、そのIDを作成して記憶します
2.セッションから現在のユーザーのIDを取得し、このユーザーのステータスを確認します(実装では、現在のユーザーのステータスのIDはセッションに保存されます)
3.目的のアクセスのタイプに応じて、システム設定(弱い、強い)に応じて、対応するステータスの目的のアクセスフィールド(r、c、d、u、l)の最大値または最小値を取得します。 ステータスがアクセステーブルにない場合、対応するグループのdef_statusを取得します。
4.考えられる結果-0アクセスは拒否され、2-すべてのユーザーにアクセスが許可され、1-所有者のみにアクセスが許可されます。 所有者のみにアクセスが許可されている場合:
checkAccess関数の最後のパラメーターが空であるかどうかを確認します。所有者、空の場合はアクセスを許可/無効にします(オプション)。 空でなく、int型の場合は、セッションのユーザーID(現在のユーザー)で確認します。 それらが等しい場合、アクセスは許可され、そうでない場合は拒否されます。 ownersパラメーターが配列の場合、現在のユーザーのID(セッションから)がこの配列(in_array)に入るかどうかを確認します。エントリがある場合は、アクセスが禁止されます。
これが作業のアルゴリズム全体です。
使用例
ユーザーcreator_idが作成したブログ投稿があるとします。 一部のユーザーには「レコードの編集」ボタンを表示し、他のユーザーには非表示にします。
if checkcheck( 'BlogPost'、 'u'、$ post ['creator_id'])){echo BUTTON}
たとえば、ユーザーは2つのグループに属し、ステータスには更新に関する次の権限があります。
ユーザー\アクティブ-1;
Admin \ active -2;
システム設定に従って、max(またはmin)を取得し、結果を返します
実装
cakePHPシステムは、リクエストキャッシングをコンポーネントとして実装されています。 アクセス、ステータス、グループ、オブジェクトの4つのメインタブレットをキャッシュするだけで十分です。そのため、アクセスをチェックするときに単一の要求がありません。 (これらのプレートは小さいため、多くのスペースを占有せず、完全にキャッシュすることができます。)純粋にシステム制御を視覚化するために、いくつかのコントローラー/モデル/ビューも使用されました。
タブレットをキャッシュにロードします(cakeを使用すると、fileとmemcachedの2種類のキャッシュを使用できます。したがって、サーバー上にキャッシュがなくてはなりません)。
便宜上、配列が次のようになるようにキャッシュする必要があります。
アクセス
パーミッション[status_id] [object_id] [access_type] =パーミッション;
例:
パーミッション[status_id] [object_id] [s] = 1;
パーミッション[status_id] [object_id] [d] = 1;
パーミッション[status_id] [object_id] [r] = 1;
許可[status_id] [object_id] [u] = 1;
許可[status_id] [object_id] [l] = 1;
ステータス
statuses [status_id] = group_id、つまり、各ステータスはどのグループにあるかを保存します
グループ
groups [group_id] = def_status_idつまり、各グループはデフォルトのステータスを保存します
オブジェクト
オブジェクト[object_name] = object_id
主な機能の実装:
function getAccess($objName = "" ,$accessType = "r" ,$authorID=NULL) {
$objectID = 0;
$isAccess = true ;
/*Getting User ID*/
if ($ this ->Session->check( 'loggedUser' )) {
$userSession = $ this ->Session->read( 'loggedUser' );
$userID = $userSession[ 'id' ];
} else {
$userID = VISITOR_USER;
}
/*Check access
* 0 - deny;
* 1 - allow only for author;
* 2 - allow for ALL;
*/
$isAccess = $ this ->__returnAccess($objName,$accessType);
if ($isAccess == 2){
$isAccess = true ;
} elseif($isAccess == 1) {
/*Check author id*/
if (is_array($authorID) && in_array($userID,$authorID)){
$isAccess = true ;
} elseif($userID==$authorID){
$isAccess = true ;
} else {
$isAccess = false ;
}
/*EOF Checking author id*/
} else {
$isAccess = false ;
}
return $isAccess;
}
, 1,0 2
function __returnAccess($objName = "" ,$accessType = "r" ){
if (!$ this ->model){
$ this ->__initModel();
}
/*Getting User ID*/
if ($ this ->Session->check( 'loggedUser' )) {
$userSession = $ this ->Session->read( 'loggedUser' );
$userID = $userSession[ 'id' ];
} else {
$userID = VISITOR_USER;
}
/*Getting user statuses*/
if ($ this ->Session->check( 'loggedUserStatuses' )) {
$userStatuses = $ this ->Session->read( 'loggedUserStatuses' );
} else {
$userStatuses = $ this ->model->query( "SELECT user_id, status_id FROM " .$ this ->model->tablePrefix. "users_statuses AS users_statuses WHERE user_id=" .$userID);
$ this ->Session->write( 'loggedUserStatuses' ,$userStatuses);
}
$objectID = $ this ->getObjIdByName($objName);
if (!$objectID) {
/*Create new object*/
$objectID = $ this ->__createNewObject($objName);
}
//Permissions
$permissions = Cache::read( 'permissions' );
if (empty($permissions)) {
$ this ->loadobjToCache();
$permissions = Cache::read( 'permissions' );
}
//Groups
$groups = Cache::read( 'groups' );
if (empty($groups)) {
$ this ->loadobjToCache();
$groups = Cache::read( 'groups' );
}
//Statuses
$statuses = Cache::read( 'statuses' );
if (empty($statuses)) {
$ this ->loadobjToCache();
$statuses = Cache::read( 'statuses' );
}
$isAccess = 0;
foreach ($userStatuses as $userStat) {
if (isset($permissions[$userStat[ 'users_statuses' ][ 'status_id' ]][$objectID] [$accessType])) {
if (intval($permissions[$userStat[ 'users_statuses' ][ 'status_id' ]][$objectID][$accessType])>$isAccess) {
$isAccess = intval($permissions[$userStat[ 'users_statuses' ][ 'status_id' ]][$objectID][$accessType]);
}
} else {
/*Getting group ID*/
$def_status_id = $groups[$statuses[$userStat[ 'users_statuses' ][ 'status_id' ]]];
if (!isset($def_status_id)) {
$isAccess = 0;
} else {
if (intval($permissions[$def_status_id][$objectID][$accessType])>$isAccess) {
$isAccess = intval($permissions[$def_status_id][$objectID][$accessType]);
}
}
}
} /*EOF foreach*/
return $isAccess;
}
* This source code was highlighted with Source Code Highlighter .
実際にはそれだけです。コンポーネント自体が必要な場合は、提供できます。 アクセス制御の視覚化を次の図に示します。
グループ、ステータス、アクセスタイプは縦に表示されます。 デフォルトのステータスは赤で強調表示されます。 水平方向にはオブジェクトがあり、そのすぐ下に転送するカテゴリのリストがあります。 チェックボックス-このステータスのこのオブジェクトへのアクセスが、このグループのデフォルトステータスと同じであることを示します(それらに対しては、設定します-//-)。 AJAXを使用する男性へのアクセス、つまり、再起動なし(キャッシュの更新中)