一个菜鸟驿站!

laravel-admin整合极验证

PHP 2018-10-10 浏览(2910) 评论(2)
- N +

文章目录 [+]

前言

在使用laravel-admin的途中,尽管不错,但是登录时没有验证码这一直是我心里不爽的一点,出于安全的考虑,于是打算整合一个极验证到我的项目当中去。

Geetest

这是我所用的极验证,目测还凑合吧,暂时先用着。

geetest.png

laravel-admin整合极验证

安装

composer安装geetest的php-sdk。

composer require gee-team/gt-php-sdk

重构

当然我们要想使用geetest这个极验证的话,我们只能重构登录系统,那么跟着我下边的思路走,新建路由->新建AuthController->新建login.blade.php

新建路由

Route::group ( [
    'prefix' => config ( 'admin.route.prefix' ) ,
    'namespace' => config ( 'admin.route.namespace' ) ,
],function (Router $router){
    $router->get('auth/start_captcha','AuthController@startCaptcha');//初始化极验证,这个必须不通过登录中间件验证,否则无法获取
});

Route::group ( [
    'prefix' => config ( 'admin.route.prefix' ) ,
    'namespace' => config ( 'admin.route.namespace' ) ,
    'middleware' => config ( 'admin.route.middleware' ) ,
],function (Router $router){
    $router->get('auth/login', 'AuthController@getLogin');//登录页面
    $router->post('auth/login', 'AuthController@postLogin');//登录请求
});

新建AuthController控制器

/vendor/encore/laravel-admin/src/Controllers/AuthController复制到/app/Admin/Controllers中,修改命名空间,以及编辑以下几个方法:getLogin(修改)、postLogin(修改)、startCaptcha(添加)、validateCaptcha(添加),如果懒得写,可以直接复制下边的代码:

<?php

namespace App\Admin\Controllers;

use Encore\Admin\Auth\Database\Administrator;
use Encore\Admin\Facades\Admin;
use Encore\Admin\Form;
use Encore\Admin\Layout\Content;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Validator;
use GeetestLib;

class AuthController extends Controller {
    public function __construct () {
        //打开session,如果打开了,可以删除
        if(!isset($_SESSION)){
            session_start ();
        }
    }

    /**
     * Show the login page.
     *
     * @return \Illuminate\Contracts\View\Factory|Redirect|\Illuminate\View\View
     */
    public function getLogin () {
        if ( ! Auth::guard ( 'admin' )->guest () ) {
            return redirect ( config ( 'admin.route.prefix' ) );
        }

        return view ( 'admin.login' );
    }

    /**
     * Handle a login request.
     *
     * @param Request $request
     *
     * @return mixed
     */
    public function postLogin ( Request $request ) {
        $credentials = $request->only ( [ 'username' , 'password' ] );

        $validator = Validator::make ( $credentials , [
            'username' => 'required' ,
            'password' => 'required' ,
        ] , [
            'username.required' => '用户名不能为空' ,
            'password.required' => '密码不能为空'
        ] );

        if ( $validator->fails () ) {
            return Redirect::back ()->withInput ()->withErrors ( $validator );
        }


        //验证极验证码
        if ( ! $this->validateCaptcha () ) {
            return Redirect::back ()->withInput ()->withErrors ( [ 'captcha' => '验证失败:拖动滑块将悬浮图像正确拼合' ] );
        }
        unset( $credentials[ 'captcha' ] );

        if ( Auth::guard ( 'admin' )->attempt ( $credentials ) ) {
            admin_toastr ( trans ( 'admin.login_successful' ) );

            return redirect ()->intended ( config ( 'admin.route.prefix' ) );
        }

        return Redirect::back ()->withInput ()->withErrors ( [ 'username' => $this->getFailedLoginMessage () ] );
    }


    /**
     * 初始化极验证
     */
    public function startCaptcha () {
        $GtSdk = new GeetestLib();
        $return = $GtSdk->register ();
        if ( $return ) {
            $_SESSION['gtserver'] = 1;
            $result = array (
                'success' => 1 ,
                'gt' => CAPTCHA_ID ,
                'challenge' => $GtSdk->challenge
            );
            echo json_encode ( $result );
        } else {
            $_SESSION['gtserver'] = 0;
            $rnd1 = md5 ( rand ( 0 , 100 ) );
            $rnd2 = md5 ( rand ( 0 , 100 ) );
            $challenge = $rnd1 . substr ( $rnd2 , 0 , 2 );
            $result = array (
                'success' => 0 ,
                'gt' => CAPTCHA_ID ,
                'challenge' => $challenge
            );
            $_SESSION[ 'challenge' ] = $result[ 'challenge' ];
            echo json_encode ( $result );
        }
        die;
    }

    /**
     * 这里二次验证极验证是否正确
     */
    public function validateCaptcha () {
//        GeetestLib
        $GtSdk = new GeetestLib();
        if ( $_SESSION[ 'gtserver' ] == 1 ) {
            $result = $GtSdk->validate ( $_POST[ 'geetest_challenge' ] , $_POST[ 'geetest_validate' ] , $_POST[ 'geetest_seccode' ] );
            if ( $result == TRUE ) {
                return true;
            } else if ( $result == FALSE ) {
                return false;
            } else {
                return false;
            }
        } else {
            if ( $GtSdk->get_answer ( $_POST[ 'geetest_validate' ] ) ) {
                return true;
            } else {
                return false;
            }
        }

    }

    /**
     * User logout.
     *
     * @return Redirect
     */
    public function getLogout ( Request $request ) {
        $this->guard ()->logout ();

        $request->session ()->invalidate ();

        return redirect ( config ( 'admin.route.prefix' ) );
    }

    /**
     * User setting page.
     *
     * @return mixed
     */
    public function getSetting () {
        return Admin::content ( function ( Content $content ) {
            $content->header ( trans ( 'admin.user_setting' ) );
            $form = $this->settingForm ();
            $form->tools (
                function ( Form\Tools $tools ) {
                    $tools->disableBackButton ();
                    $tools->disableListButton ();
                }
            );
            $content->body ( $form->edit ( Admin::user ()->id ) );
        } );
    }

    /**
     * Update user setting.
     *
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function putSetting () {
        return $this->settingForm ()->update ( Admin::user ()->id );
    }

    /**
     * Model-form for user setting.
     *
     * @return Form
     */
    protected function settingForm () {
        return Administrator::form ( function ( Form $form ) {
            $form->display ( 'username' , trans ( 'admin.username' ) );
            $form->text ( 'name' , trans ( 'admin.name' ) )->rules ( 'required' );
            $form->image ( 'avatar' , trans ( 'admin.avatar' ) );
            $form->password ( 'password' , trans ( 'admin.password' ) )->rules ( 'confirmed|required' );
            $form->password ( 'password_confirmation' , trans ( 'admin.password_confirmation' ) )->rules ( 'required' )
                ->default ( function ( $form ) {
                    return $form->model ()->password;
                } );

            $form->setAction ( admin_base_path ( 'auth/setting' ) );

            $form->ignore ( [ 'password_confirmation' ] );

            $form->saving ( function ( Form $form ) {
                if ( $form->password && $form->model ()->password != $form->password ) {
                    $form->password = bcrypt ( $form->password );
                }
            } );

            $form->saved ( function () {
                admin_toastr ( trans ( 'admin.update_succeeded' ) );

                return redirect ( admin_base_path ( 'auth/setting' ) );
            } );
        } );
    }

    /**
     * @return string|\Symfony\Component\Translation\TranslatorInterface
     */
    protected function getFailedLoginMessage () {
        return Lang::has ( 'auth.failed' )
            ? trans ( 'auth.failed' )
            : '用户名或者密码不正确';
    }

    /**
     * Get the post login redirect path.
     *
     * @return string
     */
    protected function redirectPath () {
        if ( method_exists ( $this , 'redirectTo' ) ) {
            return $this->redirectTo ();
        }

        return property_exists ( $this , 'redirectTo' ) ? $this->redirectTo : config ( 'admin.route.prefix' );
    }

    /**
     * Send the response after the user was authenticated.
     *
     * @param \Illuminate\Http\Request $request
     *
     * @return \Illuminate\Http\Response
     */
    protected function sendLoginResponse ( Request $request ) {
        admin_toastr ( trans ( 'admin.login_successful' ) );

        $request->session ()->regenerate ();

        return redirect ()->intended ( $this->redirectPath () );
    }

    /**
     * Get the login username to be used by the controller.
     *
     * @return string
     */
    protected function username () {
        return 'username';
    }

    /**
     * Get the guard to be used during authentication.
     *
     * @return \Illuminate\Contracts\Auth\StatefulGuard
     */
    protected function guard () {
        return Auth::guard ( 'admin' );
    }
}

login.blade.php视图文件

/vendor/encore/laravel-admin/resources/view/login.blade.php复制到/resource/view中,下边是完整的html代码:

<!DOCTYPE html>
<html>
    
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>
            {{config('admin.title')}} | {{ trans('admin.login') }}
        </title>
        <!-- Tell the browser to be responsive to screen width -->
        <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
        name="viewport">
        <!-- Bootstrap 3.3.5 -->
        <link rel="stylesheet" href="{{ admin_asset(" /vendor/laravel-admin/AdminLTE/bootstrap/css/bootstrap.min.css
        ") }}">
        <!-- Font Awesome -->
        <link rel="stylesheet" href="{{ admin_asset(" /vendor/laravel-admin/font-awesome/css/font-awesome.min.css
        ") }}">
        <!-- Theme style -->
        <link rel="stylesheet" href="{{ admin_asset(" /vendor/laravel-admin/AdminLTE/dist/css/AdminLTE.min.css
        ") }}">
        <!-- iCheck -->
        <link rel="stylesheet" href="{{ admin_asset(" /vendor/laravel-admin/AdminLTE/plugins/iCheck/square/blue.css
        ") }}">
        <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media
        queries -->
        <!-- WARNING: Respond.js doesn't work if you view the page via file://
        -->
        <!--[if lt IE 9]>
            <script src="//oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js">
            </script>
            <script src="//oss.maxcdn.com/respond/1.4.2/respond.min.js">
            </script>
        <![endif]-->
    </head>
    
    <body class="hold-transition login-page" @if(config(
    'admin.login_background_image'))style="background: url({{config('admin.login_background_image')}}) no-repeat;background-size: cover;"
    @endif>
        <div class="login-box">
            <div class="login-logo">
                <a href="{{ admin_base_path('/') }}">
                    <b>
                        {{config('admin.name')}}
                    </b>
                </a>
            </div>
            <!-- /.login-logo -->
            <div class="login-box-body">
                <p class="login-box-msg">
                    {{ trans('admin.login') }}
                </p>
                <form action="{{ admin_base_path('auth/login') }}" method="post" id="login-form">
                    <div class="form-group has-feedback {!! !$errors->has('username') ?: 'has-error' !!}">
                        @if($errors->has('username')) @foreach($errors->get('username') as $message)
                        <label class="control-label" for="inputError">
                            <i class="fa fa-times-circle-o">
                            </i>
                            {{$message}}
                        </label>
                        </br>
                        @endforeach @endif
                        <input type="input" class="form-control" placeholder="{{ trans('admin.username') }}"
                        name="username" value="{{ old('username') }}">
                        <span class="glyphicon glyphicon-envelope form-control-feedback">
                        </span>
                    </div>
                    <div class="form-group has-feedback {!! !$errors->has('password') ?: 'has-error' !!}">
                        @if($errors->has('password')) @foreach($errors->get('password') as $message)
                        <label class="control-label" for="inputError">
                            <i class="fa fa-times-circle-o">
                            </i>
                            {{$message}}
                        </label>
                        </br>
                        @endforeach @endif
                        <input type="password" class="form-control" placeholder="{{ trans('admin.password') }}"
                        name="password">
                        <span class="glyphicon glyphicon-lock form-control-feedback">
                        </span>
                    </div>
                    <div class="form-group has-feedback {!! !$errors->has('captcha') ?: 'has-error' !!}">
                        @if($errors->has('captcha')) @foreach($errors->get('captcha') as $message)
                        <label class="control-label" for="inputError">
                            <i class="fa fa-times-circle-o">
                            </i>
                            {{$message}}
                        </label>
                        </br>
                        @endforeach @endif
                        <div id="div_geetest_lib">
                            <div id="div_id_embed">
                            </div>
                        </div>
                    </div>
                    <div class="row">
                        <!-- /.col -->
                        <div class="col-xs-4 col-md-offset-4">
                            <input type="hidden" name="_token" value="{{ csrf_token() }}">
                            <button type="submit" class="btn btn-primary btn-block btn-flat">
                                {{ trans('admin.login') }}
                            </button>
                        </div>
                        <!-- /.col -->
                    </div>
                </form>
            </div>
            <!-- /.login-box-body -->
        </div>
        <!-- /.login-box -->
        <!-- jQuery 2.1.4 -->
        <script src="{{ admin_asset(" /vendor/laravel-admin/AdminLTE/plugins/jQuery/jQuery-2.1.4.min.js
        ")}} ">
        </script>
        <!-- Bootstrap 3.3.5 -->
        <script src="{{ admin_asset(" /vendor/laravel-admin/AdminLTE/bootstrap/js/bootstrap.min.js
        ")}}">
        </script>
        <!-- iCheck -->
        <script src="{{ admin_asset(" /vendor/laravel-admin/AdminLTE/plugins/iCheck/icheck.min.js
        ")}}">
        </script>
        <script>
            $(function() {
                $('input').iCheck({
                    checkboxClass: 'icheckbox_square-blue',
                    radioClass: 'iradio_square-blue',
                    increaseArea: '20%' // optional
                });
            });
        </script>
        <script>
            var gtFailbackFrontInitial = function(result) {
                var s = document.createElement('script');
                s.id = 'gt_lib';
                s.src = 'http://static.geetest.com/static/js/geetest.0.0.0.js';
                s.charset = 'UTF-8';
                s.type = 'text/javascript';
                document.getElementsByTagName('head')[0].appendChild(s);
                var loaded = false;
                s.onload = s.onreadystatechange = function() {
                    if (!loaded && (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete')) {
                        loadGeetest(result);
                        loaded = true;
                    }
                };

            }
            //get  geetest server status, use the failback solution
            var loadGeetest = function(config) {

                //1. use geetest capthca
                window.gt_captcha_obj = new window.Geetest({
                    gt: config.gt,
                    challenge: config.challenge,
                    product: 'embed',
                    offline: !config.success
                });

                gt_captcha_obj.appendTo("#div_id_embed");
            }

            s = document.createElement('script');
            s.src = 'http://api.geetest.com/get.php?callback=gtcallback';
            $("#div_geetest_lib").append(s);

            var gtcallback = (function() {
                var status = 0,
                result, apiFail;
                return function(r) {
                    status += 1;

                    if (r) {
                        result = r;
                        setTimeout(function() {
                            if (!window.Geetest) {
                                apiFail = true;

                                gtFailbackFrontInitial(result)
                            }
                        },
                        1000)
                    } else if (apiFail) {
                        return
                    }

                    if (status == 2) {
                        loadGeetest(result);
                    }
                }
            })()

            $.ajax({
                url: "/auth/start_captcha?rand=" + Math.round(Math.random() * 100),
                type: "get",
                dataType: 'JSON',
                success: function(result) {
                    // console.log(result);
                    gtcallback(result)
                }
            })

            $(function() {
                //验证是否点击了极验证,没有点击不可以提交,并做出错误提示
                $('form#login-form').submit(function() {
                    var geetest_validate = $(this).find('input[name="geetest_validate"]').val();
                    if (!geetest_validate) {
                        return false
                    }
                })
            })
        </script>
    </body>

</html>

添加zh-CN语言包验证提示

/resources/lang/zh-CN中添加auth.php,代码如下:

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Authentication Language Lines
    |--------------------------------------------------------------------------
    |
    | The following language lines are used during authentication for various
    | messages that we need to display to the user. You are free to modify
    | these language lines according to your application's requirements.
    |
    */

    //验证错误提示
    'failed' => '信息不正确,验证失败',
    //登录失败多次提示
    'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',

];

总结

代码不易,思路不难,学到的点个赞,谢谢。

标签:
作者:猫巷

,

评论列表 (2)条评论
网友昵称:访客
访客游客2年前 (2018-10-12)回复
正好在重构登录系统,采纳一下试试
网友昵称:访客
访客游客2年前 (2018-10-12)回复
采纳了呢

发表评论

召唤伊斯特瓦尔