Разграничение прав доступа к контенту без авторизации

21st Ноябрь 2012 | Категории: Code Igniter, PHP | Метки:

Сегодня я расскажу об одном интересном способе предоставления доступа к закрытой информации. В примере я буду использовать фреймворк 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.

Subscribe without commenting


Пока комментариев нет.