それだけでは難しかったので、他にもVOYAGE GROUPさんのブログにもお世話になりました。感謝感謝です。
作りたいものの整理
今回、作りたかったのは- /loginでログイン
- /logoutでログアウト
- ログインフォームでログイン
- データベースからユーザーを取得
- 簡単にアクセス出来るように、security.ymlでの設定
- アクセス制御はsecurity.yml
- 「/」はアクセスフリー、「page/home」はユーザー権限でログインした時にアクセス可。
はまった箇所
何箇所かハマりました。- security.ymlで「anonymous: ~」の「~」は多分tureの意
- 「UserInterface」でオーバーライドするメソッド
- Userテーブルとは別にRoleテーブルを作る
作業手順
最終的にはこの順序で作れば良い?という手順。1.securityBundleを作る。
php app/console generate:bundle --namespace=Acme/SecurityBundle
2.security.ymlを設定する。
ガイドブックだと、security.ymlとconfig.ymlでごちゃ混ぜになってますが、どうやらsecurity.ymlに書くのが正しいようです。# app/config/security.yml
security:
encoders:
main1:
class: Symfony\Component\Security\Core\User\User
algorithm: plaintext
main2:
class: Acme\SecutityBundle\Entity\User
algorithm: sha1
iterations: 1
encode_as_base64: false
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
chain_provider:
providers: [in_memory,user_db]
in_memory:
users:
user: { password: userpass, roles: [ 'ROLE_USER' ] }
admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }
user_db:
entity: { class: Acme\SecurityBundle\Entity\User, property: username}
firewalls:
login:
pattern: ^/login$
security: false
secured_area:
pattern: ^/
anonymous: ~
form_login:
login_path: /login
check_path: /login_check
logout:
path: /logout
target: /
access_control:
- { path: ^/admin/user, role: ROLE_ADMIN }
- { path: ^/admin/role, role: ROLE_ADMIN }
- { path: ^/page/home, role: ROLE_USER }
- { path: ^/page/information, role: ROLE_USER }
role_hierarchyは権限の階層。
providersは要はユーザー情報の設定の事で、security.ymlに直接書く設定とデータベースから引っ張ってくる方法を設定しています。
firewallsは認証の設定。
firewallsの「anonymous: ~」とすると、認証はするけれどもアクセス制御が空なら全部見れるよっていう設定っぽい。
access_controlはアクセス制御の設定。
3.ルーティングの設定
ログイン、ログアウトのルーティング設定をします。# app/config/routing.yml
login:
pattern: /login
defaults: { _controller: AcmeSecurityBundle:Security:login }
login_check:
pattern: /login_check
logout:
pattern: /logout
4.コントローラーを作る
# src/Acme/SecurityBundle/Controller/SecurityController.php
namespace Acme\SecurityBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Core\SecurityContext;
class SecurityController extends Controller
{
public function loginAction()
{
$request = $this->getRequest();
$session = $request->getSession();
// ログインエラーがあれば、ここで取得
if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
} else {
$error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
}
return $this->render('AcmeSecurityBundle:Security:login.html.twig', array(
// ユーザによって前回入力された username
'last_username' => $session->get(SecurityContext::LAST_USERNAME),
'error' => $error,
));
}
}
5.ログインフォームのテンプレートを作る
# src/Acme/SecurityBundle/Resources/views/Security/login.html.twig
{% if error %}
<div>{{ error.message }}</div>
{% endif %}
<form action="{{ path('login_check') }}" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="_username" value="{{ last_username }}" />
<label for="password">Password:</label>
<input type="password" id="password" name="_password" />
{#
認証成功した際のリダイレクト URL を制御したい場合(詳細は以下に説明する)
<input type="hidden" name="_target_path" value="/account" />
#}
<input type="submit" name="login" />
</form>
6.Userエンティティを作る。
取り敢えず「UserInterface」は無視する。メソッドをオーバーライドしておかないと、コンソールが動かないので。# src/Acme/SecurityBundle/Entity/User.php
namespace Acme\SecurityBundle\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* @orm\Entity(repositoryClass="Acme\SecurityBundle\Repository\UserRepository")
* @orm\Table(name="user")
*/
class User //implements UserInterface
{
/**
* @orm\Id
* @orm\Column(type="integer")
* @orm\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @orm\Column(name="username",unique="true")
*/
protected $username;
/**
* @orm\Column(name="password",length="255",)
*/
protected $password;
/**
* @orm\Column(name="salt", length="16")
*/
protected $salt;
/**
* @orm\Column(name="role_id", type="integer", nullable="true")
*/
protected $role;
}
ドクトリンにゲッター、セッターを作ってもらいます。
php app/console doctrine:generate:entities Acme/SecurityBundle/Entity/User
「UserInterface」をインプリメントして以下のメソッドを追加。
# src/Acme/SecurityBundle/Entity/User.php
...
public function getRoles(){
return array($this->role->getName());
}
public function eraseCredentials(){
}
public function equals(UserInterface $user){
return $this->getUsername() == $user->getUsername();
}
7.Roleエンティティを作る
# src/Acme/SecurityBundle/Entity/Role.php
namespace Acme\SecurityBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @orm\Entity(repositoryClass="Acme\SecurityBundle\Repository\RoleRepository")
* @orm\Table(name="role")
*/
class Role
{
/**
* @orm\Id
* @orm\Column(type="integer")
* @orm\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @orm\Column(name="name", unique="true", nullable="true")
*/
protected $name;
/**
* @ORM\Column(name="description", type="text")
*/
protected $description;
/**
* @ORM\OneToMany(targetEntity="User", mappedBy="role")
*/
protected $users;
public function __construct()
{
$this->users = new ArrayCollection();
}
}
ドクトリンにゲッター、セッターを作ってもらいます。
php app/console doctrine:generate:entities Acme/SecurityBundle/Entity/Role
8.データベースにテーブルを作る
php app/console doctrine:schema:update --force
9.レポジトリを作る
php app/console doctrine:generate:entities Acme
最終的には以下のようになってます。
namespace Acme\SecurityBundle\Repository;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\ORM\EntityRepository;
use Acme\SecurityBundle\Entity\User;
/**
* UserRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class UserRepository extends EntityRepository implements UserProviderInterface
{
public function loadUserByUsername($username)
{
return $this->findOneBy(array('username' => $username));
}
public function supportsClass($class)
{
return true;
}
public function refreshUser(UserInterface $user)
{
if(!$user->getUsername()){
throw new Exception(sprintf('Intances of "%s" are not supported.', get_class($user)));
}
return $this->loadUserByUsername($user->getUsername());
}
}
一応これでブラウザでアクセスするとそれっぽい事になっています。
次回はこれに続いてUserの管理する辺りを作っていきます。
0 件のコメント:
コメントを投稿