Write the Code. Change the World.

1月 25

上边完成了基本布局,发现某些 js,css缺失。这个时候,需要补进来。然后,完成服务端的注册登录逻辑,以及 gee 验证。

修补

加入以下文件

static/style/css/nprogress.css
static/style/css/nprogress.min.css

static/style/js/fn.js
static/style/js/gt.js
static/style/js/nprogress.min.js

部分调整

  1. 将 nprogress.min.js 和 app.js 的加载顺序更换一下。因为 app.js 中会使用到 nprogress 的封装
git add .
git commit -m '修复引入js,css不存在的bug'

认证脚手架

php artisan ui:auth

# 因为之前已经有了 layouts 相关的文件,这个时候会提示是否要覆盖,输入 no 回车。这个时候,会补入和修改相关的文件

# 查看变更的文件
git status 

# 删掉其他新增文件,只保留 resources/views/auth/ 下的文件
# web.php  路由也处理一下。之前就已经好了,这次注入是重复的,可以删掉重复的部分。

做好上边这些,然后开始修改 user 表的迁移文件,以及将 User模型移动到 app\Models\ 下。

迁移文件

<?php

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

class CreateUsersTable extends Migration
{
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('wxweb_openid', 64)->unique()->nullable(); // 微信公众号openid
            $table->string('wxapp_openid', 64)->unique()->nullable(); // 微信小程序openid
            $table->string('wxapp_session_key', 64)->nullable();  // 微信小程序 session_key
            $table->string('wx_unionid', 64)->unique()->nullable(); // 微信开放平台唯一id
            $table->string('bd_openid', 64)->unique()->nullable(); // 百度小程序openid
            $table->string('bd_unionid', 64)->unique()->nullable(); // 百度开放平台唯一id
            $table->string('bd_session_key', 64)->nullable();  // 小程序 session_key
            $table->string('account', 16)->nullable()->unique();
            $table->string('password')->nullable();
            $table->unsignedInteger('viewid')->unique();
            $table->string('nickname', 64)->nullable();
            $table->string('username', 64)->nullable();
            $table->string('phone_prefix', 8)->default('86')->comment('手机号码前缀');
            $table->string('phone', 16)->nullable();
            $table->unique(['phone_prefix', 'phone']);
            $table->smallInteger('gender')->default(1)->comment('性别,0 未知,1男,2女');
            // 普通索引 (index)
            $table->string('email', 32)->nullable()->index('email');
            $table->string('qq', 16)->nullable()->comment('qq');
            $table->string('wechat', 32)->nullable()->comment('微信');
            $table->date('birthday')->nullable()->comment('生日');
            $table->string('avatar', 200)->default('storage/upload/image/avatar/default.jpg')->comment('头像');
            $table->unsignedMediumInteger('province')->nullable();
            $table->unsignedMediumInteger('city')->nullable();
            $table->unsignedMediumInteger('area')->nullable();
            $table->string('address')->nullable();
            $table->point('position')->nullable();
            $table->string('signature', 255)->default('美的事物是永恒的喜悦!')->nullable()->comment('签名');
            $table->unsignedTinyInteger('referer')->default(1)->comment('注册来源 1 微信小程序 2 百度小程序 3 官方网站');
            $table->bigInteger('register_ip')->default(0)->comment('注册时ip');
            $table->unsignedInteger('online_days')->default(0);
            $table->string('verification_token')->nullable();
            $table->rememberToken();
            $table->dateTime('last_login_at')->nullable();
            $table->timestamp('email_verified_at')->nullable();
            $table->nullableTimestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('users');
    }
}

模型文件

  1. 新建文件夹 app/Models/
  2. 将 app 下的 User.php 移动到 app/Models 下。
  3. 修改命名空间以及白名单。如下。
<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use Notifiable;

    protected $fillable = [
        'wxweb_openid', 'wxapp_openid', 'wxapp_session_key', 'wx_unionid', 'bd_openid', 'bd_unionid', 'bd_session_key',
        'account', 'password', 'viewid', 'nickname', 'username', 'phone_prefix', 'phone', 'gender', 'email', 'qq', 'wechat',
        'birthday', 'avatar', 'province', 'city', 'area', 'address', 'position', 'signature', 'referer', 'register_ip',
        'online_days', 'last_login_at'];

    protected $hidden = [
        'password', 'remember_token',
    ];

    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

然后,先提交代码。

git add .
git commit -m '新增Models文件夹,用来安放模型,修改user迁移文件'

再替换 User 的命名空间,全局搜索 App\User ,换成 App\Models\User

修改注册登录等页面

# login.blade.php

@extends('layouts.app')

@section('title')
个人登录
@endsection

@section('style')
<style>

.login-page {
    display: flex;
    align-items: center;
    width: 100%;
    height: 100%;
    background: url({{asset('static/image/common/reg-login-bg.jpg')}});
    background-size: cover;
}

.login-page .container-fluid {
    display: flex;
    justify-content: center;
    padding-left: 0;
    padding-right: 0;
}

.login-page .card-panel {
    display: inline-block;
    width: 100%;
    max-width: 720px;
}

.login-page .card-header {
    text-align: center;
    font-size: 20px;
}

.login-page .col-form-label {
    font-weight: 600;
}

/* 小分辨率处理 */
@media (max-width: 720px)
{
    .login-page {
        background: #fff;
        align-items: flex-start;
    }

    .login-page .logo {
        align-self: center;
        width: 80px;
        height: 80px;
        background: url({{asset('static/image/common/logo.png')}});
        background-size: 80px 80px;
    }

    .card {
        border: none;
        border-radius: 0;
        margin-top: 60px;
    }

    .card-header {
        display: none;
    }
}
</style>
@endsection

@section('content')
<div class="container-fluid">
    <div class="card-panel">
        <div class="card">
            <div class="card-header">个人登录</div>
            <div class="logo"></div>
            <div class="card-body">
                <form method="POST" action="{{ route('login') }}" aria-label="个人登录">
                    @csrf

                    <div class="form-group row">
                        <label for="account" class="col-md-4 col-form-label text-md-right">账号</label>

                        <div class="col-md-5">
                            <input id="account" type="text" class="form-control{{ $errors->has('account') ? ' is-invalid' : '' }}" name="account" value="{{ old('account') }}" required autofocus>

                            @if ($errors->has('account'))
                                <span class="invalid-feedback" role="alert">
                                    <strong>{{ $errors->first('account') }}</strong>
                                </span>
                            @endif
                        </div>
                    </div>

                    <div class="form-group row">
                        <label for="password" class="col-md-4 col-form-label text-md-right">密码</label>

                        <div class="col-md-5">
                            <input id="password" type="password" class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" required>

                            @if ($errors->has('password'))
                                <span class="invalid-feedback" role="alert">
                                    <strong>{{ $errors->first('password') }}</strong>
                                </span>
                            @endif
                        </div>
                    </div>

                    <div class="form-group row">
                        <div class="col-md-6 offset-md-4">
                            <div class="checkbox">
                                <label>
                                    <input type="checkbox" name="remember" id="remember" checked style="margin-right: 6px;">记住密码
                                </label>
                            </div>
                        </div>
                    </div>

                    <div class="form-group row mb-0">
                        <div class="col-md-8 offset-md-4">
                            <button type="submit" class="btn btn-primary">登录</button>

                            <a class="btn btn-link" href="{{ route('password.request') }}">忘记密码</a>
                        </div>
                    </div>

                    <div class="form-group row" style="margin-top:40px;justify-content: center;font-size: 14px;color:#333;">
                        <span>如果没有账号,请<span><a style="margin:0 5px;" href="{{route('register')}}">注册</a>
                        <span style="font-size: 16px;">或第三方登录</span>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
@endsection

# register.blade.php

@extends('layouts.app')

@section('title')
个人登录
@endsection

@section('style')
<style>

.register-page {
    display: flex;
    align-items: center;
    width: 100%;
    height: 100%;
    background: url({{asset('static/image/common/reg-login-bg.jpg')}});
    background-size: cover;
}

.register-page .container-fluid {
    display: flex;
    justify-content: center;
    padding-left: 0;
    padding-right: 0;
}

.register-page .card-panel {
    display: inline-block;
    width: 100%;
    max-width: 720px;
}

.register-page .card-header {
    text-align: center;
    font-size: 20px;
}

.register-page .col-form-label {
    font-weight: 600;
}

/* 小分辨率处理 */
@media (max-width: 720px)
{
    .register-page {
        background: #fff;
        align-items: flex-start;
    }

    .register-page .logo {
        align-self: center;
        width: 80px;
        height: 80px;
        background: url({{asset('static/image/common/logo.png')}});
        background-size: 80px 80px;
    }

    .card {
        border: none;
        border-radius: 0;
        margin-top: 60px;
    }

    .card-header {
        display: none;
    }
}

/*验证码相关*/
/*去掉logo广告*/
#embed-captcha{
    width: 100%;
}

#embed-captcha .geetest_logo, .geetest_success_logo, .geetest_wait{
    display: none;
}

.hide{display:none;}

/*最小宽度*/
.geetest_holder.geetest_wind {
        min-width: 220px;
}

.geetest_copyright{
    display: none;
}

/*圆圈圈*/
#embed-captcha .geetest_radar{
    width: 24px;
    height: 24px;
    margin: 5px 6px;
}

#embed-captcha .geetest_btn, #embed-captcha .geetest_radar_tip, #embed-captcha .geetest_success_radar_tip, #embed-captcha span{
    height: 36px;
    line-height: 36px;
}

.geetest_holder.geetest_wind .geetest_radar_tip, .geetest_holder.geetest_wind .geetest_success_radar_tip
{
    height: 36px;
    line-height: 36px;
}

.geetest_popup_ghost{
    display: none;
}

/*成功图标*/
#embed-captcha .geetest_success_box
{
    top:6px;
}
</style>
@endsection

@section('content')
<div class="container-fluid">
    <div class="card-panel">
        <div class="card">
            <div class="card-header">个人注册</div>
            <div class="logo"></div>
            <div class="card-body">
                <form method="POST" action="{{ route('register') }}" aria-label="{{ __('Register') }}">
                    @csrf

                    <div class="form-group row">
                        <label for="account" class="col-md-4 col-form-label text-md-right">账号</label>

                        <div class="col-md-5">
                            <input placeholder="请输入手机号" id="account" type="text" class="form-control{{ $errors->has('account') ? ' is-invalid' : '' }}" name="account" value="{{ old('account') }}" required autofocus>

                            @if ($errors->has('account'))
                                <span class="invalid-feedback" role="alert">
                                    <strong>{{ $errors->first('account') }}</strong>
                                </span>
                            @endif
                        </div>
                    </div>

                    <div class="form-group row">
                        <label for="password" class="col-md-4 col-form-label text-md-right">密码</label>

                        <div class="col-md-5">
                            <input placeholder="6-16位密码,区分大小写" id="password" type="password" class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" required>

                            @if ($errors->has('password'))
                                <span class="invalid-feedback" role="alert">
                                    <strong>{{ $errors->first('password') }}</strong>
                                </span>
                            @endif
                        </div>
                    </div>

                    <div class="form-group row">
                        <label for="password_confirmation" class="col-md-4 col-form-label text-md-right">确认密码</label>

                        <div class="col-md-5">
                            <input placeholder="再次确认密码" id="password_confirmation" type="password" class="form-control" name="password_confirmation" required>

                            @if ($errors->has('password_confirmation'))
                            <span class="invalid-feedback" role="alert">
                                <strong>{{ $errors->first('password_confirmation') }}</strong>
                            </span>
                        @endif
                        </div>
                    </div>

                    <div class="form-group row">
                        <label for="email" class="col-md-4 col-form-label text-md-right">邮箱</label>

                        <div class="col-md-5">
                            <input placeholder="请输入邮箱" id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" required>

                            @if ($errors->has('email'))
                                <span class="invalid-feedback" role="alert">
                                    <strong>{{ $errors->first('email') }}</strong>
                                </span>
                            @endif
                        </div>
                    </div>

                    <div class="form-group row">
                        <div class= "col-md-5 offset-md-4">
                            <div id="embed-captcha"></div>
                            <p id="wait" class="show">正在加载验证码......</p>
                            <p id="notice" class="hide">请先完成验证</p>

                            @if ($errors->has('geetest_validate'))
                                <span class="help-block geetest-help" style="color:#a94442;">
                                    <strong>{{ $errors->first('geetest_validate') }}</strong>
                                </span>
                            @endif
                        </div>
                    </div>

                    <div class="form-group row">
                        <div class="col-md-5 offset-md-4">
                            同意并接受 <a href="javascript:void(0)">《服务条款》</a>
                        </div>
                    </div>

                    <div class="form-group row mb-0">
                        <div class="col-md-5 offset-md-4">
                            <button type="submit" class="btn btn-primary btn-block">注册</button>
                        </div>
                    </div>

                    <div class="form-group row" style="margin-top:40px;justify-content: center;font-size: 14px;color:#333;">
                        <span>如果拥有账号,请直接<span><a style="margin:0 5px;" href="{{route('login')}}">登录</a>
                        <span style="font-size: 16px;">或第三方登录</span>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
@endsection

@section('script')
<script src="{{ asset('static/style/js/gt.js') }}"></script>

<script>
// 验证码
var geeInit = function(){
    var captcha = $('#embed-captcha');

    if(!captcha)
            return;

    $.ajax({
            url: "{{route('start_gee')}}",
            type: "get",
            dataType: "json",
            success:function(data){
                if(data.code == 0)
                {
            setGee(data.data);
                }
            }
    });
}

var setGee = function(data){
        try{
          data = JSON.parse(data);

            initGeetest({
                width: "100%",
                gt: data.gt,
                protocol:"https://",
                challenge: data.challenge,
                new_captcha: data.new_captcha,
                product: "custom",
                area:"#embed-captcha",
                next_width:"70%",
                offline: !data.success
            }, handlerEmbed);
        }
        catch(e)
        {

        }
}

var handlerEmbed = function (captchaObj) {

    window.captchaObj = captchaObj;

    captchaObj.appendTo("#embed-captcha");

    captchaObj.onReady(function () {
        $("#wait")[0].className = "hide";

        setTimeout(function(){
            // $(".geetest_holder.geetest_wind").css("min-width", 220);
            $(".geetest_holder.geetest_wind").css("height", 36);
            // $(".geetest_holder.geetest_wind").css("width", 220);
        }, 1 * 100);

    });

    captchaObj.onSuccess(function(){
        var result = captchaObj.getValidate();
            if(result)
            {
                    $('.form-register').append('<input type="hidden" name="geetest_challenge" value="' + result.geetest_challenge + '" />');
                    $('.form-register').append('<input type="hidden" name="geetest_validate" value="' + result.geetest_validate + '" />');
                    $('.form-register').append('<input type="hidden" name="geetest_seccode" value="' + result.geetest_seccode + '" />');

                    if($('.geetest-help').length > 0)
                    {
                          $('.geetest-help').remove();
                    }
            }
    });
};

$(document).ready(function(){
        geeInit();
});
</script>
@endsection

# verify.blade.php
@extends('layouts.app')

@section('title')
邮箱验证
@endsection

@section('style')
<style>

.verification-notice-page {
    margin-top: 30px;
}
</style>
@endsection

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Verify Your Email Address') }}</div>

                <div class="card-body">
                    @if (session('resent'))
                        <div class="alert alert-success" role="alert">
                            {{ __('A fresh verification link has been sent to your email address.') }}
                        </div>
                    @endif

                    {{ __('Before proceeding, please check your email for a verification link.') }}
                    {{ __('If you did not receive the email') }}, <a href="{{ route('verification.resend') }}">{{ __('click here to request another') }}</a>.
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Reset Password') }}</div>

                <div class="card-body">
                    <form method="POST" action="{{ route('password.update') }}">
                        @csrf

                        <input type="hidden" name="token" value="{{ $token }}">

                        <div class="form-group row">
                            <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>

                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ $email ?? old('email') }}" required autocomplete="email" autofocus>

                                @error('email')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>

                            <div class="col-md-6">
                                <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">

                                @error('password')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>

                            <div class="col-md-6">
                                <input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
                            </div>
                        </div>

                        <div class="form-group row mb-0">
                            <div class="col-md-6 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Reset Password') }}
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Reset Password') }}</div>

                <div class="card-body">
                    <form method="POST" action="{{ route('password.update') }}">
                        @csrf

                        <input type="hidden" name="token" value="{{ $token }}">

                        <div class="form-group row">
                            <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>

                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ $email ?? old('email') }}" required autocomplete="email" autofocus>

                                @error('email')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>

                            <div class="col-md-6">
                                <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">

                                @error('password')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>

                            <div class="col-md-6">
                                <input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
                            </div>
                        </div>

                        <div class="form-group row mb-0">
                            <div class="col-md-6 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Reset Password') }}
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

RegisterController.php 中,我没要重写 register 方法,还有对 create 和 validator 方法进行修改,以满足自己的需要。顺便,用到了 gee 验证,在这里还有将其相关的逻辑 trait 进来。

**完整版的RegisterController **

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\Models\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;

use App\Traits\GeetestTraits;

class RegisterController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Register Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles the registration of new users as well as their
    | validation and creation. By default this controller uses a trait to
    | provide this functionality without requiring any additional code.
    |
    */

    use RegistersUsers;

    use GeetestTraits;
    /**
     * Where to redirect users after registration.
     *
     * @var string
     */
    protected $redirectTo = RouteServiceProvider::HOME;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest');
    }

    public function register(Request $request)
    {
        $this->geetest($request);

        $this->validator($request->all())->validate();

        $data = $request->all();
        $data['ip'] = ip2long($request->getClientIP());

        event(new Registered($user = $this->create($data)));

        $this->guard()->login($user);

        return $this->registered($request, $user)
                        ?: redirect($this->redirectPath());
    }

    /**
     * Get a validator for an incoming registration request.
     *
     * @param  array  $data
     * @return \Illuminate\Contracts\Validation\Validator
     */
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'account' => 'required|string|max:16|regex:/^(1[3-9])\\d{9}$/i|unique:users',
            'password' => 'required|confirmed|min:6',
            'password_confirmation' => 'required|same:password',
            'email' => 'required|email|max:255',
        ],[],[
            'account' => '账号',
            'password' => '密码',
            'password_confirmation' => '确认密码',
            'email' => '邮箱'
        ]);
    }

    /**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return \App\User
     */
    protected function create(array $data)
    {
        $user = User::where('phone', $data['account'])->first();

        if ($user) {
            return back()->with(['error' => '手机号码已注册,请使用微信小程序设置密码']);
        }

        $fill = [
            'account' => $data['account'],
            'password' => Hash::make($data['password']),
            'phone' => $data['account'],
            'email' => $data['email'],
            'register_ip' => $data['ip'],
            'signature' => $this->randomSignature()
        ];

        $user = User::create($fill);

        return User::where('id', $user->id)->first();
    }

    private function randomSignature()
    {
        $words = [
            '烟花飞腾的时候,火焰掉入海中。遗忘就和记得一样,是送给彼此最好的纪念。爱从来都不是归宿,也不是彼此最好的救赎。',
            '仿佛有痛楚。如果我晕眩。那是因为幻觉丰盛,能量薄弱。足以支持我对你的迷恋,不过支持我们的快乐。',
            '天没有边,没有界。心是花园也是荒野。光阴在花绽开中消亡,歌舞却永不停下。将一片云纱与你。敢不敢,愿不愿,一起飞越长空。',
            '一道电光劈开天幕。苍穹间有笔疾书,叫做战胜脆弱。',
            '虚幻之物对应着冥冥之路。',
            '美的事物是永恒的喜悦。',
            '舞低杨柳楼心月,歌尽桃花扇底风。从别后,忆相逢,几回魂梦与君同。',
            '车如流水马如龙,花月正春风。',
            '闲引鸳鸯香径里,手挼红杏蕊。',
            '夏雨雪,天地合,乃敢与君绝。',
            '一百年前,你不是你,我不是我。眼泪是真的,悲哀是假的。本来没因果。一百年后,没有你,也没有我。'
        ];

        $index = array_rand($words, 1);
        return $words[$index];
    }
}

完整版的GeetestTraits

<?php
namespace App\Traits;

use Illuminate\Support\Facades\Validator;
use Illuminate\Http\Request;
use App\Utils\Geetest;
use Session;

trait GeetestTraits{

      public function geetest(Request $request)
      {
          $this->__validGeetestData($request->all(), '请先完成验证!');
          $geetest_challenge = $request->input('geetest_challenge');
          $geetest_validate = $request->input('geetest_validate');
          $geetest_seccode = $request->input('geetest_seccode');

          $geetest =  app(Geetest::class);

          // 二次验证
          $gtserver = Session::get('gtserver');
          if($gtserver == 1)
          {
              $data = array(
                  "user_id" => 'yueqiubao',
                  "client_type" => "web",
                  "ip_address" => $request->getClientIp()
                );

              $result = $geetest->success_validate($geetest_challenge, $geetest_validate, $geetest_seccode, $data);
              if($result == FALSE)
                  $this->__validGeetestData(array(), '二次验证不通过!');
          }
          else
          {
              $result = $geetest->fail_validate($geetest_challenge, $geetest_validate, $geetest_seccode);
              if($result == FALSE)
                  $this->__validGeetestData(array(), '宕机,二次验证不通过!');
          }
      }

      /**
       * 验证geetest
       *
       * @param  array  $data
       * @return \Illuminate\Contracts\Validation\Validator
       */
      private function __validGeetestData($data, $message)
      {
          return Validator::make($data, [
              'geetest_validate' => 'required',
          ], [
              'geetest_validate.required' => $message,
          ])->validate();
      }

      // 获取geetst验证码信息。默认array其实会转成json输出。
      public function start_gee(Request $request, Geetest $geetest)
      {
          $data = array(
              "user_id" => 'yueqiubao',
              "client_type" => "web",
              "ip_address" => $request->getClientIp(),
            );

          $status = $geetest->pre_process($data, 1);
          $request->session()->put('gtserver', $status);
          $request->session()->put('user_id', $data['user_id']);
          return  response()->json(array('code' => 0, 'data' => $geetest->get_response_str()));
      }
}

完整版的 Geetest

<?php

namespace App\Utils;

define("CAPTCHA_ID", "xxxxxxxxx");
define("PRIVATE_KEY", "xxxxxxxx");

class Geetest
{
    const GT_SDK_VERSION = 'php_3.0.0';

    public static $connectTimeout = 1;
    public static $socketTimeout  = 1;

    private $response;

    public function __construct()
    {
        $this->captcha_id  = CAPTCHA_ID;
        $this->private_key = PRIVATE_KEY;
    }

    /**
     * 判断极验服务器是否down机
     *
     * @param array $data
     * @return int
     */
    public function pre_process($param, $new_captcha=1)
    {
        $data = array('gt'=>$this->captcha_id,
                     'new_captcha'=>$new_captcha
                );
        $data = array_merge($data,$param);
        $query = http_build_query($data);
        $url = "http://api.geetest.com/register.php?" . $query;
        $challenge = $this->send_request($url);
        if (strlen($challenge) != 32) {
            $this->failback_process();
            return 0;
        }
        $this->success_process($challenge);
        return 1;
    }

    /**
     * @param $challenge
     */
    private function success_process($challenge) {
        $challenge      = md5($challenge . $this->private_key);
        $result         = array(
            'success'   => 1,
            'gt'        => $this->captcha_id,
            'challenge' => $challenge,
            'new_captcha'=>1
        );
        $this->response = $result;
    }

    /**
     *
     */
    private function failback_process() {
        $rnd1           = md5(rand(0, 100));
        $rnd2           = md5(rand(0, 100));
        $challenge      = $rnd1 . substr($rnd2, 0, 2);
        $result         = array(
            'success'   => 0,
            'gt'        => $this->captcha_id,
            'challenge' => $challenge,
            'new_captcha'=>1
        );
        $this->response = $result;
    }

    /**
     * @return mixed
     */
    public function get_response_str() {
        return json_encode($this->response);
    }

    /**
     * 返回数组方便扩展
     *
     * @return mixed
     */
    public function get_response() {
        return $this->response;
    }

    /**
     * 正常模式获取验证结果
     *
     * @param string $challenge
     * @param string $validate
     * @param string $seccode
     * @param array $param
     * @return int
     */
    public function success_validate($challenge, $validate, $seccode,$param, $json_format=1) {
        if (!$this->check_validate($challenge, $validate)) {
            return 0;
        }
        $query = array(
            "seccode" => $seccode,
            "timestamp"=>time(),
            "challenge"=>$challenge,
            "captchaid"=>$this->captcha_id,
            "json_format"=>$json_format,
            "sdk"     => self::GT_SDK_VERSION
        );
        $query = array_merge($query,$param);
        $url          = "http://api.geetest.com/validate.php";
        $codevalidate = $this->post_request($url, $query);
        $obj = json_decode($codevalidate,true);
        if ($obj === false){
            return 0;
        }
        if ($obj['seccode'] == md5($seccode)) {
            return 1;
        } else {
            return 0;
        }
    }

    /**
     * 宕机模式获取验证结果
     *
     * @param $challenge
     * @param $validate
     * @param $seccode
     * @return int
     */
    public function fail_validate($challenge, $validate, $seccode) {
        if(md5($challenge) == $validate){
            return 1;
        }else{
            return 0;
        }
    }

    /**
     * @param $challenge
     * @param $validate
     * @return bool
     */
    private function check_validate($challenge, $validate) {
        if (strlen($validate) != 32) {
            return false;
        }
        if (md5($this->private_key . 'geetest' . $challenge) != $validate) {
            return false;
        }

        return true;
    }

    /**
     * GET 请求
     *
     * @param $url
     * @return mixed|string
     */
    private function send_request($url) {

        if (function_exists('curl_exec')) {
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, self::$connectTimeout);
            curl_setopt($ch, CURLOPT_TIMEOUT, self::$socketTimeout);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            $curl_errno = curl_errno($ch);
            $data = curl_exec($ch);
            curl_close($ch);
            if ($curl_errno >0) {
                return 0;
            }else{
                return $data;
            }
        } else {
            $opts    = array(
                'http' => array(
                    'method'  => "GET",
                    'timeout' => self::$connectTimeout + self::$socketTimeout,
                )
            );
            $context = stream_context_create($opts);
            $data    = @file_get_contents($url, false, $context);
            if($data){
                return $data;
            }else{
                return 0;
            }
        }
    }

    /**
     *
     * @param       $url
     * @param array $postdata
     * @return mixed|string
     */
    private function post_request($url, $postdata = '') {
        if (!$postdata) {
            return false;
        }

        $data = http_build_query($postdata);
        if (function_exists('curl_exec')) {
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, self::$connectTimeout);
            curl_setopt($ch, CURLOPT_TIMEOUT, self::$socketTimeout);

            //不可能执行到的代码
            if (!$postdata) {
                curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
            } else {
                curl_setopt($ch, CURLOPT_POST, 1);
                curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
            }
            $data = curl_exec($ch);

            if (curl_errno($ch)) {
                $err = sprintf("curl[%s] error[%s]", $url, curl_errno($ch) . ':' . curl_error($ch));
                $this->triggerError($err);
            }

            curl_close($ch);
        } else {
            if ($postdata) {
                $opts    = array(
                    'http' => array(
                        'method'  => 'POST',
                        'header'  => "Content-type: application/x-www-form-urlencoded\r\n" . "Content-Length: " . strlen($data) . "\r\n",
                        'content' => $data,
                        'timeout' => self::$connectTimeout + self::$socketTimeout
                    )
                );
                $context = stream_context_create($opts);
                $data    = file_get_contents($url, false, $context);
            }
        }

        return $data;
    }

    /**
     * @param $err
     */
    private function triggerError($err) {
        trigger_error($err);
    }
}

?>

注册好了,再来看登录。登录中需要重写 username 方法,默认是 email,这里换成你自己需要的。项目里是 account 字段,所以在 LoginController.php 中增加方法。

    /**
     * Get the login username to be used by the controller.
     * 登录对应的字段名
     *
     * @return string
     */
    public function username()
    {
        return 'account';
    }

上边这些都做好了,那么我们开始执行迁移文件。进行真正的注册登录。

先测试登录,输入账号密码,发现提示是英文的。这是语言包的问题。可以使用 https://github.com/overtrue/laravel-lang

composer require "overtrue/laravel-lang:~3.0"

# 将 config/app.php 中的 Illuminate\Translation\TranslationServiceProvider::class, 换成 Overtrue\LaravelLang\TranslationServiceProvider::class,

# 再执行下边的命令
php artisan lang:publish zh-CN

# 最后将 config/app.php 中的 locale 的值设置成 zh-CN,顺带将时区也一起改了。将 timezone 的值设置成 Asia/Shanghai 

再执行登录,提示用户名和密码错误,这样就对了。然后注册一个,退出再登录。资料都填好后,注册发现报错,说 viewid 不能为空。因为注册的时候,根本没给 viewid 对应的值。

在 User::create 创建的时候,给其填充 viewid 就可以了。我没修改 User 模型,加入下边的代码。

 protected static function boot()
    {
        parent::boot();
        // 监听模型创建事件,在写入数据库之前触发
        static::creating(function ($model) {
            // 如果模型的 viewid 字段为空
            if (!$model->viewid) {
                // 调用 build_viewid 来生成 view id
                $model->viewid = static::build_viewid();
            }

            if (!$model->nickname) {
                $model->nickname = '小伙伴-' . $model->viewid;
            }
        });
    }

    public static function build_viewid()
    {
        $prefix = rand(1, 9);
        $prefix *= 10000000;
        $difference = rand(1, 9999999);
        $viewid = $prefix + $difference;

        if ($viewid > 99999999)
        {
            static::build_viewid();
        }

        if (!static::query()->where('viewid', $viewid)->exists()) {
            return $viewid;
        }
        static::build_viewid();
    }

再注册,发现已经成功了。但是出现了 404 页面,因为不存在 /home 这个路由的呀。那是因为注册成功后跳转到这个路由了,而这个路由的定义在 App\Providers\RouteServiceProvider 中,只需要全局将 /home 换成 / 就可以了。

到了最后,还是有问题。发现 user.show user.edit 路由不存在。因此,要加入这些路由,要创建控制器,并加入方法。

php artisan make:controller Web/UserController

# 在 UserController 中加入方法。先占着坑。

    public function index($id)
    {
        return $id;
    }

    public function edit($id)
    {
        return $id;
    }

#  在路由中加入
    Route::get('/users/{user}', 'UserController@index')->name('users.show');

    Route::group(['middleware' => 'auth'], function() {
        Route::get('/users/{user}/edit', 'UserController@edit')->name('users.edit');
    });

路由弄好了,还是报错。说 getAllPermissions 方法不存在。这个的确不存在。这个后边会处理,先将 _header.blade.php 中这个注释掉。然后,刷新就好了。再退出,试试登录,发现登录也没问题了。

最后提交 git

git add .
git commit -m '实现登录注册相关逻辑以及多国语音控制'

下一节,来处理邮件验证。

发表评论

电子邮件地址不会被公开。 必填项已用*标注