Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 150 additions & 21 deletions app/Http/Controllers/AuthController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@
use App\Models\Player;
use App\Models\User;
use App\Rules;
use Auth;
use Blessing\Filter;
use Blessing\Rejection;
use Cache;
use Carbon\Carbon;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\URL;
use Mail;
use Session;
use URL;
use Vectorface\Whip\Whip;

class AuthController extends Controller
Expand Down Expand Up @@ -50,7 +50,7 @@ public function handleLogin(
Request $request,
Rules\Captcha $captcha,
Dispatcher $dispatcher,
Filter $filter,
Filter $filter
) {
$data = $request->validate([
'identification' => 'required',
Expand Down Expand Up @@ -151,7 +151,7 @@ public function handleRegister(
Request $request,
Rules\Captcha $captcha,
Dispatcher $dispatcher,
Filter $filter,
Filter $filter
) {
$can = $filter->apply('can_register', null);
if ($can instanceof Rejection) {
Expand All @@ -176,8 +176,8 @@ public function handleRegister(
$dispatcher->dispatch('auth.registration.attempt', [$data]);

if (
option('register_with_player_name')
&& Player::where('name', $playerName)->count() > 0
option('register_with_player_name') &&
Player::where('name', $playerName)->count() > 0
) {
return json(trans('user.player.add.repeated'), 1);
}
Expand Down Expand Up @@ -248,7 +248,7 @@ public function handleForgot(
Request $request,
Rules\Captcha $captcha,
Dispatcher $dispatcher,
Filter $filter,
Filter $filter
) {
$data = $request->validate([
'email' => 'required|email',
Expand Down Expand Up @@ -279,12 +279,25 @@ public function handleForgot(

$dispatcher->dispatch('auth.forgot.ready', [$user]);

$url = URL::temporarySignedRoute(
// 生成带有时间戳的签名
$timestamp = time();
$uid = $user->uid;

// 使用应用密钥、时间戳和用户ID生成签名
$signature = hash_hmac('sha256', "{$uid}:{$timestamp}", config('app.key'));

// 存储签名和过期时间到数据库
$user->password_reset_signature = $signature;
$user->password_reset_expires_at = Carbon::now()->addHour();
$user->save();

// 生成重置链接
$url = URL::route(
'auth.reset',
Carbon::now()->addHour(),
['uid' => $user->uid],
['uid' => $uid, 'timestamp' => $timestamp, 'signature' => $signature],
false
);

try {
Mail::to($email)->send(new ForgotPassword(url($url)));
} catch (\Exception $e) {
Expand All @@ -302,22 +315,81 @@ public function handleForgot(

public function reset(Request $request, $uid)
{
abort_unless($request->hasValidSignature(false), 403, trans('auth.reset.invalid'));
$signature = $request->input('signature');
$timestamp = $request->input('timestamp');

// 验证参数完整性
if (!$signature || !$timestamp) {
abort(403, trans('auth.reset.invalid'));
}

$user = User::find($uid);
if (!$user) {
abort(403, trans('auth.reset.invalid'));
}

// 验证签名匹配
if ($user->password_reset_signature !== $signature) {
abort(403, trans('auth.reset.invalid'));
}

// 验证签名未过期
if (Carbon::parse($user->password_reset_expires_at)->isPast()) {
abort(403, trans('auth.reset.expired'));
}

// 验证时间戳是否匹配签名生成时的时间戳
$expectedSignature = hash_hmac('sha256', "{$uid}:{$timestamp}", config('app.key'));
if ($signature !== $expectedSignature) {
abort(403, trans('auth.reset.invalid'));
}

return view('auth.reset')->with('user', User::find($uid));
return view('auth.reset')->with('user', $user);
}

public function handleReset(Dispatcher $dispatcher, Request $request, $uid)
{
abort_unless($request->hasValidSignature(false), 403, trans('auth.reset.invalid'));
$signature = $request->input('signature');
$timestamp = $request->input('timestamp');

// 验证参数完整性
if (!$signature || !$timestamp) {
return json(trans('auth.reset.invalid'), 1);
}

$user = User::find($uid);
if (!$user) {
return json(trans('auth.reset.invalid'), 1);
}

// 验证签名匹配
if ($user->password_reset_signature !== $signature) {
return json(trans('auth.reset.invalid'), 1);
}

// 验证签名未过期
if (Carbon::parse($user->password_reset_expires_at)->isPast()) {
return json(trans('auth.reset.expired'), 1);
}

// 验证时间戳是否匹配签名生成时的时间戳
$expectedSignature = hash_hmac('sha256', "{$uid}:{$timestamp}", config('app.key'));
if ($signature !== $expectedSignature) {
return json(trans('auth.reset.invalid'), 1);
}

['password' => $password] = $request->validate([
'password' => 'required|min:8|max:32',
]);
$user = User::find($uid);

$dispatcher->dispatch('auth.reset.before', [$user, $password]);
$user->changePassword($password);

// 清除数据库中的签名,确保一次性使用
$user->password_reset_signature = null;
$user->password_reset_expires_at = null;
$user->save();

$dispatcher->dispatch('auth.reset.after', [$user, $password]);

return json(trans('auth.reset.success'), 0);
Expand All @@ -344,20 +416,74 @@ public function fillEmail(Request $request)
return redirect('/user');
}

public function verify(Request $request)
public function verify(Request $request, $uid)
{
if (!option('require_verification')) {
throw new PrettyPageException(trans('user.verification.disabled'), 1);
}

abort_unless($request->hasValidSignature(false), 403, trans('auth.verify.invalid'));
$signature = $request->input('signature');
$timestamp = $request->input('timestamp');

// 验证参数完整性
if (!$signature || !$timestamp) {
abort(403, trans('auth.verify.invalid'));
}

$user = User::find($uid);
if (!$user) {
abort(403, trans('auth.verify.invalid'));
}

// 验证签名匹配
if ($user->email_verification_signature !== $signature) {
abort(403, trans('auth.verify.invalid'));
}

// 验证签名未过期
if (Carbon::parse($user->email_verification_expires_at)->isPast()) {
abort(403, trans('auth.verify.invalid'));
}

// 验证时间戳是否匹配签名生成时的时间戳
$expectedSignature = hash_hmac('sha256', "{$uid}:{$timestamp}", config('app.key'));
if ($signature !== $expectedSignature) {
abort(403, trans('auth.verify.invalid'));
}

return view('auth.verify');
}

public function handleVerify(Request $request, User $user)
public function handleVerify(Request $request, $uid)
{
abort_unless($request->hasValidSignature(false), 403, trans('auth.verify.invalid'));
$signature = $request->input('signature');
$timestamp = $request->input('timestamp');

// 验证参数完整性
if (!$signature || !$timestamp) {
abort(403, trans('auth.verify.invalid'));
}

$user = User::find($uid);
if (!$user) {
abort(403, trans('auth.verify.invalid'));
}

// 验证签名匹配
if ($user->email_verification_signature !== $signature) {
abort(403, trans('auth.verify.invalid'));
}

// 验证签名未过期
if (Carbon::parse($user->email_verification_expires_at)->isPast()) {
abort(403, trans('auth.verify.invalid'));
}

// 验证时间戳是否匹配签名生成时的时间戳
$expectedSignature = hash_hmac('sha256', "{$uid}:{$timestamp}", config('app.key'));
if ($signature !== $expectedSignature) {
abort(403, trans('auth.verify.invalid'));
}

['email' => $email] = $request->validate(['email' => 'required|email']);

Expand All @@ -366,6 +492,9 @@ public function handleVerify(Request $request, User $user)
}

$user->verified = true;
// 清除数据库中的签名,确保一次性使用
$user->email_verification_signature = null;
$user->email_verification_expires_at = null;
$user->save();

return redirect()->route('user.home');
Expand Down
33 changes: 25 additions & 8 deletions app/Http/Controllers/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@
use App\Mail\EmailVerification;
use App\Models\Texture;
use App\Models\User;
use Auth;
use Blessing\Filter;
use Blessing\Rejection;
use Carbon\Carbon;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\URL;
use League\CommonMark\GithubFlavoredMarkdownConverter;
use Mail;
use Session;
use URL;

class UserController extends Controller
{
Expand Down Expand Up @@ -157,7 +157,24 @@ public function sendVerificationEmail()
return json(trans('user.verification.verified'), 1);
}

$url = URL::signedRoute('auth.verify', ['user' => $user], null, false);
// 生成带有时间戳的签名
$timestamp = time();
$uid = $user->uid;

// 使用应用密钥、时间戳和用户ID生成签名
$signature = hash_hmac('sha256', "{$uid}:{$timestamp}", config('app.key'));

// 存储签名和过期时间到数据库
$user->email_verification_signature = $signature;
$user->email_verification_expires_at = Carbon::now()->addHour();
$user->save();

// 生成验证链接
$url = URL::route(
'auth.verify',
['uid' => $uid, 'timestamp' => $timestamp, 'signature' => $signature],
false
);

try {
Mail::to($user->email)->send(new EmailVerification(url($url)));
Expand Down Expand Up @@ -330,9 +347,9 @@ public function setAvatar(Request $request, Filter $filter, Dispatcher $dispatch
}

if (
!$texture->public
&& $user->uid !== $texture->uploader
&& !$user->isAdmin()
!$texture->public &&
$user->uid !== $texture->uploader &&
!$user->isAdmin()
) {
return json(trans('skinlib.show.private'), 1);
}
Expand Down
20 changes: 19 additions & 1 deletion app/Listeners/SendEmailVerification.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use App\Mail\EmailVerification;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\URL;

Expand All @@ -12,7 +13,24 @@ class SendEmailVerification
public function handle(User $user)
{
if (option('require_verification')) {
$url = URL::signedRoute('auth.verify', ['user' => $user->uid], null, false);
// 生成带有时间戳的签名
$timestamp = time();
$uid = $user->uid;

// 使用应用密钥、时间戳和用户ID生成签名
$signature = hash_hmac('sha256', "{$uid}:{$timestamp}", config('app.key'));

// 存储签名和过期时间到数据库
$user->email_verification_signature = $signature;
$user->email_verification_expires_at = Carbon::now()->addHour();
$user->save();

// 生成验证链接
$url = URL::route(
'auth.verify',
['uid' => $uid, 'timestamp' => $timestamp, 'signature' => $signature],
false
);

try {
Mail::to($user->email)->send(new EmailVerification(url($url)));
Expand Down
2 changes: 2 additions & 0 deletions app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ class User extends Authenticatable

protected $fillable = [
'email', 'nickname', 'avatar', 'score', 'permission', 'last_sign_at',
'password_reset_signature', 'password_reset_expires_at',
'email_verification_signature', 'email_verification_expires_at',
];

protected $casts = [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;

class AddVerificationSignatureFields extends Migration
{
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('verification_signature')->nullable();
$table->timestamp('signature_expires_at')->nullable();
});
}

public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn(['verification_signature', 'signature_expires_at']);
});
}
}
Loading
Loading