Nette Documentation Preview

syntax
認証
*************

Netteは、私たちのサイトで認証をプログラムする方法を提供しますが、何も強制しません。実装は私たち次第です。Netteには`Nette\Security\Authenticator`インターフェースが含まれており、これは`authenticate`という1つのメソッドのみを要求します。このメソッドは、私たちが望む任意の方法でユーザーを検証します。

ユーザーを認証する方法はたくさんあります。最も一般的な認証方法はパスワードによるものです(ユーザーは名前またはメールアドレスとパスワードを提供します)が、他の方法もあります。一部のサイトで「Facebookでログイン」ボタンやGoogle/Twitter/GitHubによるログインを見たことがあるかもしれません。Netteを使用すると、任意のログイン方法を持つことができ、それらを組み合わせることもできます。実装は開発者に委ねられています。

通常は独自の認証システムを作成しますが、このシンプルな小さなブログでは、設定ファイルに保存されているパスワードとユーザー名に基づいてログインする組み込みの認証システムを使用します。これはテスト目的に適しています。したがって、設定ファイル`config/common.neon`に次の*security*セクションを追加します。


```neon .{file:config/common.neon}
security:
	users:
		admin: secret  # ユーザー 'admin', パスワード 'secret'
```

NetteはDIコンテナに自動的にサービスを作成します。


ログインフォーム
========

これで認証の準備が整い、ログイン用のユーザーインターフェースを準備する必要があります。*SignPresenter*という名前の新しいPresenterを作成しましょう。これは次のことを行います。

- ログインフォーム(ログイン名とパスワード付き)を表示する
- フォーム送信後、ユーザーを認証する
- ログアウトオプションを提供する

ログインフォームから始めましょう。Presenterでフォームがどのように機能するかはすでに知っています。したがって、Presenter `SignPresenter`を作成し、メソッド`createComponentSignInForm`を記述します。次のようになります。

```php .{file:app/Presentation/Sign/SignPresenter.php}
<?php
namespace App\Presentation\Sign;

use Nette;
use Nette\Application\UI\Form;

final class SignPresenter extends Nette\Application\UI\Presenter
{
	protected function createComponentSignInForm(): Form
	{
		$form = new Form;
		$form->addText('username', 'ユーザー名:')
			->setRequired('ユーザー名を入力してください。');

		$form->addPassword('password', 'パスワード:')
			->setRequired('パスワードを入力してください。');

		$form->addSubmit('send', 'ログイン');

		$form->onSuccess[] = $this->signInFormSucceeded(...);
		return $form;
	}
}
```

ユーザー名とパスワードのフィールドがあります。


テンプレート
------

フォームはテンプレート`in.latte`でレンダリングされます。

```latte .{file:app/Presentation/Sign/in.latte}
{block content}
<h1 n:block=title>ログイン</h1>

{control signInForm}
```


ログインコールバック
----------

次に、フォームが正常に送信された直後に呼び出されるユーザーログイン用のコールバックを追加します。

コールバックは、ユーザーが入力したユーザー名とパスワードを受け取り、それらを認証システムに渡すだけです。ログイン後、ホームページにリダイレクトします。

```php .{file:app/Presentation/Sign/SignPresenter.php}
private function signInFormSucceeded(Form $form, \stdClass $data): void
{
	try {
		$this->getUser()->login($data->username, $data->password);
		$this->redirect('Home:');

	} catch (Nette\Security\AuthenticationException $e) {
		$form->addError('ユーザー名またはパスワードが正しくありません。');
	}
}
```

メソッド[User::login() |api:Nette\Security\User::login()]は、ユーザー名とパスワードが設定ファイルの情報と一致しない場合に例外をスローします。すでに知っているように、これにより赤いエラーページが表示されるか、本番モードではサーバーエラーを通知するメッセージが表示される可能性があります。しかし、それは望ましくありません。したがって、この例外をキャッチし、フォームにわかりやすい、ユーザーフレンドリーなエラーメッセージを渡します。

フォームでエラーが発生すると、フォームのあるページが再描画され、フォームの上に、ユーザーが間違ったログイン名またはパスワードを入力したことを通知するわかりやすいメッセージが表示されます。


Presenterの保護
============

投稿を追加および編集するためのフォームを保護します。これはPresenter `EditPresenter`で定義されています。目標は、ログインしていないユーザーがページにアクセスできないようにすることです。

[Presenterのライフサイクル |application:presenters#Presenterのライフサイクル]の開始直後に実行される`startup()`メソッドを作成します。これにより、ログインしていないユーザーがログインフォームにリダイレクトされます。

```php .{file:app/Presentation/Edit/EditPresenter.php}
public function startup(): void
{
	parent::startup();

	if (!$this->getUser()->isLoggedIn()) {
		$this->redirect('Sign:in');
	}
}
```


リンクの非表示
-------

認証されていないユーザーは、*create*ページや*edit*ページを表示できなくなりましたが、それらへのリンクはまだ表示されます。これらも非表示にする必要があります。そのようなリンクの1つはテンプレート`app/Presentation/Home/default.latte`にあり、ログインしているユーザーのみが表示できるようにする必要があります。

*n:属性* `n:if`を使用して非表示にできます。この条件が`false`の場合、コンテンツを含む`<a>`タグ全体が非表示のままになります。

```latte
<a n:href="Edit:create" n:if="$user->isLoggedIn()">投稿を作成</a>
```

これは、次の記述の省略形です(`tag-if`と混同しないでください):

```latte
{if $user->isLoggedIn()}<a n:href="Edit:create">投稿を作成</a>{/if}
```

同様の方法で、テンプレート`app/Presentation/Post/show.latte`のリンクも非表示にします。


ログインリンク
=======

実際にログインページにアクセスするにはどうすればよいでしょうか?そこにつながるリンクはありません。それでは、テンプレート`@layout.latte`に追加しましょう。適切な場所を見つけてみてください - ほとんどどこでもかまいません。

```latte .{file:app/Presentation/@layout.latte}
...
<ul class="navig">
	<li><a n:href="Home:">記事</a></li>
	{if $user->isLoggedIn()}
		<li><a n:href="Sign:out">ログアウト</a></li>
	{else}
		<li><a n:href="Sign:in">ログイン</a></li>
	{/if}
</ul>
...
```

ユーザーがログインしていない場合は、「ログイン」リンクが表示されます。それ以外の場合は、「ログアウト」リンクが表示されます。このアクションも`SignPresenter`に追加します。

ログアウト後すぐにユーザーをリダイレクトするため、テンプレートは必要ありません。ログアウトは次のようになります。

```php .{file:app/Presentation/Sign/SignPresenter.php}
public function actionOut(): void
{
	$this->getUser()->logout();
	$this->flashMessage('ログアウトしました。');
	$this->redirect('Home:');
}
```

`logout()`メソッドが呼び出され、その後、ログアウトが成功したことを確認するわかりやすいメッセージが表示されます。


まとめ
===

ログインとユーザーログアウトのためのリンクがあります。検証には組み込みの認証システムを使用し、ログイン情報は設定ファイルにあります。これは簡単なテストアプリケーションであるためです。また、編集フォームも保護したので、ログインしているユーザーのみが投稿を追加および編集できます。

.[note]
ここでは、[ユーザーログイン |security:authentication]と[権限の検証 |security:authorization]について詳しく読むことができます。

{{priority: -1}}
{{sitename: Nette Quickstart}}

認証

Netteは、私たちのサイトで認証をプログラムする方法を提供しますが、何も強制しません。実装は私たち次第です。NetteにはNette\Security\Authenticatorインターフェースが含まれており、これはauthenticateという1つのメソッドのみを要求します。このメソッドは、私たちが望む任意の方法でユーザーを検証します。

ユーザーを認証する方法はたくさんあります。最も一般的な認証方法はパスワードによるものです(ユーザーは名前またはメールアドレスとパスワードを提供します)が、他の方法もあります。一部のサイトで「Facebookでログイン」ボタンやGoogle/Twitter/GitHubによるログインを見たことがあるかもしれません。Netteを使用すると、任意のログイン方法を持つことができ、それらを組み合わせることもできます。実装は開発者に委ねられています。

通常は独自の認証システムを作成しますが、このシンプルな小さなブログでは、設定ファイルに保存されているパスワードとユーザー名に基づいてログインする組み込みの認証システムを使用します。これはテスト目的に適しています。したがって、設定ファイルconfig/common.neonに次のsecurityセクションを追加します。

security:
	users:
		admin: secret  # ユーザー 'admin', パスワード 'secret'

NetteはDIコンテナに自動的にサービスを作成します。

ログインフォーム

これで認証の準備が整い、ログイン用のユーザーインターフェースを準備する必要があります。SignPresenterという名前の新しいPresenterを作成しましょう。これは次のことを行います。

  • ログインフォーム(ログイン名とパスワード付き)を表示する
  • フォーム送信後、ユーザーを認証する
  • ログアウトオプションを提供する

ログインフォームから始めましょう。Presenterでフォームがどのように機能するかはすでに知っています。したがって、Presenter SignPresenterを作成し、メソッドcreateComponentSignInFormを記述します。次のようになります。

<?php
namespace App\Presentation\Sign;

use Nette;
use Nette\Application\UI\Form;

final class SignPresenter extends Nette\Application\UI\Presenter
{
	protected function createComponentSignInForm(): Form
	{
		$form = new Form;
		$form->addText('username', 'ユーザー名:')
			->setRequired('ユーザー名を入力してください。');

		$form->addPassword('password', 'パスワード:')
			->setRequired('パスワードを入力してください。');

		$form->addSubmit('send', 'ログイン');

		$form->onSuccess[] = $this->signInFormSucceeded(...);
		return $form;
	}
}

ユーザー名とパスワードのフィールドがあります。

テンプレート

フォームはテンプレートin.latteでレンダリングされます。

{block content}
<h1 n:block=title>ログイン</h1>

{control signInForm}

ログインコールバック

次に、フォームが正常に送信された直後に呼び出されるユーザーログイン用のコールバックを追加します。

コールバックは、ユーザーが入力したユーザー名とパスワードを受け取り、それらを認証システムに渡すだけです。ログイン後、ホームページにリダイレクトします。

private function signInFormSucceeded(Form $form, \stdClass $data): void
{
	try {
		$this->getUser()->login($data->username, $data->password);
		$this->redirect('Home:');

	} catch (Nette\Security\AuthenticationException $e) {
		$form->addError('ユーザー名またはパスワードが正しくありません。');
	}
}

メソッドUser::login()は、ユーザー名とパスワードが設定ファイルの情報と一致しない場合に例外をスローします。すでに知っているように、これにより赤いエラーページが表示されるか、本番モードではサーバーエラーを通知するメッセージが表示される可能性があります。しかし、それは望ましくありません。したがって、この例外をキャッチし、フォームにわかりやすい、ユーザーフレンドリーなエラーメッセージを渡します。

フォームでエラーが発生すると、フォームのあるページが再描画され、フォームの上に、ユーザーが間違ったログイン名またはパスワードを入力したことを通知するわかりやすいメッセージが表示されます。

Presenterの保護

投稿を追加および編集するためのフォームを保護します。これはPresenter EditPresenterで定義されています。目標は、ログインしていないユーザーがページにアクセスできないようにすることです。

Presenterのライフサイクルの開始直後に実行されるstartup()メソッドを作成します。これにより、ログインしていないユーザーがログインフォームにリダイレクトされます。

public function startup(): void
{
	parent::startup();

	if (!$this->getUser()->isLoggedIn()) {
		$this->redirect('Sign:in');
	}
}

リンクの非表示

認証されていないユーザーは、createページやeditページを表示できなくなりましたが、それらへのリンクはまだ表示されます。これらも非表示にする必要があります。そのようなリンクの1つはテンプレートapp/Presentation/Home/default.latteにあり、ログインしているユーザーのみが表示できるようにする必要があります。

n:属性 n:ifを使用して非表示にできます。この条件がfalseの場合、コンテンツを含む<a>タグ全体が非表示のままになります。

<a n:href="Edit:create" n:if="$user->isLoggedIn()">投稿を作成</a>

これは、次の記述の省略形です(tag-ifと混同しないでください):

{if $user->isLoggedIn()}<a n:href="Edit:create">投稿を作成</a>{/if}

同様の方法で、テンプレートapp/Presentation/Post/show.latteのリンクも非表示にします。

ログインリンク

実際にログインページにアクセスするにはどうすればよいでしょうか?そこにつながるリンクはありません。それでは、テンプレート@layout.latteに追加しましょう。適切な場所を見つけてみてください – ほとんどどこでもかまいません。

...
<ul class="navig">
	<li><a n:href="Home:">記事</a></li>
	{if $user->isLoggedIn()}
		<li><a n:href="Sign:out">ログアウト</a></li>
	{else}
		<li><a n:href="Sign:in">ログイン</a></li>
	{/if}
</ul>
...

ユーザーがログインしていない場合は、「ログイン」リンクが表示されます。それ以外の場合は、「ログアウト」リンクが表示されます。このアクションもSignPresenterに追加します。

ログアウト後すぐにユーザーをリダイレクトするため、テンプレートは必要ありません。ログアウトは次のようになります。

public function actionOut(): void
{
	$this->getUser()->logout();
	$this->flashMessage('ログアウトしました。');
	$this->redirect('Home:');
}

logout()メソッドが呼び出され、その後、ログアウトが成功したことを確認するわかりやすいメッセージが表示されます。

まとめ

ログインとユーザーログアウトのためのリンクがあります。検証には組み込みの認証システムを使用し、ログイン情報は設定ファイルにあります。これは簡単なテストアプリケーションであるためです。また、編集フォームも保護したので、ログインしているユーザーのみが投稿を追加および編集できます。

ここでは、ユーザーログイン権限の検証について詳しく読むことができます。