Tredun ohjelmistokehittäjien kurssimateriaaleja
Lisää navikointipalkkiin (views/templates/header.php) linkki rekisteröitymis- ja kirjautumislomakkeille sekä uloskirjautumiselle:
<li><a href="/user/register">Rekisteröidy</a></li>
<li><a href="/user/login">Kirjaudu</a></li>
<li><a href="/user/logout">Kirjaudu ulos</a></li>
Tee rekisteröitymislomake (Views/user/register.php)
<?= \Config\Services::validation()->listErrors(); ?>
<h3>Register</h3>
<form action="/user/register" method="post">
<div><label for="username">Käyttäjänimi</label>
<input type="text" name="username" id="username" value="">
</div>
<div><label for="password">Salasana</label>
<input type="password" name="password" id="password" value="">
</div>
<div><label for="password2">Salasana uudelleen</label>
<input type="password" name="password2" id="password2" value="">
</div>
<input type="submit" name="submit" value="Rekisteröidy" />
</form>
Tee samalla vastaava kirjautumislomake (Views/user/register.php), jossa action-kentässä on: “user/login”.
Tee uusi kontrolleri (Controllers/User.php) ja siihen register-metodi.
<?php namespace App\Controllers;
use App\Models\UserModel;
class User extends BaseController
{
public function register()
{
$data = [];
$model = new UserModel();
if ($this->request->getMethod() === 'post'){
$rules = [
'username' => 'required|min_length[6]|max_length[255]|is_unique[users.username]',
'password' => 'required|min_length[8]|max_length[255]',
'password2' => 'matches[password]'
];
$errors = [
'username' => [
'required' => 'Käyttäjänimi ei voi olla tyhjä'
],
'password' => [
'required' => 'Salasana ei voi olla tyhjä'
],
];
if(! $this->validate($rules, $errors)){
$data['validation'] = $this->validator;
} else {
$model->save([
'username' => $this->request->getPost('username'),
'password' => $this->request->getPost('password'),
]);
return redirect()->to('/user/login');
}
}
echo view('templates/header');
echo view('user/register');
echo view('templates/footer');
}
}
Tässä annetaan validointikirjastolle suomenkieliset virheviestit error-taulukossa, joka kulkeutuu view:lle. Kirjasto osaa tarkistaa, että salasanat täsmäävät (match) ja sen onko valittu käyttäjätunnus jo käytössä (is_unique*). Voit itse lisätä virheilmoituksia.
Jotta tieto tallentuu tietokantaan, tee uusi model (Models/UserModel.php). Model hoitaa salasanan hashaamisen (beforeUpdate).
<?php namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model{
protected $table = 'users';
protected $allowedFields = ['username', 'password'];
protected $beforeInsert = ['beforeInsert'];
protected $beforeUpdate = ['beforeUpdate'];
protected function beforeInsert(array $data){
$data = $this->passwordHash($data);
return $data;
}
protected function beforeUpdate(array $data){
$data = $this->passwordHash($data);
return $data;
}
protected function passwordHash(array $data){
if(isset($data['data']['password']))
$data['data']['password'] = password_hash($data['data']['password'], PASSWORD_DEFAULT);
return $data;
}
}
Lisää yllä tehty register-metodi config/routes.php:een:
$routes->match(['get', 'post'], '/user/register', 'User::register');
Nyt pitäisi rekisteröitymisen toimia.
Lisää User-kontrolleriin metodi, joka hoitaa kirjautumisen:
public function login()
{
$data = [];
$model = new UserModel();
if ($this->request->getMethod() === 'post'){
$rules = [
'username' => 'required|min_length[6]|max_length[50]',
'password' => 'required|min_length[8]|max_length[255]|validateUser[username,password]',
];
$errors = [
'password' => [
'validateUser' => 'Tarkista käyttäjänimi tai salasana'
]
];
if (! $this->validate($rules, $errors)) {
$data['validation'] = $this->validator;
}else{
$model = new UserModel();
$user = $model->where('username', $this->request->getVar('username'))->first();
$this->setUserSession($user);
return redirect()->to('/');
}
}
echo view('templates/header');
echo view('user/login');
echo view('templates/footer');
}
Login - käyttää käyttäjätunnuksen ja salasanan tarkistamiseen CI:n validaatiokirjastoa (validate), johon voi lisätä omia validointisääntöjä. Tee uusi tiedosto (Validation/UserRules.php) ja liitä seuraava koodi siihen:
<?php
namespace App\Validation;
use App\Models\UserModel;
class UserRules
{
public function validateUser(string $str, string $fields, array $data){
$model = new UserModel();
$user = $model->where('username', $data['username'])
->first();
if(!$user)
return false;
return password_verify($data['password'], $user['password']);
}
}
Jotta CI-framework löytää tämän tiedoston, lisää se Config/Validation.php-tiedostoon:
public $ruleSets = [
\CodeIgniter\Validation\Rules::class,
\CodeIgniter\Validation\FormatRules::class,
\CodeIgniter\Validation\FileRules::class,
\CodeIgniter\Validation\CreditCardRules::class,
\App\Validation\UserRules::class, // UUSI!
];
Jotta kirjautuminen muistetaan, tallennetaan session-muuttujaan kirjautuneen tiedot. Tämä tapahtuu uuden private-metodin avulla, joka lisätään User-kontrolleriin. private-metodit ovat luokan sisäisiä “apumetodeja”, eikä niitä käytetä luokan ulkopuolelta.
private function setUserSession($user){
$data = [
'id' => $user['id'],
'username' => $user['username'],
'isLoggedIn' => true,
];
session()->set($data);
return true;
}
Session-muuttujaa voidaan käyttää session()-funktion avulla, palauttaa session-olion, jolla on käteviä apumetodeja. Jotta sessio syntyy, jokaisen requestin kohdalla pitää muistaa kutsua session alustusfunktiota. Oliomallissa tämä hoituu, kun seuraavan koodirivin ottaa käyttöön BaseController:issa (jonka kaikki kontrollerit perivät):
$this->session = \Config\Services::session();
Lisää yllä tehty login-metodi config/routes.php:een:
$routes->match(['get', 'post'], '/user/login', 'User::login');
Jotta näemme, että login toimii, muutetaan navigointipalkin sisältöä kirjautumisen mukaisesti:
<ul>
<li><a href="/">Takaisin</a></li>
<?php if (session()->get('isLoggedIn')): ?>
<li><a href="/news">Lue uutisia</a></li>
<li><a href="/news/create">Luo uusi uutinen</a></li>
<li><a href="/user/logout">Kirjaudu ulos</a></li>
<?php else: ?>
<li><a href="/user/register">Rekisteröidy</a></li>
<li><a href="/user/login">Kirjaudu</a></li>
<?php endif ?>
</ul>
Nyt kirjautumisen pitäisi toimia.
Jotta uusi uutinen tallentuu tietokantaan oikean käyttäjän alle, käydään korjaamassa News-kontrollerin create-metodiin seuraava rivi. Kovakoodatun user_id:n tilalle haetaan kirjautuneen käyttäjän id session-muuttujasta:
$model->save([
'title' => $this->request->getPost('title'),
'content' => $this->request->getPost('content'),
'user_id' => session()->get('id') /// TÄMÄ!
]);
Jotta voit testata kirjautumista, lisätään User-kontrolleriin logout-metodi:
public function logout(){
session()->destroy();
return redirect()->to('/');
}
Lisää sille myös reitti (Conf/routes.php):
$routes->get('/user/logout', 'User::logout');
Vaikka kirjautuminen periaatteessa toimii, tekemämme reitit eivät ole suojattuja. Jos avaat käsin osoitteen: /news se aukeaa, vaikka käyttäjä ei ole kirjautuneena.
Jotta näin ei pääsisi käymään lisätään Auth-filter, suojaamaan reittejä. Tee Auth-luokka (Filters/Auth.php):
<?php namespace App\Filters;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Filters\FilterInterface;
class Auth implements FilterInterface
{
public function before(RequestInterface $request, $arguments = NULL)
{
// Do something here
if(! session()->get('isLoggedIn')){
return redirect()->to('/');
}
}
//------------------------------------------
public function after(RequestInterface $request, ResponseInterface $response, $arguments = NULL)
{
// Do something here
}
}
Luokka ei peri CI framework:ista toimintoja, mutta se implementoi FilterInterface:n, eli se lupaa toteuttaa tietyt toiminnallisuudet, jotta framework voi sitä käyttää.
Jotta filteriä on helpompi käyttää annetaan sille lyhyempi alias. Lisää seuraava rivi (Conf/Filters.php)
public $aliases = [
'csrf' => \CodeIgniter\Filters\CSRF::class,
'toolbar' => \CodeIgniter\Filters\DebugToolbar::class,
'honeypot' => \CodeIgniter\Filters\Honeypot::class,
'auth' => \App\Filters\Auth::class, //TÄMÄ!!
];
Ja lopuksi otetaan filter-suojaukset reiteille käyttöön, muokkaa Conf/routes.php:
$routes->get('/news', 'News::index',['filter' => 'auth']);
$routes->match(['get', 'post'], '/news/create', 'News::create',['filter' => 'auth']);
$routes->get('/news/delete/(:num)', 'News::delete/$1',['filter' => 'auth']);
$routes->get('/news/edit/(:num)', 'News::edit/$1',['filter' => 'auth']);
$routes->get('/news/update/(:num)', 'News::update/$1',['filter' => 'auth']);
Nyt rekisteröityminen, kirjautuminen ja uloskirjautuminen toimivat.
Jos haluat kertoa, että rekistöityminen onnistui, voit tallentaa tiedon session:n flashData:aan. Tähän voi tallentaa väliaikaista tietoa, joka näytetään seuraavan sivun yhteydessä eli login-sivulla.
Lisää User::register-metodin sisälle registeröinnin onnistumishaaraan (ennen redirect:iä):
session()->setFlashdata('success', 'Rekisteröinti onnistui');
Lisää myös login-view:iin:
<?php if (session()->get('success')): ?>
<?= session()->get('success') ?>
<?php endif; ?>