Разграничение прав доступа к контенту без авторизации
Сегодня я расскажу об одном интересном способе предоставления доступа к закрытой информации. В примере я буду использовать фреймворк CodeIgniter v1.7, но приведенный метод легко переносится в любое приложение. Суть метода заключается в следующем. Есть контроллер, доступ к которому необходимо ограничить. По каким-то причинам использование авторизации невозможно. Мы создаем специальную ключ-ссылку. Любой, кто зайдет по такой ссылке, получает доступ к информации. Разные ключи дают доступ к разной информации.
Создадим базовый контроллер.
app/libraries/Key_Controller.php
<?php class Key_Controller extends MY_Controller { public $access_list = array(); public $module = ''; //http://cite/module/index/key/asdaa function Key_Controller($module = '') { parent::MY_Controller(); $this->module = $module; $this->load->library('session'); if (!$this->_check_access()) die("access denied<BR> Есть ключ-ссылка? Попробуйте войти с её помощью."); } function _check_access() { /* 'module' => array ( 'key' => array (access_list), ) можно вынести в конфиг файл */ $all_keys = array ( 'report' => array ( 'example' => array ('shop_id' => array(211)), 'fghgf' => array ('shop_id' => array(5604, 3015)), 'z4ehg' => array ('shop_id' => array(1888, 5001)), 'zdhtr' => array ('shop_id' => array(6259, 2894)), 'zdrgr' => array ('shop_id' => array(433)), 'asdaa' => array ('shop_id' => array(476)), 'warfs' => array ('shop_id' => array(5390)), ), 'report_1209' => array ( 'example' => array ('shop_id' => array(211)), 'cbres' => array ('shop_id' => array(302, 326)), 'sdfsd' => array ('shop_id' => array(4370)), 'zdfza' => array ('shop_id' => array(928)), 'sdfed' => array ('shop_id' => array(2336)), 'ddr4e' => array ('shop_id' => array(5152)), 'fvxsz' => array ('shop_id' => array(4776)), 'cvnvn' => array ('shop_id' => array(17), 'order_type' => array(10, 15)), ), ); // неизвестный модуль, доступ запретить if (!isset($all_keys[$this->module])) return FALSE; $module_keys = $all_keys[$this->module]; $array = $this->uri->uri_to_assoc(3); // в url нет связки /key/_ключ_ // возможно, пользователь уже заходил через ключ-ссылку // а значит в сессии уже содержится запись if (!isset($array['key'])) { $key = $this->session->userdata('key_controller'); // в сессии ничего нет if (!$key) return FALSE; // в сессии есть запись, но такого ключа нет в таблице разрешений if (!isset($module_keys[$key])) return FALSE; // запоминаем права и сообщаем, что все в порядке $this->access_list = $module_keys[$key]; return TRUE; } // ключ пришел из url $key = $array['key']; // но такого ключа нет в таблице разрешений if (!isset($module_keys[$key])) return FALSE; // все в порядке: заносим ключ в сессию $sses_data = array('key_controller' => $key); $this->session->set_userdata($sses_data); $this->access_list = $module_keys[$key]; return TRUE; } } |
Информацию о ключах мы храним в коде контроллера. В зависимости от ваших потребностей, вы можете перенести ключи в конфиг или базу данных.
Логика работы контроллера:
1. Проверяем, к какому модулю пытаются получить доступ. Если модуль не описан в конфиге – die()
;
2. Получаем список параметров из URL с помощью $this->uri->uri_to_assoc(3)
. Тройка означает, что необходимо начать разбор с третьего параметра (1 и 2 – это обычно контроллер и метод). На выходе мы должны получить массив key => '_ключ_'
;
3. Проверяем, есть ли связка key => '_ключ_'
.
3.1 Связки нет? Если связки нет, то возможно, пользователь уже заходил через ключ-ссылку.
3.1.1 Если в сессии (ключ key_controller
) нет данных – die()
;
3.1.2 Если в сессии (ключ key_controller
) есть ключ, но этот ключ отсутствует в таблице разрешений – die()
;
3.1.3 Ключ опознан. Запоминаем права данного ключа (права будут доступны во всех наследуемых контроллерах через $this->access_list
). Пропускаем пользователя;
3.2 Связка есть? Проверяем ключ.
3.2.1 Ключ отсутствует в таблице разрешений – die()
;
3.2.2 Ключ опознан. Запоминаем права данного ключа (права будут доступны во всех наследуемых контроллерах через $this->access_list
);
3.2.3 Заносим запись в сессию (ключ key_controller). Пропускаем пользователя;
Вместо die()
можно выдавать 403 ошибку или перенаправлять на страницу с какой-то информацией.
Структура ключей:
'module' => array ( 'key' => array (access_list), ) |
Рассмотрим пример конфига:
$all_keys = array ( 'report' => array ( 'example' => array ('shop_id' => array(211)), 'fghgf' => array ('shop_id' => array(5604, 3015)), ... |
Для модуля report
добавлено 2 ключа. Первому дано разрешение 'shop_id' => array(211)
, второму 'shop_id' => array(5604, 3015)
. В рамках модуля с отчетами эти разрешения означают: первому ключу дать разрешение на просмотр статистики для магазина с номером 211, второму – для магазинов 5604 и 3015. Количество разрешений может быть любым.
Создадим контроллер Report
, который унаследуем от Key_Controller
app/controllers/report.php
<?php class Report extends Key_Controller { function __construct() { parent::Key_Controller('report'); } function index() { echo 'Доступ разрешен.<BR>Ваш ключ позволяет:<BR>'; print_r ($this->access_list); echo '<BR>А здесь можно вывести меню.'; } function denied() { echo 'Доступ к этим данным запрещен.<BR>Ваш ключ позволяет:<BR>'; print_r ($this->access_list); echo '<BR>А здесь можно вывести меню.'; } // какой-то статистический отчет function total($shop_id = 0) { $shop_id = (int) $shop_id; // может кто-то пытается получить доступ к данным, не разрешенным для него? // сверяем запрашиваемый id со списоком разрешений if (!in_array($shop_id, $this->access_list['shop_id'])) redirect($this->uri->rsegment(1).'/denied'); echo $sql = "SELECT some_data FROM table WHERE shop_id = {$shop_id};"; //.................. } } |
В конструкторе контроллера обязательно вызываем родительский конструктор с параметром «имя модуля». Для гибкости мы можем использовать в разных контроллерах одинаковые названия модулей. В таком случае для доступа можно будет использовать одну и ту же ключ-ссылку.
В контроллере Report
реализуем три метода:
index
– Метод по умолчанию. На него попадает пользователь при первом посещении. Можно вывести информацию о правах и меню с ссылками на статистику;
denied
– «Доступ запрещен». На него попадает пользователь при попытке доступа к данным, к которым у него доступа нет (например, подбор значений URL). Можно вывести информацию о правах и меню со ссылками на статистику;
total
– Метод со статистикой. Имеет один параметр – $shop_id
. Этот параметр сравнивается со списком разрешений. Если доступ к этим данным запрещен, пользователь перенаправляется на denied
.