ride / lib-security
Ride框架的安全库
Requires
- ride/lib-encryption: ^1.0.0
- ride/lib-event: ^1.0.0
- ride/lib-http: ^1.0.0
- ride/lib-log: ^1.0.0
README
PHP Ride框架的安全抽象库。
该库实现基于角色的访问控制。更多关于此的信息请参阅 维基百科。
本库包含的内容
SecurityModel
SecurityModel 接口是安全实现的数据源的代理。它提供用户、角色和权限。
User
User 接口表示一个用户,该用户可以向应用程序进行身份验证。您可以将角色附加到用户,以便授予他们对应用程序特定部分的访问权限。User 接口由安全模型实现。
Role
Role 接口表示一组允许通过授权权限和允许路径执行的操作。通过将角色附加到用户,您授予用户对应用程序特定部分的访问权限。Role 接口由安全模型实现。
Permission
Permission 接口用于授予或拒绝单个操作。
为了保护应用程序的某个部分,您应在应用程序代码中始终检查授予的权限。不要检查当前用户是否是特定用户,或者当前用户是否具有特定角色。这将违反安全模型的灵活性。
Permission 接口由安全模型实现。
Authenticator
Authenticator 接口决定身份验证机制并保持当前用户的会话状态。
GenericAuthenticator
GenericAuthenticator 提供默认或通用的身份验证器实现。
您可以启用唯一会话。当用户从另一个客户端登录时,此功能用于使原始客户端注销。
它还支持切换用户功能。
ChainAuthenticator
您可以使用 ChainAuthenticator 将不同的身份验证器链接在一起。同时提供不同的身份验证机制。
PathMatcher
PathMatcher 负责将路径正则表达式或规则与提供的路径和方法匹配。
GenericPathMatcher
GenericPathMatcher 提供默认或通用的路径匹配器实现。
您可以在规则中使用以下特殊标记
- *: 匹配单个路径标记
- **: 匹配所有内容
- !: 在路径前缀加上感叹号以进行负(不是)匹配
您还可以在方括号内定义一个或多个方法。
所有规则都将进行检查,并按照提供的顺序执行。这对于 not 函数是必需的。
例如,假设以下规则
/admin**
/sites**
!/sites/my-site/pages/*/content [GET]
这些规则将匹配以 /admin 和 /sites 开头的所有请求,除了对 my-site 每页内容的 GET 请求。
Voter
Voter 接口用于检查授予的权限和允许的路径。
ModelVoter
ModelVoter 通过安全模型执行其检查。它使用当前用户及其角色来获取授予的权限和允许的路径。
ChainVoter
ChainVoter 用于将不同的投票者链接在一起。这可以用于捕获特殊情况或某些边缘情况。
您有 3 种不同的策略
- 肯定:此策略在任一投票者授予访问权限后立即授予访问权限。这是默认策略。
- 共识:此策略在大多数投票者授予访问权限时授予访问权限。
- 一致:此策略在所有投票者都授予访问权限时授予访问权限。
安全管理器
安全管理器类是此库的代理。它将其他组件粘合在一起,形成一个易于使用的接口。使用此类的实例来处理您的安全。
代码示例
查看此代码示例以了解此库的一些可能性。
注意:此示例中使用的某些类来自ride/lib-security-generic、ride/web-security或ride/web-security-generic。
<?php use ride\library\encryption\hash\GenericHash; use ride\library\event\EventManager; use ride\library\http\Request; use ride\library\security\authenticator\ChainAuthenticator; use ride\library\security\authenticator\GenericAuthenticator; use ride\library\security\exception\EmailAuthenticationException; use ride\library\security\exception\InactiveAuthenticationException; use ride\library\security\exception\PasswordAuthenticationException; use ride\library\security\exception\UnauthorizedException; use ride\library\security\exception\UsernameAuthenticationException; use ride\library\security\exception\UserNotFoundException; use ride\library\security\exception\UserSwitchException; use ride\library\security\matcher\GenericPathMatcher; use ride\library\security\model\generic\GenericSecurityModel; use ride\library\security\model\ChainSecurityModel; use ride\library\security\voter\ChainVoter; use ride\library\security\voter\ModelVoter; use ride\library\security\SecurityManager; use ride\library\system\file\File; use ride\web\security\authenticator\io\SessionAuthenticatorIO; use ride\web\security\authenticator\HttpAuthenticator; function createSecurityManager(EventManager $eventManager, File $fileSecurityModel) { // first create the default authenticator $sessionAuthenticatorIO = new SessionAuthenticatorIO(); // used to store values in the session $salt = 'a-random-string'; // salt for value generation $timeout = 1800; // time in seconds $isUnique = false; // allow only 1 client per user at the same time $genericAuthenticator = new GenericAuthenticator($sessionAuthenticatorIO, $salt, $timeout, $isUnique); // we use a chain so we can add other implementations like HTTP authentication or OAuth $chainAuthenticator = new ChainAuthenticator(); $chainAuthenticator->addAuthenticator($genericAuthenticator); // let's add the HTTP authenticator to the chain (optional) $realm = 'My Site'; // the title of the login box $httpAuthenticator = new HttpAuthenticator($sessionAuthenticatorIO, $realm, $eventManager); $chainAuthenticator->addAuthenticator($httpAuthenticator); // decide the hash algorithm $hashAlgorithm = new GenericHash('sha512'); // initialize the voter $genericPathMatcher = new GenericPathMatcher(); $modelVoter = new ModelVoter($genericPathMatcher); // again a chain to add other voters if needed $chainVoter = new ChainVoter(); $chainVoter->addVoter($modelVoter); // now, we create the security model // as example we use a file based security model which is good for a small user base. $xmlSecurityModelIO = new XmlSecurityModelIO($fileSecurityModel); $genericSecurityModel = new GenericSecurityModel($xmlSecurityModelIO, $eventManager, $hashAlgorithm); // a chain, you guessed it ... $chainSecurityModel = new ChainSecurityModel(); $chainSecurityModel->addSecurityModel($genericSecurityModel); // throw it all together in the security manager $securityManager = new SecurityManager($chainAuthenticator, $eventManager); $securityManager->setHashAlgorithm($hashAlgorithm); $securityManager->setSecurityModel($chainSecurityModel); $securityManager->setVoter($chainVoter); return $securityManager; } function manageSecurityModel(SecurityManager $securityManager) { $securityModel = $securityManager->getSecurityModel(); // set the globally secured paths $securedPaths = array( '/admin**', '/sites**', ); $securityModel->setSecuredPaths($securedPaths); // create some roles $administratorRole = $securityModel->createRole(); $administratorRole->setName('Administrator'); $administratorRole->setWeight(99); $contentManagerRole = $securityModel->createRole(); $contentManagerRole->setName('Content Manager'); $contentManagerRole->setWeight(50); $securityModel->saveRole($adminstratorRole); $securityModel->saveRole($contentManagerRole); // allow paths and grant permissions for the roles $securityModel->setAllowedPathsToRole($administratorRole, array('**')); $securityModel->setAllowedPathsToRole($contentManagerRole, array('/sites**')); $securityModel->setGrantedPermissionsToRole($administratorRole, array('security.switch')); $securityModel->setGrantedPermissionsToRole($contentManagerRole, array('security.switch')); // create users $administratorUser = $securityModel->createUser(); $administratorUser->setUsername('admin'); $administratorUser->setPassword('secure password'); $administratorUser->setIsActive(true); $contentManagerUser = $securityModel->createUser(); $contentManagerUser->setUsername('cm'); $contentManagerUser->setPassword('secure password'); $contentManagerUser->setIsActive(true); $securityModel->saveUser($administratorUser); $securityModel->saveUser($contentManagerUser); // assign roles to the users $securityModel->setRolesToUser($administratorUser, array($administratorRole)); $securityModel->setRolesToUser($contentManagerUser, array($contentManagerRole)); // create a super user $superUser = $securityModel->createUser(); $superUser->setUsername('root'); $superUser->setPassword('secure password'); $superUser->setIsActive(true); $superUser->setIsSuperUser(true); $securityModel->saveUser($superUser); // create a regular user with all properties $regularUser = $securityModel->createUser(); $regularUser->setDisplayName('John Doe'); $regularUser->setUsername('john'); $regularUser->setPassword('secure password'); $regularUser->setEmail('john@doe.com'); $regularUser->setIsEmailConfirmed(true); $regularUser->setImage('upload/users/john-doe-avatar.png'); $regularUser->setPreference('locale', 'en_GB'); // any custom preference $regularUser->setIsActive(true); $securityModel->saveUser($regularUser); // delete it again $securityModel->deleteUser($regularUser); // find some users $user = $securityModel->getUserById(1); $user = $securityModel->getUserByUsername('admin'); $user = $securityModel->getUserByEmail('john@doe.com'); $options = array( // 'query' => 'adm', // 'username' => 'adm', // 'email' => 'adm', 'page' => 1, 'limit' => 20, ); $users = $securityModel->getUsers($options); $numUsers = $securityModel->countUsers($options); // the same for roles $role = $securityModel->getRoleById(1); $role = $securityModel->getRoleByName('Content Manager'); $options = array( // 'query' => 'content', // 'name' => 'content', 'page' => 1, 'limit' => 20, ); $roles = $securityModel->getRoles($options); $numRoles = $securityModel->countRoles($options); // obtain all permissions $permissions = $securityModel->getPermissions(); } function handleSecurity(SecurityManager $securityManager, Request $request) { // set the request to the security manager to detect the user from previous requests $securityManager->setRequest($request); // get the current user $user = $securityManager->getUser(); if (!$user) { // no user logged in from a previous request try { $securityManager->login('admin', 'secure password'); } catch (UsernameAuthenticationException $exception) { // invalid username } catch (PasswordAuthenticationException $exception) { // invalid password } catch (EmailAuthenticationException $exception) { // email is not confirmed } catch (InactiveAuthenticationException $exception) { // user is inactive } } // perform some checks if ($securityManager->isPermissionGranted('my.permission')) { // user is granted } else { // user is denied } if ($securityManager->isPathAllowed('/admin/system', 'GET')) { // user is allowed } else { // user is denied } if ($securityManager->isUrlAllowed('https://www.foo.bar/admin/system')) { // user is allowed } else { // user is denied } // mock an other user through the switch user feature try { $securityManager->switchUser('cm'); // perform a check on the switched user if ($securityManager->isPermissionGranted('my.permission')) { // switched user is granted } else { // switched user is denied } // logout the switched user $securityManager->logout(); } catch (UserNotFoundException $exception) { // requested user does not exist } catch (UserSwitchException $exception) { // can't switch to a super user as a non super user } catch (UnauthorizedException $exception) { // not allowed to switch user } // logout the current user $securityManager->logout(); }
实现
更多示例,您可以查看以下此库的实现:
- ride/cli-security
- ride/lib-security-generic
- ride/lib-security-oauth
- ride/web-security
- ride/web-security-generic
- ride/web-security-oauth
- ride/web-security-orm
安装
您可以使用Composer来安装此库。
composer require ride/lib-security