使用 PGSQL 和 SQLite 登录区分大小写

login is case sensitive using PGSQL and SQLite

所以,我使用 Laravel 9x 和 Jetstream 和 Inertia/Vue

我注意到使用 PGSQL 和 SQLite 时,电子邮件登录区分大小写。解决此问题的解决方案是什么?我知道我没有发布代码,因为它似乎没有必要,但如果你想看什么,请告诉我。

我也听说 MySQL 不会发生这种情况,但我还没有测试过。

我不能具体与 Jetstream 交谈,但我们的应用程序使用 FormRequest 类 作为登录、密码重置等表单。

在每个 FormRequest 中,我们使用 Laravel 的 prepareForValidation() 方法来处理传入的数据。

protected function prepareForValidation($attributes) {
    $attributes['email'] = strtolower(trim($attributes['email']));

    return $attributes;
}

此外,在用户模型中,我们在电子邮件 属性 上有一个 Eloquent mutator 以确保对该字段的更新进行规范化:

public function setEmailAttribute($value) {
    $this->attributes['email'] =  trim(strtolower($value));
}

(您也可以在 saving 事件中通过 Eloquent 观察者处理此问题。)

更改您的应用程序以执行此操作后,您可能希望 运行 对数据库进行 one-time 更新,以将用户 table 中的现有电子邮件值小写。忘记这样做会使使用大写字符的用户无法登录。

一般来说,我认为保留用户输入是一个好主意,因为用户提供它,而不是在将它们保存到数据库之前对其进行规范化(即将电子邮件转换为统一的大小写)。例如,如果用户输入 MrSmith@gmail.com 作为他们的电子邮件,我更愿意在后续页面上向他们显示 MrSmith@gmail.com,而不是将其转换为 mrsmith@gmail.com 只是为了使其在数据库中可搜索。显然用户电子邮件不是 case-sensitive,但我认为软件丢弃用户喜欢使用的外壳有点粗鲁,在某些情况下,这可能会使不明白电子邮件是 [=59] 的不那么精明的用户感到困惑=].

虽然这适用于电子邮件,但 通常 能够以 case-insensitive 方式搜索某些字段,同时保留准确的原始值是很重要的。例如,您不希望转换某人地址的大小写只是为了使其可搜索。

您可以在 Postgres 中通过创建和使用 case-insensitive 索引来实现这一点 - 更具体地说,通过使用 lower 转换来规范索引中的大小写来创建索引,但是 不是列本身。

例如,给定一个 users table 和一个 email 列,您通常使用这样的方式创建索引:

create unique index users_email_idx on users (email);

相反,您可以在 lower(email):

上创建索引
create unique index users_email_idx on users (lower(email));

这允许写入列的值保留其原始大小写。插入MrSmith@gmail.com时,列中保留的值为MrSmith@gmail.com,但写入索引的值为mrsmith@gmail.com.

有几点需要注意:

首先,这以 case-insensitive 的方式强制唯一性,因此记录 foo@bar.comFoo@Bar.Com 不能在数据库中共存。这通常对于电子邮件来说既好又必要,但可能会导致其他类型的数据出现问题。

其次,为了在查询时使用索引,您需要稍微调整查询以匹配索引:

select * from users where lower(email) = 'foo@bar.com'; -- uses index
select * from users where email = 'foo@bar.com' -- does not use index

第二次查询有问题;它不仅会遗漏大小写不匹配的记录,还会完全遗漏索引并对您的数据库执行顺序扫描,这通常非常缓慢且代价高昂。

最后,值得明确提及的是,在查询中使用之前,您必须规范化搜索词。如果用户输入 MrSmith@gmail.com,您必须将输入转换为索引中使用的相同大小写,否则将不匹配。这可以在将字符串发送到数据库之前在应用程序代码中完成,或者您可以在数据库中比较的两边使用 lower

-- works, database normalizes input:
select * from users where lower(email) = lower('MrSmith@gmail.com')

-- works, application has already normalized input
select * from users where lower(email) = 'mrsmith@gmail.com'

-- No error, but cannot possibly match any records
select * from users where lower(email) = 'MrSmith@gmail.com'