- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我们正在为客户提供多项选择练习系统的解决方案,学生每月支付成员(member)费以测试他们的知识并为医学相关考试做准备。在 Symfony2 中提供此解决方案的一个主要问题是学生可以购买一个订阅,与同学和同事分享他们的凭据,并将订阅费用分摊到多个并发登录上。
为了尽量减少这一问题,我们希望防止在我们的 Symfony2 项目中同时维护多个 session 。
大量的 Google-fu 让我找到了这个稀疏的 Google group thread OP 被简要告知使用 PdoSessionHandler 将 session 存储在数据库中。
这是 another SO question where someone else worked around the same thing ,但没有解释如何去做。
我已经为该项目实现了这个处理程序,目前有一个 security.interactive_login
监听器,它将生成的 session ID 与用户存储在数据库中。进度在这里
public function __construct(SecurityContext $securityContext, Doctrine $doctrine, Container $container)
{
$this->securityContext = $securityContext;
$this->doc = $doctrine;
$this->em = $doctrine->getManager();
$this->container = $container;
}
/**
* Do the magic.
*
* @param InteractiveLoginEvent $event
*/
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
{
if ($this->securityContext->isGranted('IS_AUTHENTICATED_FULLY')) {
// user has just logged in
}
if ($this->securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
// user has logged in using remember_me cookie
}
// First get that user object so we can work with it
$user = $event->getAuthenticationToken()->getUser();
// Now check to see if they're a subscriber
if ($this->securityContext->isGranted('ROLE_SUBSCRIBED')) {
// Check their expiry date versus now
if ($user->getExpiry() < new \DateTime('now')) { // If the expiry date is past now, we need to remove their role
$user->removeRole('ROLE_SUBSCRIBED');
$this->em->persist($user);
$this->em->flush();
// Now that we've removed their role, we have to make a new token and load it into the session
$token = new \Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken(
$user,
null,
'main',
$user->getRoles()
);
$this->securityContext->setToken($token);
}
}
// Get the current session and associate the user with it
$sessionId = $this->container->get('session')->getId();
$user->setSessionId($sessionId);
$this->em->persist($user);
$s = $this->doc->getRepository('imcqBundle:Session')->find($sessionId);
if ($s) { // $s = false, so this part doesn't execute
$s->setUserId($user->getId());
$this->em->persist($s);
}
$this->em->flush();
// We now have to log out all other users that are sharing the same username outside of the current session token
// ... This is code where I would detach all other `imcqBundle:Session` entities with a userId = currently logged in user
}
在 security.interactive_login
监听器完成 之前, session 不会从 PdoSessionHandler 存储到数据库中,因此用户 ID 永远不会与 session 表。 我怎样才能使这项工作?我可以在 session 表中的什么位置存储用户 ID?
或者,有没有更好的方法来解决这个问题?这对 Symfony 来说非常令人沮丧,因为我认为它从未被设计为为每个用户提供独占的单一用户 session 。
最佳答案
我已经解决了我自己的问题,但在我能够接受我自己的答案之前,我会将问题留待对话(如果有的话)。
我创建了一个 kernel.request
监听器,它会在每次登录时使用与用户关联的最新 session ID 检查用户的当前 session ID。
代码如下:
<?php
namespace Acme\Bundle\Listener;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\Routing\Router;
/**
* Custom session listener.
*/
class SessionListener
{
private $securityContext;
private $container;
private $router;
public function __construct(SecurityContext $securityContext, Container $container, Router $router)
{
$this->securityContext = $securityContext;
$this->container = $container;
$this->router = $router;
}
public function onKernelRequest(GetResponseEvent $event)
{
if (!$event->isMasterRequest()) {
return;
}
if ($token = $this->securityContext->getToken()) { // Check for a token - or else isGranted() will fail on the assets
if ($this->securityContext->isGranted('IS_AUTHENTICATED_FULLY') || $this->securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED')) { // Check if there is an authenticated user
// Compare the stored session ID to the current session ID with the user
if ($token->getUser() && $token->getUser()->getSessionId() !== $this->container->get('session')->getId()) {
// Tell the user that someone else has logged on with a different device
$this->container->get('session')->getFlashBag()->set(
'error',
'Another device has logged on with your username and password. To log back in again, please enter your credentials below. Please note that the other device will be logged out.'
);
// Kick this user out, because a new user has logged in
$this->securityContext->setToken(null);
// Redirect the user back to the login page, or else they'll still be trying to access the dashboard (which they no longer have access to)
$response = new RedirectResponse($this->router->generate('sonata_user_security_login'));
$event->setResponse($response);
return $event;
}
}
}
}
}
和 services.yml
条目:
services:
acme.session.listener:
class: Acme\Bundle\Listener\SessionListener
arguments: ['@security.context', '@service_container', '@router']
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
有趣的是,当我意识到我之前将 imcq.session.listener
命名为 session_listener< 时,我花了很多时间想知道为什么我的监听器让我的应用程序中断
。结果是 Symfony(或其他一些包)已经在使用该名称,因此我覆盖了它的行为。
小心! 这会破坏 FOSUserBundle 1.3.x 上的隐式登录功能。您应该升级到 2.0.x-dev 并使用其隐式登录事件,或者将 LoginListener
替换为您自己的 fos_user.security.login_manager
服务。 (我做了后者,因为我使用的是 SonataUserBundle)
根据要求,这里是 FOSUserBundle 1.3.x 的完整解决方案:
对于隐式登录,将此添加到您的 services.yml
:
fos_user.security.login_manager:
class: Acme\Bundle\Security\LoginManager
arguments: ['@security.context', '@security.user_checker', '@security.authentication.session_strategy', '@service_container', '@doctrine']
然后在 Acme\Bundle\Security
下创建一个名为 LoginManager.php
的文件,代码如下:
<?php
namespace Acme\Bundle\Security;
use FOS\UserBundle\Security\LoginManagerInterface;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface;
use Doctrine\Bundle\DoctrineBundle\Registry as Doctrine; // for Symfony 2.1.0+
class LoginManager implements LoginManagerInterface
{
private $securityContext;
private $userChecker;
private $sessionStrategy;
private $container;
private $em;
public function __construct(SecurityContextInterface $context, UserCheckerInterface $userChecker,
SessionAuthenticationStrategyInterface $sessionStrategy,
ContainerInterface $container,
Doctrine $doctrine)
{
$this->securityContext = $context;
$this->userChecker = $userChecker;
$this->sessionStrategy = $sessionStrategy;
$this->container = $container;
$this->em = $doctrine->getManager();
}
final public function loginUser($firewallName, UserInterface $user, Response $response = null)
{
$this->userChecker->checkPostAuth($user);
$token = $this->createToken($firewallName, $user);
if ($this->container->isScopeActive('request')) {
$this->sessionStrategy->onAuthentication($this->container->get('request'), $token);
if (null !== $response) {
$rememberMeServices = null;
if ($this->container->has('security.authentication.rememberme.services.persistent.'.$firewallName)) {
$rememberMeServices = $this->container->get('security.authentication.rememberme.services.persistent.'.$firewallName);
} elseif ($this->container->has('security.authentication.rememberme.services.simplehash.'.$firewallName)) {
$rememberMeServices = $this->container->get('security.authentication.rememberme.services.simplehash.'.$firewallName);
}
if ($rememberMeServices instanceof RememberMeServicesInterface) {
$rememberMeServices->loginSuccess($this->container->get('request'), $response, $token);
}
}
}
$this->securityContext->setToken($token);
// Here's the custom part, we need to get the current session and associate the user with it
$sessionId = $this->container->get('session')->getId();
$user->setSessionId($sessionId);
$this->em->persist($user);
$this->em->flush();
}
protected function createToken($firewall, UserInterface $user)
{
return new UsernamePasswordToken($user, null, $firewall, $user->getRoles());
}
}
对于更重要的交互式登录,您还应该将此添加到您的services.yml
:
login_listener:
class: Acme\Bundle\Listener\LoginListener
arguments: ['@security.context', '@doctrine', '@service_container']
tags:
- { name: kernel.event_listener, event: security.interactive_login, method: onSecurityInteractiveLogin }
以及用于交互式登录事件的后续 LoginListener.php
:
<?php
namespace Acme\Bundle\Listener;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\DependencyInjection\Container;
use Doctrine\Bundle\DoctrineBundle\Registry as Doctrine; // for Symfony 2.1.0+
/**
* Custom login listener.
*/
class LoginListener
{
/** @var \Symfony\Component\Security\Core\SecurityContext */
private $securityContext;
/** @var \Doctrine\ORM\EntityManager */
private $em;
private $container;
private $doc;
/**
* Constructor
*
* @param SecurityContext $securityContext
* @param Doctrine $doctrine
*/
public function __construct(SecurityContext $securityContext, Doctrine $doctrine, Container $container)
{
$this->securityContext = $securityContext;
$this->doc = $doctrine;
$this->em = $doctrine->getManager();
$this->container = $container;
}
/**
* Do the magic.
*
* @param InteractiveLoginEvent $event
*/
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
{
if ($this->securityContext->isGranted('IS_AUTHENTICATED_FULLY')) {
// user has just logged in
}
if ($this->securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
// user has logged in using remember_me cookie
}
// First get that user object so we can work with it
$user = $event->getAuthenticationToken()->getUser();
// Get the current session and associate the user with it
//$user->setSessionId($this->securityContext->getToken()->getCredentials());
$sessionId = $this->container->get('session')->getId();
$user->setSessionId($sessionId);
$this->em->persist($user);
$this->em->flush();
// ...
}
}
关于php - 防止 Symfony2 中的同时用户 session ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25368713/
我有一个应用程序,其中许多对象都扩展了一个抽象类,该抽象类定义了诸如 create() edit() retrieve() 和 delete()。由于每个子类对这些函数使用相同的逻辑,抽象类定义了默认
我正在使用$anchorScroll滚动到页面顶部,其中 html 元素具有 ID #brand。 AngularJS 代码: $location.hash(
我想停用我的应用程序中的右键单击,该右键单击提供了在桌面上安装应用程序的选项。我该如何做这样的事情? 最佳答案 右键单击 Visual Studio 中的项目并选择属性。那里有一个复选框“启用浏览器运
我使用 jquery 定位 div,在我的 CSS 中我有一个 div.right-sm:hover{background-color: blue} 我想使用 jquery 停止悬停: $(this
所以,我正在尝试复制 html5“占位符”属性功能。 我目前坚持的一件事是,在获得元素焦点时,插入符号立即出现在输入的开头。 就目前情况而言,插入符号出现在用户单击的位置,然后当我使用 jQuery
当表单填写并发送时,如果您刷新页面,它表示表单将再次发送。 (再次提交表格)。 防止这种情况发生的好方法是什么?或者终止这个 session ? 这方面有什么指导吗? 谢谢 最佳答案 处理完POST信
我想阻止 @ 被输入到 input 中。但它不起作用,知道为什么吗? $(function() { $(document).on('keyup', '[placeholder="x"]', fun
我正在使用 PHP 创建一个应用程序并涉及 MySQL。如果在请求过程中发生错误,我将如何“将查询分组在一起”,检查它是否会成功,然后对真实表进行实际影响。如果对表的实际更新失败,则恢复到更新之前的状
这个问题在这里已经有了答案: 关闭 11 年前。 Possible Duplicate: Best Java obfuscator ? 对于我的示例,我知道 eclipse 提供了一个反编译插件。而
这是一个演示我的问题的 fiddle :JSFiddle 我正在制作自定义下拉菜单(实际上我使用的是 icomoon 图标而不是 V)...它看起来不错,但是父元素的 ::after 是阻止选择:(
每当我编写需要大量条件的代码时,我都会这样做: if foo: if bar: if foobar: if barfoo: if foobarfoo:
我不确定术语是否正确,您可以使用哪些代码实践来使某人难以修改二进制文件/程序集以绕过检查: 例如在源代码中。 bool verificationResult = verify(); if (verif
我正在寻找一种简单的方法来检查多个零件表,以确定给定零件号在添加到给定表之前是否已经存在。 我目前想到的最好的想法是一个辅助表,它简单地将所有表中的每个 PN 列在一个列中,并带有一个唯一的键;但是我
这个问题在这里已经有了答案: jquery stop child triggering parent event (7 个答案) 关闭 8 年前。 我不确定这是否真的冒泡,我会解释。 我有这个:
我有一个 Spring MVC web 应用程序(不确定该信息是否重要,但它可能是)使用 ModelAndView 将字符串值传递给 JSP 文件。 字符串值的形式是: d@.
我在这里尝试使用表单 key 方法进行 csrf 保护 http://net.tutsplus.com/tutorials/php/secure-your-forms-with-form-keys/
htmlentities 是防止 PHP 中的 XSS 的最佳解决方案吗?我还想允许像 b、i、a 和 img 这样的简单标签。实现这一点的最佳解决方案是什么?我确实考虑过 bbcode,但发现如果没
我有一个非常基本的 JAX-RS 服务(下面的 BookService 类),它允许创建 Book 类型的实体(也在下面)。 POST负载 { "acquisitionDate": 14188
我正在使用 Polymer 1.5,我确实需要“this”变量不要映射到外部。我知道 typescript 会为某些人做这件事 valid reasons . declare var Polymer:
这个问题在这里已经有了答案: Class-level read-only properties in Python (3 个答案) 关闭 6 年前。 有没有一种方法可以通过重写实例变量的 __set
我是一名优秀的程序员,十分优秀!