Files
laralog/app/Models/User.php
T
gouki 2d8752f863
Build and Deploy / verify (push) Successful in 10s
Build and Deploy / deploy (push) Successful in 17s
fix(auth): return empty string from getAuthPassword for legacy null passwords (recaller hash_equals TypeError)
2026-10-12 23:07:10 +00:00

123 lines
2.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace App\Models;
use Database\Factories\UserFactory;
use Filament\Models\Contracts\FilamentUser;
use Filament\Panel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Illuminate\Support\Facades\Hash;
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable implements FilamentUser
{
/** @use HasFactory<UserFactory> */
use HasFactory, Notifiable, HasRoles, HasApiTokens;
public function canAccessPanel(Panel $panel): bool
{
return $this->canAccessAdmin();
}
public function canAccessAdmin(): bool
{
return $this->hasRole(['admin', 'editor']);
}
/**
* The attributes that are mass assignable.
*
* @var list<string>
*/
protected $fillable = [
'name',
'email',
'password',
'legacy_md5',
'url',
'logincount',
'loginip',
'regip',
'logintime',
'lastpost_at',
];
/**
* The attributes that should be hidden for serialization.
*
* @var list<string>
*/
protected $hidden = [
'password',
'remember_token',
'legacy_md5',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
'logintime' => 'datetime',
'lastpost_at' => 'datetime',
];
}
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
/**
* 兼容老用户 password 为 null:remember-me 校验时 hash_equals 不接受 null,
* 返回空串使其校验失败并忽略 recaller,而不是抛 TypeError 造成全站 500。
*/
public function getAuthPassword()
{
return $this->password ?? '';
}
public function comments(): HasMany
{
return $this->hasMany(Comment::class);
}
/**
* 验证登录:优先新密码哈希,兼容 sablog 老 MD5 密码。
*/
public function verifyPassword(string $plain): bool
{
if ($this->password && Hash::check($plain, $this->password)) {
return true;
}
if ($this->legacy_md5 && md5($plain) === strtolower($this->legacy_md5)) {
// 登录成功后自动升级为现代哈希
$this->password = Hash::make($plain);
$this->legacy_md5 = null;
$this->save();
return true;
}
return false;
}
public function isAdmin(): bool
{
return $this->hasRole('admin');
}
}