1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
|
<?xml version="1.0" encoding="UTF-8"?>
<!-- Reviewed: no -->
<sect1 id="zend.auth.adapter.dbtable">
<title>Uwierzytelnianie w oparciu o tabelę bazy danych</title>
<sect2 id="zend.auth.adapter.dbtable.introduction">
<title>Wprowadzenie</title>
<para>
<classname>Zend_Auth_Adapter_DbTable</classname> zapewnia możliwość
przeprowadzenia uwierzytelniania w oparciu o dane przechowywane w
tabeli bazy danych. Z tego względu, że klasa
<classname>Zend_Auth_Adapter_DbTable</classname> wymaga przekazania instancji
klasy <classname>Zend_Db_Adapter_Abstract</classname> do jej konstruktora,
każda ta instancja jest powiązana z konkretnym połączeniem do bazy
danych. Inne opcje konfiguracyjne mogą być ustawione za pomocą
konstruktora lub za pomocą metod instancji, po jednej dla każdej z
opcji.
</para>
<para>
Dostępne opcje konfiguracyjne to:
</para>
<itemizedlist>
<listitem>
<para>
<emphasis>tableName</emphasis>: Jest to nazwa tabeli bazy
danych, która zawiera dane uwierzytelniania i do której
jest przeprowadzane zapytanie uwierzytelniające.
</para>
</listitem>
<listitem>
<para>
<emphasis>identityColumn</emphasis>: Jest to nazwa kolumny
tabeli bazy danych, która reprezentuje tożsamość.
Kolumna tożsamości musi zawierać unikalne wartości,
na przykład takie jak nazwa użytkownika czy adres
e-mail.
</para>
</listitem>
<listitem>
<para>
<emphasis>credentialColumn</emphasis>: Jest to nazwa kolumny
tabeli bazy danych, która reprezentuje wartość
uwierzytelniającą. W prostym schemacie uwierzytelniania
opartym o nazwę tożsamości i hasło, wartość
uwierzytelniająca odpowiada hasłu. Zobacz także opcję
<emphasis>credentialTreatment</emphasis>.
</para>
</listitem>
<listitem>
<para>
<emphasis>credentialTreatment</emphasis>: W wielu przypadkach,
hasło i inne wrażliwe dane są zszyfrowane, haszowane,
zakodowane lub w inny sposób przetworzone przez
jakąś funkcję lub algorytm. Określając metodę
przerobienia danych, taką jak na przykład
'MD5(?)' czy 'PASSWORD(?)',
programista może użyć konkretnej funkcji SQL na danych
uwierzytelniających. Z tego względu, że te funkcje są
specyficzne dla konkretnych systemów baz danych, zajrzyj
do odpowiednich dokumentacji aby sprawdzić dostępność
takich funkcji dla twojego systemu bazy danych.
</para>
</listitem>
</itemizedlist>
<example id="zend.auth.adapter.dbtable.introduction.example.basic_usage">
<title>Podstawowe użycie</title>
<para>
Jak wyjaśniono we wprowadzeniu, konstruktor klasy
<classname>Zend_Auth_Adapter_DbTable</classname> wymaga przekazania mu
instancji klasy <classname>Zend_Db_Adapter_Abstract</classname>,
zapewniającej połączenie do bazy danych, z którym powiązana jest
instancja adaptera uwierzytelniania. Na początku powinno być
utworzone połączenie do bazy danych.
</para>
<para>
Poniższy kod tworzy adapter bazy danych przechowywanej w pamięci,
tworzy prostą strukturę tabeli, a następnie wstawia wiersz, w
oparciu o który przeprowadzimy później zapytanie
uwierzytelniające. Ten przykład wymaga dostępnego rozszerzenia
PDO SQLite:
</para>
<programlisting language="php"><![CDATA[
// Tworzymy połączenie do bazy danych SQLite przechowywanej w pamięci
$dbAdapter = new Zend_Db_Adapter_Pdo_Sqlite(array('dbname' =>
':memory:'));
// Budujemy zapytanie tworzące prostą tabelę
$sqlCreate = 'CREATE TABLE [users] ('
. '[id] INTEGER NOT NULL PRIMARY KEY, '
. '[username] VARCHAR(50) UNIQUE NOT NULL, '
. '[password] VARCHAR(32) NULL, '
. '[real_name] VARCHAR(150) NULL)';
// Tworzymy tabelę z danymi uwierzytelniania
$dbAdapter->query($sqlCreate);
// Budujemy zapytanie wstawiające wiersz, dla którego możemy przeprowadzić
// próbę uwierzytelniania
$sqlInsert = "INSERT INTO users (username, password, real_name) "
. "VALUES ('my_username', 'my_password', 'My Real Name')";
// Wstawiamy dane
$dbAdapter->query($sqlInsert);
]]></programlisting>
<para>
Gdy połączenie do bazy danych oraz dane w tabeli są już
dostępne, możesz utworzyć instancję klasy
<classname>Zend_Auth_Adapter_DbTable</classname>. Opcje konfiguracyjne
mogą być przekazane do konstruktora lub przekazane jako
parametry do metod dostępowych już po utworzeniu instancji:
</para>
<programlisting language="php"><![CDATA[
// Konfigurujemy instancję za pomocą parametrów konstruktora
$authAdapter = new Zend_Auth_Adapter_DbTable(
$dbAdapter,
'users',
'username',
'password'
);
// ...lub konfigurujemy instancję za pomocą metod dostępowych
$authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);
$authAdapter
->setTableName('users')
->setIdentityColumn('username')
->setCredentialColumn('password')
;
]]></programlisting>
<para>
W tym momencie intancja adaptera uwierzytelniania jest gotowa
do przeprowadzenia zapytań uwierzytelniających. W celu
utworzenia zapytania, wejściowe dane uwierzytelniania
są przekazywane do adaptera przed wywołaniem metody
<methodname>authenticate()</methodname>:
</para>
<programlisting language="php"><![CDATA[
// Ustawiamy wartości danych uwierzytelniania (np., z formularza logowania)
$authAdapter
->setIdentity('my_username')
->setCredential('my_password');
// Przeprowadzamy zapytanie uwierzytelniające, zapisując rezultat
$result = $authAdapter->authenticate();
]]></programlisting>
<para>
Oprócz możliwości użycia metody <methodname>getIdentity()</methodname>
obiektu rezultatu uwierzytelniania, obiekt
<classname>Zend_Auth_Adapter_DbTable</classname> pozwala także na
odebranie wiersza tabeli po udanym uwierzytelnieniu.
</para>
<programlisting language="php"><![CDATA[
// Wyświetlamy tożsamość
echo $result->getIdentity() . "\n\n";
// Wyświetlamy wiersz rezultatów
print_r($authAdapter->getResultRowObject());
/* Wyświetlone dane:
my_username
Array
(
[id] => 1
[username] => my_username
[password] => my_password
[real_name] => My Real Name
)
*/
]]></programlisting>
<para>
Z tego względu, że wiersz tabeli zawiera dane potrzebne do
uwierzytelniania, ważne jest, aby dane były zabezpieczone przed
dostępem przez osoby nieuprawnione.
</para>
</example>
</sect2>
<sect2 id="zend.auth.adapter.dbtable.advanced.storing_result_row">
<title>Zaawansowane użycie: Stałe przechowywanie obiektu DbTable Result</title>
<para>
Domyślnie <classname>Zend_Auth_Adapter_DbTable</classname> po udanym
uwierzytelnieniu zwraca do obiektu uwierzytelniającego spowrotem
tę samą tożsamość. W innym przykładzie użycia programista może
chcieć przechować w stałym mechanizmie przechowywania
<classname>Zend_Auth</classname> obiekt tożsamości zawierający inne użyteczne
informacje. W takim przypadku może użyć metody
<methodname>getResultRowObject()</methodname> aby zwrócić obiekt klasy
<classname>stdClass</classname>. Poniższy kod ilustruje sposób jego użycia:
</para>
<programlisting language="php"><![CDATA[
// uwierzytelniamy za pomocą Zend_Auth_Adapter_DbTable
$result = $this->_auth->authenticate($adapter);
if ($result->isValid()) {
// przechowujemy tożsamość jako obiekt, w którym zwracane są jedynie
// pola username oraz real_name
$storage = $this->_auth->getStorage();
$storage->write($adapter->getResultRowObject(array(
'username',
'real_name'
)));
// przechowujemy tożsamość jako obiekt, w którym kolumna zawierająca
// hasło została pominięta
$storage->write($adapter->getResultRowObject(
null,
'password'
));
/* ... */
} else {
/* ... */
}
]]></programlisting>
</sect2>
<sect2 id="zend.auth.adapter.dbtable.advanced.advanced_usage">
<title>Przykład zaawansowanego użycia</title>
<para>
O ile głównym przeznaczeniem komponentu <classname>Zend_Auth</classname>
(i odpowiednio <classname>Zend_Auth_Adapter_DbTable</classname>) jest
<emphasis>uwierzytelnianie</emphasis> a nie <emphasis>autoryzacja</emphasis>,
jest kilka problemów które możemy rozwiązać odrobinę przekraczając
pole zastosowań komponentu. Zależnie od tego jak zdecydujesz
wyjaśnić swój problem, czasem może być przydatne rozwiązanie
problemu autoryzacji podczas uwierzytelniania.
</para>
<para>
Komponent <classname>Zend_Auth_Adapter_DbTable</classname> posiada
pewien wbudowany mechanizm, który może być użyty do dodania dodatkowych
warunków podczas uwierzytelniania, dzięki czemu można rozwiązać niektóre
problemy.
</para>
<programlisting language="php"><![CDATA[
// Wartość pola "status" dla tego konta nie jest równa wartości "compromised"
$adapter = new Zend_Auth_Adapter_DbTable(
$db,
'users',
'username',
'password',
'MD5(?) AND status != "compromised"'
);
// Wartość pola "active" dla tego konta jest równa wartości "TRUE"
$adapter = new Zend_Auth_Adapter_DbTable(
$db,
'users',
'username',
'password',
'MD5(?) AND active = "TRUE"'
);
]]></programlisting>
<para>
Innym przykładem może być implementacja mechanizmu saltingu. Jest to
technika pozwalająca w znaczny sposób zwiększyć bezpieczeństwo
aplikacji. Polega ona na tym, że dołączając do każdego hasła losowy
łańcuch znaków spowodujemy, że niemożliwe będzie przeprowadzenie
ataku brute force na bazę danych w oparciu o przygotowany słownik.
</para>
<para>
Zaczniemy od zmodyfikowania schematu tabeli bazy danych, aby móc
przechowywać nasz łańcuch znaków salt:
</para>
<programlisting language="php"><![CDATA[
$sqlAlter = "ALTER TABLE [users] "
. "ADD COLUMN [password_salt] "
. "AFTER [password]";
$dbAdapter->query($sqlAlter);
]]></programlisting>
<para>
W prosty sposób wygenerujmy salt dla każdego rejestrującego się
użytkownika:
</para>
<programlisting language="php"><![CDATA[
for ($i = 0; $i < 50; $i++)
{
$dynamicSalt .= chr(rand(33, 126));
}
]]></programlisting>
<para>
I skonfigurujmy sterownik bazy danych:
</para>
<programlisting language="php"><![CDATA[
$adapter = new Zend_Auth_Adapter_DbTable(
$db,
'users',
'username',
'password',
"MD5(CONCAT('"
. Zend_Registry::get('staticSalt')
. "', ?, password_salt))"
);
]]></programlisting>
<note>
<para>
Możesz jeszcze zwiększyć bezpieczeństwo używając dodatkowo
statycznego fragmentu łańcucha znaków umieszczonego na stałe
w kodzie aplikacji. W przypadku, gdy atakujący uzyska dostęp
do bazy danych (np. za pomocą ataku <acronym>SQL</acronym>
injection), a nie będzie miał dostępu do kodu źródłowego,
hasła wciąż będą dla niego nieprzydatne.
</para>
</note>
<para>
Innym sposobem jest użycie metody <methodname>getDbSelect()</methodname>
klasy <classname>Zend_Auth_Adapter_DbTable</classname> po utworzeniu
adaptera. Ta metoda zwróci obiekt klasy <classname>Zend_Db_Select</classname>,
który ma być użyty do przeprowadzenia uwierzytalniania. Ważne jest, że
ta metoda zawsze zwróci ten sam obiekt, niezależnie od tego czy metoda
<methodname>authenticate()</methodname> została wywołana czy nie.
Ten obiekt <emphasis>nie będzie</emphasis> posiadał żadnych informacji
dotyczących nazwy tożsamości i hasła, ponieważ te dane będą umieszczone
tam dopiero w czasie wywołania metody <methodname>authenticate()</methodname>.
</para>
<para>
Przykładem sytuacji w której można by użyć metody getDbSelect()
może być potrzeba sprawdzenia statusu użytkownika, czyli sprawdzenia
czy konto użytkownika jest aktywne.
</para>
<programlisting language="php"><![CDATA[
// Kontynuując poprzedni przykład
$adapter = new Zend_Auth_Adapter_DbTable(
$db,
'users',
'username',
'password',
'MD5(?)'
);
// pobieramy obiekt Zend_Db_Select (przez referencję)
$select = $adapter->getDbSelect();
$select->where('active = "TRUE"');
// uwierytelniamy, z warunkiem users.active = TRUE
$adapter->authenticate();
]]></programlisting>
</sect2>
</sect1>
|