feat: 邮箱验证部分

This commit is contained in:
韦荣超 2022-03-02 18:11:19 +08:00
parent d9b9c0f42c
commit 68f5b30f7d
9 changed files with 303 additions and 0 deletions

View File

@ -3,12 +3,17 @@
namespace App\Http\Controllers\Api;
use App\Models\User;
use App\Models\UserEmailVerification;
use App\Module\Base;
use Arr;
use Cache;
use Captcha;
use Carbon\Carbon;
use Config;
use Mail;
use Request;
use Exception;
use Validator;
/**
* @apiDefine users
@ -53,6 +58,10 @@ class UsersController extends AbstractController
}
}
$user = User::reg($email, $password);
$isRegVerify = false;//Base::settingFind('emailSetting', 'reg_verify') === 'open' ? true : false;
if ($isRegVerify) {
UserEmailVerification::userEmailSend($user);
}
} else {
$needCode = !Base::isError(User::needCode($email));
if ($needCode) {
@ -559,4 +568,53 @@ class UsersController extends AbstractController
//
return Base::retSuccess('修改成功', $userInfo);
}
/**
* @api {get} api/users/email/verification 13. 邮箱验证
*
* @apiDescription 不需要token身份
* @apiVersion 1.0.0
* @apiGroup users
* @apiName email__verification
*
* @apiParam {String} code 验证参数
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据(同"获取我的信息"接口)
*/
public function email__verification()
{
$data = Request::input();
// 表单验证
$validator = Validator::make($data, [
"code" => ["required"],
], [
"code.required" => "required字段非法",
]);
if ($validator->fails())
return Base::retError($validator->errors()->first());
$res = UserEmailVerification::where('code', $data['code'])->first();
if (empty($res)) {
return Base::retError('链接已失效');
}
// 如果已经校验过
if (intval($res->status) === 1)
return Base::retError('链接已经使用过');
$oldTime = strtotime($res->created_at);
$time = time();
//24个小时失效
if (abs($time - $oldTime) > 86400) {
return Base::retError("链接已失效");
}
UserEmailVerification::where('code', $data['code'])
->update([
'status' => 1
]);
User::where('userid', $res->userid)->update([
'is_email_verity' => 1
]);
return Base::retSuccess('绑定邮箱成功');
}
}

View File

@ -29,6 +29,7 @@ use Carbon\Carbon;
* @property int|null $task_dialog_id 最后打开的任务会话ID
* @property string|null $created_ip 注册IP
* @property string|null $disable_at 禁用时间
* @property int $is_email_verity 邮箱是否已验证
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Database\Factories\UserFactory factory(...$parameters)
@ -43,6 +44,7 @@ use Carbon\Carbon;
* @method static \Illuminate\Database\Eloquent\Builder|User whereEmail($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereEncrypt($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereIdentity($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereIsEmailVerity($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereLastAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereLastIp($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereLineAt($value)

View File

@ -0,0 +1,69 @@
<?php
namespace App\Models;
use App\Exceptions\ApiException;
use App\Module\Base;
use Config;
use Exception;
use Mail;
/**
* App\Models\UserEmailVerification
*
* @property int $id
* @property int $userid 用户id
* @property string $code 验证参数
* @property string $email 电子邮箱
* @property string $status 0-未验证1-已验证
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket query()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket whereCode($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket whereEmail($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket whereStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket whereUserid($value)
* @mixin \Eloquent
*/
class UserEmailVerification extends AbstractModel
{
/**
* 发验证邮箱
* @param User $user
*/
public static function userEmailSend(User $user)
{
//删除
self::where('userid', $user->userid)->delete();
$info['created_at'] = date("Y-m-d H:i:s");
$info['userid'] = $user->userid;
$info['email'] = $user->email;
$info['code'] = md5(uniqid(md5(microtime(true)), true)) . md5($user->userid . md5('lddsgagsgkdiid' . microtime(true)));
$url = Base::fillUrl('valid/email') . '?code=' . $info['code'];
$info['status'] = 0;
$userEmailVerification = self::createInstance($info);
$userEmailVerification->save();
try {
// 15秒后超时
Mail::send('email', ['url' => $url], function ($m) use ($user) {
$m->from(Base::settingFind('emailSetting', 'account') ?? Config::get("mail.mailers.smtp.username"), env('APP_NAME'));
$m->to($user->email);
$m->subject("绑定邮箱验证");
});
} catch (Exception $exception) {
// 一般是请求超时
if (strpos($exception->getMessage(), "Timed Out") !== false)
throw new ApiException("language.TimedOut");
else
throw new ApiException($exception->getMessage());
}
}
}

View File

@ -8,6 +8,7 @@ namespace App\Models;
* @property int $id
* @property int|null $dialog_id 对话ID
* @property int|null $userid 会员ID
* @property int|null $top 是否置顶0否1是
* @property string|null $top_at 置顶时间
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
@ -17,6 +18,7 @@ namespace App\Models;
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereDialogId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereTop($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereTopAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereUserid($value)

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddIsEmailVerityToUsers extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->boolean('is_email_verity')->default(0)->nullable(false)->after('disable_at')->comment('邮箱是否已验证');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn("is_email_verity");
});
}
}

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUserEmailVerificationsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('user_email_verifications', function (Blueprint $table) {
$table->id();
$table->integer('userid')->nullable()->default(0)->comment('用户id');
$table->string('code')->nullable()->default('')->comment('验证参数');
$table->string('email')->nullable()->default('')->comment('电子邮箱');
$table->integer('status')->nullable()->default(0)->comment('0-未验证1-已验证');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('user_email_verifications');
}
}

View File

@ -0,0 +1,84 @@
<template>
<div class="valid-wrap">
<div class="valid-box">
<div class="valid-title">{{$L('验证邮箱')}}</div>
<Spin size="large" v-if="!success && !error"></Spin>
<div class="validation-text" v-if="success">
<p>{{$L('您的邮箱已通过验证')}}</p>
<p>{{$L('今后您可以通过此邮箱重置您的账号密码')}}</p>
</div>
<div class="validation-text" v-if="error">
<div>{{$L('您的邮箱未通过验证,可能的原因:')}}</div>
<div>{{$L('1.验证码已过期')}}</div>
<div>{{$L('2.重复使用验证码')}}</div>
</div>
<div slot="footer" v-if="success">
<Button type="primary" @click="$A.userLogoutV2()" long>{{$L('返回首页')}}</Button>
</div>
</div>
</div>
</template>
<script>
export default {
name: "validEmail",
components: {},
data() {
return {
success: false,
error: false,
}
},
mounted() {
this.verificationEmail();
},
watch: {},
methods: {
verificationEmail() {
this.$store
.dispatch("call", {
url: "users/email/verification",
data: {
code: this.$route.query.code
}
})
.then(({data}) => {
this.success = true;
this.error = false;
})
.catch(() => {
this.success = false;
this.error = true;
});
}
},
}
</script>
<style lang="scss">
.valid-wrap {
height: 100vh;
width: 100vw;
display: flex;
align-items: center;
justify-content: center;
}
.valid-box {
width: 500px;
background-color: #fff;
padding: 5px 15px 20px 15px;
border-radius: 10px;
.valid-title {
border-bottom: 1px solid #e8eaec;
padding: 14px 16px;
line-height: 1;
}
.validation-text{
padding: 10px;
color: #333;
font-size: 14px;
}
}
</style>

View File

@ -100,4 +100,10 @@ export default [
path: '*',
component: () => import('./pages/404.vue')
},
{
name: 'validEmail',
path: '/valid/email',
meta: {title: '验证绑定邮箱'},
component: () => import('./pages/email/validEmail.vue')
},
]

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
</head>
<body>
你好,你正在绑定 {{env('APP_NAME') }} 的邮箱请于24小时之内点击该链接完成验证 :
<div style="display: flex; justify-content: center;">
<a href="{{$url}}">{{$url}}</a>
</div>
</body>
<body>