如果页面需要身份验证并且没有找到用户,则Symfony会简单地重定向或显示登录页面。如此简单,我就能正常工作了。
接下来,如果用户在需要身份验证的页面内进行Ajax调用,但是会话已死(例如,该用户不再受身份验证),我想发送自定义消息(或html)。
security.yml
security:
encoders:
AppBundle\Entity\User:
algorithm: bcrypt
role_hierarchy:
ROLE_ADMIN: ROLE_USER
providers:
db_provider:
entity:
class: AppBundle:User
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: ~
pattern: ^/
form_login:
login_path: security_login
check_path: security_login
use_forward: false
failure_handler: AppBundle\Security\AuthenticationHandler
logout:
path: /logout
target: /
access_denied_handler: AppBundle\Security\AccessDeniedHandler
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, role: ROLE_ADMIN }
我尝试使用access_denied_handler
或failure_handler
拦截事件错误。
AppBundle \ Security \ AccessDeniedHandler.php
namespace AppBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface;
class AccessDeniedHandler implements AccessDeniedHandlerInterface {
public function handle(Request $request, AccessDeniedException $exception) {
return new JsonResponse([
'success' => 0,
'error' => 1,
'message' => $exception -> getMessage(),
'from' => 'AccessDeniedHandler'
]);
}
}
AppBundle \ Security \ AuthenticationHandler.php
namespace AppBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
class AuthenticationHandler implements AuthenticationFailureHandlerInterface {
public function onAuthenticationFailure(Request $request, AuthenticationException $exception) {
return new JsonResponse(['error' => 1, 'from' => 'AuthenticationHandler']);
}
}
这些类都不被访问。我想念什么?
答案 0 :(得分:0)
为Symfony 3.4项目创建,应与Symfony 4兼容,但我尚未测试;
所有服务都是自动连线的,因此没有要添加的服务。yml
我没有使用FOSUserBundle;
我没有遵循Symfony编码标准;
我在这里和那里做笔记;我还在代码本身中添加了一些注释;
重要的是最后(LoginFormAuthenticator),我正在发布整个代码,希望有人会比我更轻松。
https://symfony.com/doc/3.4/security.html
https://symfonycasts.com/screencast/symfony3-security
https://www.sitepoint.com/easier-authentication-with-guard-in-symfony-3/
security.yml
安全配置
对于“内存”用户,用户名和密码为“ admin”
security:
encoders:
Symfony\Component\Security\Core\User\User:
algorithm: bcrypt
AppBundle\Entity\User:
algorithm: bcrypt
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
chain_provider:
chain:
providers: [memory_provider, db_provider]
memory_provider:
memory:
users:
admin:
password: '$2y$13$21gXkzksqlR68HhAYB2WLOqcQvJZzgIrSH/KRq1aEzkkOnjI7lR9e'
roles: 'ROLE_SUPER_ADMIN'
db_provider:
entity:
class: AppBundle:User
property: email
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: ~
pattern: ^/
logout:
path: /logout
target: /
guard:
authenticators:
- AppBundle\Security\LoginFormAuthenticator
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, role: ROLE_USER }
模板 app / Resources / views / Security / _content.login.html.twig
{% set form_action = path('security_login') %}
<form action="{{ form_action }}" method="post" autocomplete="off" id="login_f">
{% if error %}
<div class="login_form_error">{{ error.messageKey }}</div>
{% endif %}
<div class="closed">
<input type="hidden" name="_csrf_token" value="{{ csrf_token(login_csrf_token) }}" />
</div>
<div class="login_field login_field_0">
<label for="login_username" class="login_l">
<i class="fas fa-user"></i>
</label>
<input type="text" class="login_i" id="login_username" name="_username" placeholder="Username" />
</div>
<div class="login_field login_field_1">
<label for="login_password" class="login_l">
<i class="fas fa-key"></i>
</label>
<input type="password" class="login_i" id="login_password" name="_password" placeholder="Password" />
</div>
<div>
<input type="submit" class="login_bttn" id="_submit" value="Login" />
</div>
</form>
模板 app / Resources / views / Security / login.html.twig
不需要base.html.twig
{% extends 'base.html.twig' %}
{% block content %}
<div id="login_c">
{% include 'Security/_content.login.html.twig' %}
</div>
{% endblock %}
服务
呈现登录页面或登录内容
用您自己的CSRF_TOKEN常量值替换
namespace AppBundle\Services\User;
use Symfony\Component\Templating\EngineInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class LoginFormService {
private $templatingEngine;
private $authenticationUtils;
const CSRF_TOKEN = 'login:token:w4kSzA3v5VJyb4aWLbV7stAY92cNwgL77J6QrXpU!';
function __construct(
EngineInterface $templatingEngine,
AuthenticationUtils $authenticationUtils) {
$this -> templatingEngine = $templatingEngine;
$this -> authenticationUtils = $authenticationUtils;
}
function getHtml($contentOnly = False) {
// last username entered by the user
$lastUsername = $this -> authenticationUtils -> getLastUsername();
// get the login error if there is one
$error = $this -> authenticationUtils -> getLastAuthenticationError();
$html_vars = array(
'lastUsername' => $lastUsername,
'error' => $error,
'login_csrf_token' => self::CSRF_TOKEN,
);
$html_template = 'Security/login.html.twig';
if ( $contentOnly ) {
$html_template = 'Security/_content.login.html.twig';
}
$html = $this -> templatingEngine -> render($html_template, $html_vars);
return $html;
}
}
登录控制器
一个简单的缓冲区控制器,用于在用户访问/ login时呈现登录页面
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use AppBundle\Services\User\LoginFormService;
class SecurityController extends Controller {
/**
@Route("/login", name="security_login")
*/
public function loginAction(LoginFormService $loginFormService, Request $request) {
return new Response($loginFormService -> getHtml());
}
/**
@Route("/logout", name="security_logout")
*/
public function logoutAction() {}
}
卫兵认证者
使用“”来代替“ project_homepage_route”
namespace AppBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
use Symfony\Component\Routing\RouterInterface;
use AppBundle\Services\User\LoginFormService;
class LoginFormAuthenticator extends AbstractGuardAuthenticator {
private $router;
private $templatingEngine;
private $passwordEncoder;
private $csrfTokenManager;
private $loginService;
protected $auth_error_csrf = 'Invalid CSRF token!!!';
protected $auth_error_message = 'Invalid credentials!!!';
function __construct(
RouterInterface $router,
UserPasswordEncoderInterface $passwordEncoder,
CsrfTokenManagerInterface $csrfTokenManager,
LoginFormService $loginService) {
$this -> router = $router;
$this -> passwordEncoder = $passwordEncoder;
$this -> csrfTokenManager = $csrfTokenManager;
$this -> loginService = $loginService;
}
/* Methods */
protected function loginResponse(Request $request, $forbidden = False) {
// The javascript library must set the 'X-Requested-With' header
// xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
if ( $request -> isXmlHttpRequest() ) {
$response = new JsonResponse([
'error' => 1,
'html' => $this -> loginService -> getHtml(True)
]);
} else {
$html = $this -> loginService -> getHtml();
$response = new Response($html);
}
if ($forbidden) {
$response -> setStatusCode(Response::HTTP_FORBIDDEN);
}
return $response;
}
/* AbstractGuardAuthenticator methods */
public function supports(Request $request) {
return $request -> attributes -> get('_route') === 'security_login' && $request -> isMethod('POST');
}
public function getCredentials(Request $request) {
// Add csrf protection
$csrfData = $request -> request -> get('_csrf_token');
$csrfToken = new CsrfToken(LoginFormService::CSRF_TOKEN, $csrfData);
if ( !$this -> csrfTokenManager -> isTokenValid($csrfToken) ) {
throw new InvalidCsrfTokenException( $this -> auth_error_csrf );
}
return array(
'username' => $request -> request -> get('_username'),
'password' => $request -> request -> get('_password'),
);
}
public function getUser($credentials, UserProviderInterface $userProvider) {
$username = $credentials['username'];
try {
return $userProvider -> loadUserByUsername($username);
} catch (UsernameNotFoundException $e) {
throw new CustomUserMessageAuthenticationException( $this -> auth_error_message );
}
return null;
}
public function checkCredentials($credentials, UserInterface $user) {
$is_valid_password = $this -> passwordEncoder -> isPasswordValid($user, $credentials['password']);
if ( !$is_valid_password ) {
throw new CustomUserMessageAuthenticationException( $this -> auth_error_message );
return;
}
return True;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $authException) {
$session = $request -> getSession();
$session -> set(Security::AUTHENTICATION_ERROR, $authException);
$session -> set(Security::LAST_USERNAME, $request -> request -> get('_username'));
// Shows the login form instead of the page content
return $this -> loginResponse($request, True);
// If you want redirect make sure the line below is used
// return new RedirectResponse($this -> router -> generate('security_login'));
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) {
if ( $request -> isXmlHttpRequest() ) {
return new JsonResponse([
'success' => 1,
'message' => 'Authentication success!'
]);
}
return new RedirectResponse($this -> router -> generate('project_homepage_route'));
}
public function start(Request $request, AuthenticationException $authException = null) {
// Shows the login form instead of the page content
return $this -> loginResponse($request);
// If you want redirect make sure the line below is used
// return new RedirectResponse($this -> router -> generate('security_login'));
}
public function supportsRememberMe() {
return false;
}
}