【问题标题】:How to make username case insensitive in zf2如何在 zf2 中使用户名不区分大小写
【发布时间】:2018-04-28 07:05:51
【问题描述】:

我在我的项目中使用 zf2 身份验证对用户进行身份验证。我将 Harib 作为用户名保存在我的用户表中,但如果我使用我的用户名 Harib 然后它接受,或者如果我使用 harib 然后它不接受,我想删除大小写用户名的敏感性,所以 Harib 或 harib 都可以访问我如何解决这个问题?

这是我的代码:

public function loginAction()
{
    $this->layout('layout/login-layout.phtml');
    $login_error = false;
    $loginForm = new LoginForm();
    $form_elements = json_encode($loginForm->form_elements);
    if ($this->request->isPost()) {
        $post = $this->request->getPost();
        $loginForm->setData($post);
        if ($loginForm->isValid()) {
            $hashed_string = '';
            if(
                array_key_exists('hashed_input' , $post) &&
                $post['hashed_input'] != '' &&
                strpos(urldecode($this->params('redirect')) , 'programdetailrequest') !== false
            ) {
                $hashed_string = $post['hashed_input'];
            }
            $data = $loginForm->getData();
            $authService = $this->getServiceLocator()->get('doctrine.authenticationservice.odm_default');
            $adapter = $authService->getAdapter();
            $adapter->setIdentityValue($data['username']);
            $adapter->setCredentialValue(md5($data['password']));
            $authResult = $authService->authenticate();
            if($authResult->isValid()){
                $identity = $authResult->getIdentity();
                if( is_object($identity) && method_exists($identity, 'getData') ){
                    $user_data = $identity->getData();
                    $authService->getStorage()->write($identity);
                    // for remeber checkbox
                    if ($post['rememberme']) {
                        $token = new UserToken();
                        $dm = $this->getServiceLocator()->get('doctrine.documentmanager.odm_default');
                        //if same user already running from other browser then remove previous token.
                        $check_token = $dm->getRepository('Admin\Document\UserToken')->findOneBy(array( "user_id.id" => $user_data['id'] ));
                        if (is_object($check_token) && !is_null($check_token)) {
                            $remove_token = $dm->createQueryBuilder('Admin\Document\UserToken')
                                ->remove()
                                ->field('id')->equals($check_token->id)
                                ->getQuery()->execute();
                        }
                        //create token
                        $user = $dm->getRepository('Admin\Document\User')->findOneBy(array( "id" => $user_data['id'] ));
                        $token->setProperty('user_id', $user);
                        $token->setProperty('dataentered', new \MongoDate());
                        $dm->persist($token);
                        $dm->flush($token);
                        //create cookie
                        if(is_object($token) && property_exists($token, 'id')){
                            $time = time() + (60 * 60 * 24 * 30); // 1 month
                            setcookie('token', $token->getProperty('id'), $time, '/');
                        }
                    }
                    if ($user_data['user_type'] == 'onlinemarketer') {
                        $this->redirect()->toRoute('admin_program_meta');
                    } elseif ($user_data['user_type'] == 'bucharestofficemanager') {
                        $this->redirect()->toRoute('admin_program_detail_request');
                    } else {
                        if ($this->params('redirect') && urldecode($this->params('redirect')) !== '/logout/') {
                            $server_url = $this->getRequest()->getUri()->getScheme() . '://' . $this->getRequest()->getUri()->getHost().urldecode($this->params('redirect') . $hashed_string);
                            return $this->redirect()->toUrl($server_url);
                        }
                        return $this->redirect()->toRoute('admin_index');
                    }
                }
            } else {
                $identity = false;
                $login_error = true;
            }
        }
    }
    return new ViewModel(array(
            'loginForm' => $loginForm,
            'form_elements' =>$form_elements,
            'login_error' => $login_error,
    ));
}

这是我的登录表单代码:

<?php
namespace Admin\Form;

use Zend\Form\Form;
use Zend\Form\Element;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\Factory as InputFactory;

class LoginForm extends Form implements InputFilterAwareInterface
{
protected $inputFilter;
public $form_elements = array(
    array(
        'name' => 'username',
        'attributes' => array(
            'id' => 'username',
            'type'  => 'text',
            'error_msg' => 'Enter Valid Username',
            'data-parsley-required' => 'true',
            'data-parsley-pattern' => '^[a-zA-Z0-9_\.\-]{1,50}$',
            'data-parsley-trigger' => 'change'
        ),
        'options' => array(
            'label' => 'User Name'
        ), 
        'validation' => array(
            'required'=>true,
            'filters'=> array(
                array('name'=>'StripTags'),
                array('name'=>'StringTrim')
            ),
            'validators'=>array(
                array('name'=>'Regex',
                    'options'=> array(
                        'pattern' => '/^[a-z0-9_.-]{1,50}+$/', // contain only a to z 0 to 9 underscore, hypen and space, min 1 max 50
                        'pattern_js' => '^[a-zA-Z0-9_\.\-]{1,50}$' 
                    )
                )
            )
        )
    ),
    array(
        'name' => 'password',
        'attributes' => array(
            'id' => 'password',
            'type'  => 'password',
            'error_msg' => 'Enter Valid Password',
            'data-parsley-required' => 'true',
            'data-parsley-pattern' => '^[a-zA-Z0-9_\.\-]{6,25}$',
            'data-parsley-trigger' => 'change'
        ),
        'options' => array(
            'label' => 'Password'
        ), 
        'validation' => array(
            'required' => true,
            'filters'=> array(
                array('name'=>'StripTags'),
                array('name'=>'StringTrim')
            ),
            'validators'=>array(
                array('name'=>'Regex',
                    'options'=> array(
                        'pattern' => '/^[a-z0-9_.-]{6,25}+$/', // contain only a to z 0 to 9 underscore, hypen and space, min 1 max 50
                        'pattern_js' => '^[a-zA-Z0-9_\.\-]{6,25}$' 
                    )
                )
            )
        )
    ),
    array(
        'name' => 'hashed_input',
        'attributes' => array(
            'type'  => 'hidden',
            'id' => 'hashed_input',
            'value' => ''
        )
    ),
    array(
        'name' => 'rememberme',
        'attributes' => array(
            'value' => 1,
            'id' => 'rememberme',
            'type' => 'Checkbox'
        ),
        'options' => array(
            'label' => 'Remember Me',
            'use_hidden_element' => false,
        )
    ),
    array(
        'name' => 'submit',
        'attributes' => array(
            'type'  => 'submit',
            'value' => 'Log in',
            'id' => 'submitbutton'
        )
    )
);
public function __construct()
{
    parent::__construct('user');
    $this->setAttribute('method', 'post');
    $this->setAttribute('data-parsley-validate', '');
    $this->setAttribute('data-elements', json_encode($this->form_elements));
    $this->setAttribute('autocomplete', 'off');
    for($i=0;$i<count($this->form_elements);$i++){
        $elements=$this->form_elements[$i];
        $this->add($elements);
    }
}
public function getInputFilter($action=false)
{
    if(!$this->inputFilter){
        $inputFilter = new InputFilter();
        $factory = new InputFactory();
        for($i=0;$i<count($this->form_elements);$i++){
            if(array_key_exists('validation',$this->form_elements[$i])){    
                $this->form_elements[$i]['validation']['name']=$this->form_elements[$i]['name'];
                $inputFilter->add($factory->createInput( $this->form_elements[$i]['validation'] ));
            }
        }
        $this->inputFilter = $inputFilter;
    }
    return $this->inputFilter;
}
}

我们如何删除用户名的大小写敏感性以便 Harib 或 harib 都被接受?

【问题讨论】:

  • 你用的是什么数据库?
  • 我用的是mongodb数据库
  • 在这种情况下,您应该为用户名字段创建一个case-insensitive index,这样当数据库适配器搜索它时,它会不分大小写地找到记录。
  • 嗨@drew010,您看到我的控制器代码和表单代码,因为您看到我使用了zf2身份验证,所以请根据此建议
  • 我相信最好的解决方案是修复您的数据库索引/排序规则,以便查找不区分大小写。否则,您将需要修复所有可能的 PHP 代码,这些代码会查找用户名以将其过滤为小写,并且可能会遇到其他问题。如果您在数据库级别修复它,则您的应用程序代码都不需要更改或有特殊情况来过滤用户名。如果您想走代码路线,请使用下面的 Alain 代码;只要确保您不允许插入带有任何大写字母的用户名!

标签: php authentication doctrine-orm zend-framework2 doctrine-odm


【解决方案1】:

loginform 元素 user_id 上添加过滤器 StringToLower

为此,定义您的loginform 的类必须实现InputFilterProviderInterface,并且您必须添加getInputFilterSpecification 方法,如下所示:

public function getInputFilterSpecification()
{
    return [
        'username' => [
            'name' => 'username',
            'required' => true,
            'filters' => [
                'name' => 'StringToLower',
                'name'=>'StripTags',
                'name'=>'StringTrim' 
            ],
            validators => [
                [
                    'name'=>'Regex',
                    'options'=> [
                        'pattern' => '/^[a-z0-9_.-]{1,50}+$/', 
                        'pattern_js' => '^[a-zA-Z0-9_\.\-]{1,50}$' 
                    ]
                ]
            ] 
        ],
        'password' => [
            'name' => 'password',
            'required' => true,
            'filters' => [
                array('name'=>'StripTags'),
                array('name'=>'StringTrim')
            ],
            'validators' => [
                [
                    'name'=>'Regex',
                    'options'=> [
                    'pattern' => '/^[a-z0-9_.-]{6,25}+$/', 
                    'pattern_js' => '^[a-zA-Z0-9_\.\-]{6,25}$' 
                    ]
                ]
            ]
        ]
    ];
}

所以您可以放心,帖子中返回的值是小写的。

【讨论】:

  • 嗨@Alian Promirol 我附上了我的登录表单代码,你能告诉我我在表单中改变了什么吗?
  • 我个人更喜欢InputFilterProviderInterface接口而不是InputFilterAwareInterface,我不实现getInputFilter()方法,除非我想改变ZF2的默认行为并且我实现getInputFilterSpecification()方法如我的回答所示。
【解决方案2】:

您可以通过两种方式做到这一点。您可以创建自定义身份验证适配器或覆盖默认身份验证适配器的方法。我建议重写该方法,这比创建自定义适配器更容易。

所以这里是方法CredentialTreatmentAdapter::authenticateCreateSelect()。如果您从zend-authentication 组件中查找该方法的94 行(zf 2.5),那么您会找到以下行。

$dbSelect->from($this->tableName)
    ->columns(['*', $credentialExpression])
    // See the making of where clause
    ->where(new SqlOp($this->identityColumn, '=', $this->identity));

在这里,我们将进行更改。现在让我们通过扩展Zend\Authentication\Adapter\DbTable 来覆盖该方法。因此,我们将创建一个 where 子句来搜索 Haribharib。请参阅以下扩展CustomDbTable::class

<?php
namespace Define\Your\Own\Namespace;

use Zend\Authentication\Adapter\DbTable;

class CustomDbTable extends DbTable
{
    protected function authenticateCreateSelect()
    {
        // build credential expression
        if (empty($this->credentialTreatment) || (strpos($this->credentialTreatment, '?') === false)) {
            $this->credentialTreatment = '?';
        }

        $credentialExpression = new SqlExpr(
            '(CASE WHEN ?' . ' = ' . $this->credentialTreatment . ' THEN 1 ELSE 0 END) AS ?',
            array($this->credentialColumn, $this->credential, 'zend_auth_credential_match'),
            array(SqlExpr::TYPE_IDENTIFIER, SqlExpr::TYPE_VALUE, SqlExpr::TYPE_IDENTIFIER)
        );

        // Here is the catch
        $where = new \Zend\Db\Sql\Where();
        $where->nest()
            ->equalTo($this->identityColumn, $this->identity)
            ->or
            ->equalTo($this->identityColumn, strtolower($this->identity))
            ->unnest();

        // get select
        $dbSelect = clone $this->getDbSelect();
        $dbSelect->from($this->tableName)
            ->columns(array('*', $credentialExpression))
            ->where($where); // Here we are making our own where clause

        return $dbSelect;
    }
}

现在自定义身份验证适配器已准备就绪。您需要在工厂内部使用这个来进行身份验证服务,而不是Zend\Authentication\Adapter\DbTable,如下

'factories' => array(

    // Auth service
    'AuthService' => function($sm) {
        $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');

        // Use CustomDbTable instead of DbTable here
        $customDbTable = new CustomDbTable($dbAdapter, 'tableName', 'usernameColumn', 'passwordColumn', 'MD5(?)');
        $authService = new AuthenticationService();
        $authService->setAdapter($customDbTable);

        return $authService;
    },
),

现在一切都设置好了。每当您在控制器方法中调用此方法时,都应调用该重写方法:

$authResult = $authService->authenticate();

这未经测试。因此,您可能需要在需要的地方进行更改。如果需要,请修复它们。

希望对您有所帮助!

【讨论】:

  • 嗨@unclexo 我用的是mongodb而不是sql
  • 我没有注意到您使用的是 mongodb。我真的很抱歉。但是你能炫耀identityClass吗?或者你正在处理 mongodb 的东西?
  • 嗨@unclexo 我使用了zend身份验证,你看到了我上面的所有代码
  • 嗯,与 mongodb 的处理已经在您在控制器方法中使用的此服务 doctrine.authenticationservice.odm_default 中进行。如果您可以找到用户名匹配的条件,那么您就可以将您的规则作为@Pascut 的答案应用。
【解决方案3】:

由于您使用的是 MongoDB,因此您可以使用正则表达式从数据库中获取用户名。

建议一:

在你的例子中是:

db.stuff.find( { foo: /^bar$/i } );

建议 2:

您可以使用 $options => i 进行不区分大小写的搜索。给出字符串匹配所需的一些可能的例子。

不区分大小写的字符串

db.collection.find({name:{'$regex' : '^string$', '$options' : 'i'}})

包含字符串

db.collection.find({name:{'$regex' : 'string', '$options' : 'i'}})

以字符串开头

db.collection.find({name:{'$regex' : '^string', '$options' : 'i'}})

以字符串结尾

db.collection.find({name:{'$regex' : 'string$', '$options' : 'i'}})

不包含字符串

db.collection.find({name:{'$regex' : '^((?!string).)*$', '$options' : 'i'}})

更多关于 MongoDb 正则表达式的信息:https://docs.mongodb.com/manual/reference/operator/query/regex/index.html

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-04
    • 2020-09-15
    • 2015-02-24
    • 2019-09-16
    • 1970-01-01
    相关资源
    最近更新 更多