Commit d4077bde authored by UtopiaXC's avatar UtopiaXC

📨 完成邮件发送,修改Redis工具,更改验证码保存方式

parent 7db4a661
<?php <?php
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Http\Utils\CustomCaptcha; use App\Http\Utils\CustomCaptcha;
use App\Http\Utils\R; use App\Http\Utils\R;
use App\Http\Utils\RedisAndCache;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Mews\Captcha\Captcha; use Mews\Captcha\Captcha;
class CaptchaController extends Controller{ class CaptchaController extends Controller {
function getCaptcha(Request $request, Captcha $captchaBuilder) function getCaptcha(Request $request, Captcha $captchaBuilder) {
{
//获取laravel的session token,这里的思想是通过缓存token与验证码值来验证以避免重复提交同一hash问题 //获取laravel的session token,这里的思想是通过缓存token与验证码值来验证以避免重复提交同一hash问题
$key = $request->cookie(app()->getNamespace() . "session"); $key = $request->cookie(env("APP_NAME", "utopia_open_platform") . "_session");
//创建自定义验证码对象,需要将构建器传入 //创建自定义验证码对象,需要将构建器传入
$captcha = new CustomCaptcha($captchaBuilder); $captcha = new CustomCaptcha($captchaBuilder);
//设置过期时间。我设置了两分钟 //设置过期时间。我设置了两分钟
$expiredAt = now()->addMinute(2); $expiredAt = now()->addMinutes(2);
//将验证码值,session token放入缓存并设置过期时间 //将验证码值,session token放入缓存并设置过期时间
Cache::put($key, ['captcha' => $captcha->getCode()], $expiredAt); RedisAndCache::setWithExpire(\RedisCacheKey::CAPTCHA . $key, $captcha->getCode(), 2);
//构建返回数组,包括有效期截止时间和BASE64格式图片 //构建返回数组,包括有效期截止时间和BASE64格式图片
$result = [ $result = [
'expired_at' => $expiredAt->toDateTimeString(), 'expired_at' => $expiredAt->toDateTimeString(),
...@@ -28,18 +27,20 @@ class CaptchaController extends Controller{ ...@@ -28,18 +27,20 @@ class CaptchaController extends Controller{
return R::ok($result); return R::ok($result);
} }
//验证用户提交的验证码,返回值bool(私有方法) //验证用户提交的验证码,返回值bool
static function check_captcha($captcha,$session):bool{ static function check_captcha($request) {
$captcha = $request->get(\FormKey::CAPTCHA);
$session = \RedisCacheKey::CAPTCHA . $request->cookie(env("APP_NAME", "utopia_open_platform") . "_session");
//通过传入的session获取缓存中的验证码对象,不存在则返回验证失败 //通过传入的session获取缓存中的验证码对象,不存在则返回验证失败
$captchaData = Cache::get($session); $captchaData = RedisAndCache::get($session);
Cache::forget($session); RedisAndCache::forget($session);
if ($captchaData == null) { if ($captchaData == null) {
return false; return false;
} }
//判断传入的验证码与缓存是否相等 //判断传入的验证码与缓存是否相等
if ($captcha == $captchaData['captcha']){ if ($captcha == $captchaData) {
return true; return true;
}else{ } else {
return false; return false;
} }
} }
......
...@@ -4,6 +4,7 @@ namespace App\Http\Controllers; ...@@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use App\Http\Utils\R; use App\Http\Utils\R;
use App\Http\Utils\RedisAndCache; use App\Http\Utils\RedisAndCache;
use App\Mail\RegisterVerifyLinkMail;
use App\Models\Users\User; use App\Models\Users\User;
use App\Models\Users\UserProfile; use App\Models\Users\UserProfile;
use Exception; use Exception;
...@@ -11,21 +12,25 @@ use HTTP_CODE; ...@@ -11,21 +12,25 @@ use HTTP_CODE;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cookie; use Illuminate\Support\Facades\Cookie;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Mail;
use RedisCacheKey; use RedisCacheKey;
use Webpatser\Uuid\Uuid; use Webpatser\Uuid\Uuid;
class UserController extends Controller { class UserController extends Controller {
/**
* @throws \Throwable
*/
function register(Request $request) { function register(Request $request) {
if (!CaptchaController::check_captcha($request->get("captcha"), $request->cookie(app()->getNamespace() . "session"))) { if (!CaptchaController::check_captcha($request)) {
return R::error(HTTP_CODE::UNAUTHORIZED_CAPTCHA); return R::error(HTTP_CODE::UNAUTHORIZED_CAPTCHA);
} }
try { try {
if (!$request->get("email") || !$request->get("user_name") || !$request->get("password")) { if (!$request->get(\FormKey::EMAIL) || !$request->get(\FormKey::EMAIL) || !$request->get(\FormKey::PASSWORD)) {
return R::error(HTTP_CODE::NOT_ACCEPT_PARAMS_CONTENT_WRONG); return R::error(HTTP_CODE::NOT_ACCEPT_PARAMS_CONTENT_WRONG);
} }
$email = $request->get("email"); $email = $request->get(\FormKey::EMAIL);
$user_name = $request->get("user_name"); $user_name = $request->get(\FormKey::USER_NAME);
$password = password_hash($request->get("password"), PASSWORD_DEFAULT); $password = password_hash($request->get(\FormKey::PASSWORD), PASSWORD_DEFAULT);
$user = User::query() $user = User::query()
->where("user_name", $user_name) ->where("user_name", $user_name)
->orWhere("user_name", $email) ->orWhere("user_name", $email)
...@@ -47,6 +52,10 @@ class UserController extends Controller { ...@@ -47,6 +52,10 @@ class UserController extends Controller {
$user->save(); $user->save();
$user_profile->save(); $user_profile->save();
DB::commit(); DB::commit();
$code = md5(Uuid::generate());
$link = env("APP_URL") . \WebUrl::REGISTER_VERIFY . "/" . $code;
R::ok(RedisAndCache::setWithExpire(RedisCacheKey::REGISTER_VERIFY . $code, $user->id,15));
Mail::to($email)->send(new RegisterVerifyLinkMail($link, $user_name));
} catch (Exception $e) { } catch (Exception $e) {
DB::rollBack(); DB::rollBack();
return R::error(HTTP_CODE::INTERNAL_SERVER_ERROR); return R::error(HTTP_CODE::INTERNAL_SERVER_ERROR);
...@@ -55,11 +64,11 @@ class UserController extends Controller { ...@@ -55,11 +64,11 @@ class UserController extends Controller {
} }
function login(Request $request) { function login(Request $request) {
if (!CaptchaController::check_captcha($request->get("captcha"), $request->cookie(app()->getNamespace() . "session"))) { if (!CaptchaController::check_captcha($request)) {
return R::error(HTTP_CODE::UNAUTHORIZED_CAPTCHA); return R::error(HTTP_CODE::UNAUTHORIZED_CAPTCHA);
} }
$username = $request->get("user"); $username = $request->get(\FormKey::USER);
$password = $request->get("password"); $password = $request->get(\FormKey::PASSWORD);
if (!$username || !$password) { if (!$username || !$password) {
return R::error(HTTP_CODE::NOT_ACCEPT_PARAMS_CONTENT_WRONG); return R::error(HTTP_CODE::NOT_ACCEPT_PARAMS_CONTENT_WRONG);
} }
......
...@@ -61,7 +61,6 @@ class RedisAndCache { ...@@ -61,7 +61,6 @@ class RedisAndCache {
return $value; return $value;
} }
public static function getWithJson($key) { public static function getWithJson($key) {
try { try {
if (env(EnvKey::REDIS_USE, false) == true) { if (env(EnvKey::REDIS_USE, false) == true) {
...@@ -78,4 +77,21 @@ class RedisAndCache { ...@@ -78,4 +77,21 @@ class RedisAndCache {
} }
return $value; return $value;
} }
public static function forget($key) {
try {
if (env(EnvKey::REDIS_USE, false) == true) {
try {
Redis::del($key);
} catch (ConnectionException $e) {
Cache::forget($key);
}
} else {
Cache::forget($key);
}
} catch (Exception $e) {
return false;
}
return true;
}
} }
...@@ -7,7 +7,7 @@ use Illuminate\Contracts\Queue\ShouldQueue; ...@@ -7,7 +7,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
class VerityLinkMail extends Mailable { class RegisterVerifyLinkMail extends Mailable {
use Queueable, SerializesModels; use Queueable, SerializesModels;
protected $link; protected $link;
...@@ -20,8 +20,8 @@ class VerityLinkMail extends Mailable { ...@@ -20,8 +20,8 @@ class VerityLinkMail extends Mailable {
*/ */
public function __construct($link, $user) { public function __construct($link, $user) {
// //
$this->$user = $user; $this->user = $user;
$this->$link = $link; $this->link = $link;
} }
/** /**
...@@ -33,6 +33,6 @@ class VerityLinkMail extends Mailable { ...@@ -33,6 +33,6 @@ class VerityLinkMail extends Mailable {
return $this->view('email.register_verify_link')->with([ return $this->view('email.register_verify_link')->with([
"link" => $this->link, "link" => $this->link,
"user" => $this->user, "user" => $this->user,
]); ])->from(['address' => env("MAIL_FROM_ADDRESS"), 'name' => env("APP_NAME")]);
} }
} }
...@@ -8,6 +8,7 @@ class WebUrl ...@@ -8,6 +8,7 @@ class WebUrl
const FIND_PASSWORD = '/find_password'; const FIND_PASSWORD = '/find_password';
const PRIVACY_POLICY = "/privacy_policy"; const PRIVACY_POLICY = "/privacy_policy";
const REGISTER_VERIFY="/register_verify";
} }
class ApiUrl class ApiUrl
...@@ -44,6 +45,8 @@ class RedisCacheKey ...@@ -44,6 +45,8 @@ class RedisCacheKey
{ {
const SITE_PROFILE = "site_profile"; const SITE_PROFILE = "site_profile";
const USER_TOKEN = "user_token:"; const USER_TOKEN = "user_token:";
const REGISTER_VERIFY="register_verify:";
const CAPTCHA="captcha:";
} }
class CookieKey class CookieKey
...@@ -58,6 +61,14 @@ class HeaderKey ...@@ -58,6 +61,14 @@ class HeaderKey
const SITE_PROFILE="site_profile"; const SITE_PROFILE="site_profile";
} }
class FormKey{
const CAPTCHA="captcha";
const USER_NAME="user_name";
const EMAIL="email";
const PASSWORD="password";
const USER="user";
}
class SiteProfileTypeEnum class SiteProfileTypeEnum
{ {
const WEB_TITLE = "01"; const WEB_TITLE = "01";
...@@ -75,3 +86,4 @@ class DefaultSiteProfile ...@@ -75,3 +86,4 @@ class DefaultSiteProfile
class EnvKey{ class EnvKey{
const REDIS_USE="REDIS_USE"; const REDIS_USE="REDIS_USE";
} }
...@@ -3,5 +3,5 @@ ...@@ -3,5 +3,5 @@
@section('title') - 注册验证 @endsection @section('title') - 注册验证 @endsection
@section('body') @section('body')
注册验证页 {{$code}}
@endsection @endsection
<!DOCTYPE html> <body style="
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" >
<head>
<meta charset="UTF-8">
<title>CodePen - Material VCard</title>
<style>
@import url(https://fonts.googleapis.cn/css?family=Open+Sans:400,300,700);
* {
box-sizing: border-box; box-sizing: border-box;
}
body {
min-height: 100vh; min-height: 100vh;
display: flex; display: flex;
background-color: #eaeaea; background-color: #eaeaea;
font-family: Open Sans; font-family: Open Sans, sans-serif;
font-weight: 300; font-weight: 300;
line-height: 1.8; line-height: 1.8;
background-size: cover; background-size: cover;
background-repeat: no-repeat; background-repeat: no-repeat;">
} <div style="
box-sizing: border-box;
.contact {
position: absolute;
top: 30px;
left: 50px;
z-index: 6;
color: rgba(0, 0, 0, 0.5);
text-transform: uppercase;
letter-spacing: 3px;
font-size: 12px;
font-weight: 700;
padding: 5px 15px;
border-radius: 20px;
background: rgba(0, 0, 0, 0.1);
line-height: 1;
cursor: pointer;
text-shadow: 0 1px 0px rgba(255, 255, 255, 0.1);
}
.contact-form {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
background: white;
z-index: 5;
padding: 80px 50px;
transform: translate3d(-100%, 0, 0);
transition: 0.3s ease;
border-radius: 5px;
}
.contact-form.active {
transform: translate3d(0, 0, 0);
}
.contact-form .close {
color: rgba(0, 0, 0, 0.7);
position: absolute;
right: 30px;
top: 30px;
}
.cards {
margin: auto; margin: auto;
background: #fefefe; background: #fefefe;
border-radius: 5px; border-radius: 5px;
...@@ -71,31 +20,13 @@ ...@@ -71,31 +20,13 @@
position: relative; position: relative;
display: flex; display: flex;
align-items: flex-end; align-items: flex-end;
padding: 30px; padding: 30px;" class="cards">
} <div style="
.cards .card { box-sizing: border-box;
display: inline-block; display: inline-block;
margin-right: 20px; margin-right: 20px;">
} <div style="
.cards .card-toggle { box-sizing: border-box;
z-index: 4;
position: relative;
width: 48px;
height: 48px;
border-radius: 50%;
display: block;
text-align: center;
line-height: 1.8;
font-size: 24px;
cursor: pointer;
border: 2px solid transparent;
transition: 0.3s ease;
}
.cards .card-toggle.active {
color: white;
border-color: white;
}
.cards .card-content {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
...@@ -104,174 +35,37 @@ ...@@ -104,174 +35,37 @@
transition: -webkit-clip-path 1s ease; transition: -webkit-clip-path 1s ease;
padding: 100px 0 0; padding: 100px 0 0;
overflow: hidden; overflow: hidden;
border-radius: 5px; border-radius: 5px;">
} <div style="
.cards .card-content .row { box-sizing: border-box;
display: table; display: table;
width: 100%; width: 100%;
height: 100%; height: 100%;">
} <div style="
.cards .card-content .col { box-sizing: border-box;
width: 70%;
height: 100%;
display: table-cell;
transition: 0.3s ease 0.3s;
transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0);
vertical-align: top; padding-left: 50px;
} opacity: 1;">
.cards .card-content .col h2 { <h2 style="
box-sizing: border-box;
font-weight: 300; font-weight: 300;
font-size: 3em; font-size: 3em;
line-height: 1; line-height: 1;
margin: 0 0 30px; margin: 0 0 30px;">欢迎注册<br>{{env('APP_SHOW_NAME','Utopia Open Platform')}}</h2>
} <p style="width: 70%">亲爱的 {{$user}}<br>您已成功注册{{env('APP_SHOW_NAME','Utopia Open Platform')}}
.cards .card-content .col h2 strong { 的账户,请点击<a target="_blank" href="{{$link}}">验证链接</a>来激活您的账户功能。验证有效期十五分钟。 <br/><em>如果您点击链接后未跳转,请将以下链接复制到浏览器访问:<br>{{$link}}
font-weight: 700; </em></p>
display: block;
}
.cards .card-content .col img {
max-width: 90%;
width: 100%;
}
.cards .card-content .col.left {
transform: translate3d(0, 0, 0);
opacity: 0;
padding-left: 50px;
}
.cards .card-content .col.right {
transform: translate3d(100px, 0, 0);
opacity: 0;
padding-left: 30px;
}
.cards .card.active .col {
transform: translate3d(0, 0, 0);
opacity: 1;
}
.cards #overview .card-toggle {
position: absolute;
top: 30px;
right: 30px;
opacity: 1;
color: white;
}
.cards #overview .card-content {
background-color: #efefef;
-webkit-clip-path: circle(0% at 91.5% 75px);
}
.cards #overview.active .card-toggle {
opacity: 0;
}
.cards #overview.active .card-content {
-webkit-clip-path: circle(270% at 91.5% 75px);
}
.cards #overview .right {
background-size: contain;
}
.cards #dribbble .card-content, .cards #behance .card-content, .cards #linkedin .card-content, .cards #twitter .card-content {
color: white;
}
.cards #dribbble .card-content p, .cards #behance .card-content p, .cards #linkedin .card-content p, .cards #twitter .card-content p {
color: rgba(255, 255, 255, 0.8);
}
.cards #dribbble .card-content {
background-color: #F46899;
-webkit-clip-path: circle(0% at 76px 88%);
clip-path: circle(0% at 50px 88%);
}
.cards #dribbble.active .card-content {
-webkit-clip-path: circle(270% at 76px 88%);
clip-path: circle(270% at 50px 88%);
}
.cards #behance .card-content {
background-color: #2F98D1;
-webkit-clip-path: circle(0% at 150px 88%);
clip-path: circle(0% at 150px 88%);
}
.cards #behance.active .card-content {
-webkit-clip-path: circle(270% at 150px 88%);
clip-path: circle(270% at 150px 88%);
}
.cards #linkedin .card-content {
background-color: #03679B;
-webkit-clip-path: circle(0% at 220px 88%);
clip-path: circle(0% at 220px 88%);
}
.cards #linkedin.active .card-content {
-webkit-clip-path: circle(270% at 220px 88%);
clip-path: circle(270% at 220px 88%);
}
.cards #twitter .card-content {
background-color: #7FD0ED;
-webkit-clip-path: circle(0% at 292px 88%);
clip-path: circle(0% at 292px 88%);
}
.cards #twitter.active .card-content {
-webkit-clip-path: circle(270% at 292px 88%);
clip-path: circle(270% at 292px 88%);
}
form .control {
position: relative;
margin-bottom: 10px;
padding-top: 20px;
}
form .control label {
position: absolute;
top: 30px;
left: 0;
transition: 0.3s ease;
text-transform: uppercase;
font-weight: 600;
letter-spacing: 2px;
font-size: 14px;
}
form .control.submit {
text-align: right;
}
form input, form textarea {
width: 100%;
border: none;
border-bottom: 1px solid #e3e3e3;
outline: none;
padding: 10px 0;
}
form .filled label, form input:focus + label, form textarea:focus + label {
top: 0;
font-size: 12px;
}
form textarea {
height: 100px;
}
form input[type=submit] {
width: auto;
background-color: #F06292;
padding: 10px 40px;
color: white;
border-radius: 40px;
}
</style>
</head>
<body>
<div class="cards">
<div class="contact" onclick="window.open('{{env('APP_URL','https://open.utopiaxc.cn/')}}')">Utopia Open Platform</div>
<div class="card active">
<div class="card-content">
<div class="row">
<div class="left col">
<h2>欢迎注册<br>{{env('APP_SHOW_NAME','Utopia Open Platform')}}</h2>
<p>亲爱的 {{$user}}<br>您已成功注册{{env('APP_SHOW_NAME','Utopia Open Platform')}}的账户,请点击<a target="_blank" href="{{$link}}">验证链接</a>来激活您的账户功能。验证有效期十五分钟。 <br/><em>如果您点击链接后未跳转,请将以下链接复制到浏览器访问:<br>{{$link}}</em></p>
</div> </div>
<div class="right col"> <div style="
text-align: right;
padding-right: 100px">
<p>
<a target="_blank"
href="{{env('APP_URL',"")}}">{{env('APP_SHOW_NAME','Utopia Open Platform')}}</a>
</p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</body> </body>
</html>
...@@ -23,7 +23,7 @@ Route::group(['prefix' => '/', 'middleware' => [Middleware::SITE_PROFILE_MIDDLEW ...@@ -23,7 +23,7 @@ Route::group(['prefix' => '/', 'middleware' => [Middleware::SITE_PROFILE_MIDDLEW
Route::get("/register", function () { Route::get("/register", function () {
return view('register'); return view('register');
}); });
Route::get("/register_verify", function () { Route::get("/register_verify/{code}", function ($code) {
return view('email.register_verify'); return view('email.register_verify')->with("code",$code);
}); });
}); });
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment