028-86261949

当前位置:首页 > 技术交流 > laravel中验证码及图片上传

laravel中验证码及图片上传

2019/10/09 17:27 分类: 技术交流 浏览:1

 

1、验证码

 

1.1、问题说明

我们的注册功能存在一个问题,因我们表单未添加任何防护,恶意用户可以轻易使用机器人自动化注册新用户。机器人自由注册,对我们站点稳定性来讲是巨大的威胁,恶意用户可以很轻易的通过机器人程序在短时间内,注册大量用户,甚至于填满我们的数据库。

 

1.2、验证码

 

验证码 是防止恶意破解密码、刷票、论坛灌水、刷页的手段。验证码有 多种类型。 本项目中我们将使用图片验证码,其原理是让用户输入一个扭曲变形的图片上所显示的文字或数字,扭曲变形是为了避免被光学字符识别软件(OCR)自动辨识。由于计算机无法识别验证码的图片,所以回答出问题的用户就可以被认为是人类。

 

接下来我们将使用验证码来防卫的用户注册功能。

 

1.3、安装扩展包

我们将以第三方扩展包 mews/captcha 作为基础来实现 Laravel 中的验证码功能。

 

使用 Composer 安装:(需要打开fileinfo扩展)

 

composer require "mews/captcha:~2.0"

修改config/app.php,在providers添加

Mews\Captcha\CaptchaServiceProvider::class,

 

'providers' => [

        // ...

        Mews\Captcha\CaptchaServiceProvider::class,

    ]

在aliases添加

'Captcha' => Mews\Captcha\Facades\Captcha::class,

'aliases' => [

        // ...

        'Captcha' => Mews\Captcha\Facades\Captcha::class,

    ]

 

运行以下命令生成配置文件 config/captcha.php:

 

php artisan vendor:publish

然后选择7

我们可以打开配置文件,查看其内容:

 

config/captcha.php

 

<?php

 

return [

 

    'characters' => '2346789abcdefghjmnpqrtuxyzABCDEFGHJMNPQRTUXYZ',

 

    'default'   => [

        'length'    => 5,

        'width'     => 120,

        'height'    => 36,

        'quality'   => 90,

    ],

 

    'flat'   => [

        'length'    => 6,

        'width'     => 160,

        'height'    => 46,

        'quality'   => 90,

        'lines'     => 6,

        'bgImage'   => false,

        'bgColor'   => '#ecf2f4',

        'fontColors'=> ['#2c3e50', '#c0392b', '#16a085', '#c0392b', '#8e44ad', '#303f9f', '#f57c00', '#795548'],

        'contrast'  => -5,

    ],

 

    'mini'   => [

        'length'    => 3,

        'width'     => 60,

        'height'    => 32,

    ],

 

    'inverse'   => [

        'length'    => 5,

        'width'     => 120,

        'height'    => 36,

        'quality'   => 90,

        'sensitive' => true,

        'angle'     => 12,

        'sharpen'   => 10,

        'blur'      => 2,

        'invert'    => true,

        'contrast'  => -5,

    ]

];

可以看到这些配置选项都非常通俗易懂,characters 选项是用来显示给用户的所有字符串,default, flat, mini, inverse 分别是定义的四种验证码类型,你可以在此修改对应选项自定义验证码的长度、背景颜色、文字颜色等属性,在此不做过多叙述。

 

 

1.4、页面嵌入

此扩展包的使用分为两步:

 

前端展示 —— 生成验证码给用户展示,并收集用户输入的答案;

后端验证 —— 接收答案,检测用户输入的验证码是否正确。

 

 

1.4.1前端展示

 

<input id="captcha" class="form-control" name="captcha" >

<img class="thumbnail captcha" src="{{ captcha_src('flat') }}" onclick="this.src='/captcha/flat?'+Math.random()" title="点击图片重新获取验证码">

captcha_src() 方法是 mews/captcha 提供的辅助方法,用于生成验证码图片链接;

『验证码』区块中 onclick() 是 JavaScript 代码,实现了点击图片重新获取验证码的功能,允许用户在验证码太难识别的情况下换一张图片试试。

  1. 后端验证

前端展示部分我们已经开发完毕,接下来处理后端验证逻辑。 mews/captcha 是专门为 Laravel 量身定制的扩展包,能很好的兼容 Laravel 生成的注册逻辑。我们只需要在注册的时候,添加上表单验证规则即可:

 

app/Http/Controllers/Auth/RegisterController.php

 

<?php

.

.

.

 

class RegisterController extends Controller

{

    .

    .

    .

 

    /**

     * 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, [

            'name' => 'required|string|max:255',

            'email' => 'required|string|email|max:255|unique:users',

            'password' => 'required|string|min:6|confirmed',

            'captcha' => 'required|captcha',

        ], [

            'captcha.required' => '验证码不能为空',

            'captcha.captcha' => '请输入正确的验证码',

        ]);

    }

    .

    .

    .

}

我们添加了验证规则:

 

'captcha' => 'required|captcha',

表达式里的第二个 captcha 是 mews/captcha 自定义的表单验证规则。扩展包非常巧妙地利用了 Laravel 表单验证器提供的 自定义表单验证规则 功能。令我们在开发验证码时非常方便。

 

Validator 表单验证的 make() 方法第三个参数是自定义错误提示,这里我们对验证码的错误提示进行自定义。

 

1.4.2、图片上传

给数据表添加相应的字段

上传头像

接下来我们将开发个人资料里的头像上传功能。

 

模型文件修改

首先我们需在 User 模型里将 avatar 字段加入到允许修改的白名单 $fillable 中:

 

 

编辑页面

接下来我们在 资料编辑页面 的『个人简介』编辑框下面,增加头像上传的选项:

 

resources/views/users/edit.blade.php.

                <div class="form-group">

                    <label for="introduction-field">个人简介</label>

                    <textarea name="introduction" id="introduction-field" class="form-control" rows="3">{{ old('introduction', $user->introduction ) }}</textarea>

                </div>

 

                <div class="form-group">

                    <label for="" class="avatar-label">用户头像</label>

                    <input type="file" name="avatar">

 

                    @if($user->avatar)

                        <br>

                        <img class="thumbnail img-responsive" src="{{ $user->avatar }}" width="200" />

                    @endif

                </div>

 

在 Laravel 中,我们可直接通过 请求对象(Request) 来获取用户上传的文件,如以下两种方法:

 

// 第一种方法

$file = $request->file('avatar');

 

// 第二种方法,可读性更高

$file = $request->avatar;

接下来我们将在 UsersController 的 update() 方法中,利用 Laravel 的 dd() 调试方法,来查看文件上传的情况:

 

app/Http/Controllers/UsersController.php

 

<?php

class UsersController extends Controller

{

    public function update(UserRequest $request, User $user)

    {

        dd($request->avatar);

 

        $user->update($request->all());

        return redirect()->route('users.show', $user->id)->with('success', '个人资料更新成功!');

    }

}

测试一下:

 

访问 资料编辑页面 ;

点击 『choose file』 按钮,选择图片;

点击『保存』按钮提交表单:

 

打印的结果居然为空。

 

经过一番仔细检查后,发现是因为我们忘记为表单添加 enctype="multipart/form-data" 声明了。请记住,在图片或者文件上传时,为表单添加此句声明是必须的。那我们再次修改下:

 

resources/views/users/edit.blade.php

<form action="{{ route('users.update', $user->id) }}" method="POST" accept-charset="UTF-8" enctype="multipart/form-data">

 

Laravel 的『用户上传文件对象』底层使用了 Symfony 框架的 UploadedFile 对象进行渲染,为我们提供了便捷的文件读取和管理接口,我们将在后面使用这些方法。

 

现在我们已经能得到用户上传的图片数据了,接下来是对图片进行存储。

 

存储用户上传图片

//保存文件

$fileName = $request->file('cover')->store('public/cover');

//获取文件路径

var_dump(Storage::url($fileName));

由于图片是保存在storage目录,浏览器无法直接访问,所有需要

使用 Artisan 命令 storage:link 来创建符号链接:

php artisan storage:link

 

 

本项目中,我们不止上传头像需要用到『图片上传功能』,在后面其他功能中,我们也将会允许用户上传图片,所以此处我们需要预先设计一下图片上传相关的逻辑,我们可以将『图片上传』核心操作做成一个工具类:

 

app/Handlers/ImageUploadHandler.php

 

<?php

namespace App\Handlers;

class ImageUploadHandler

{

    // 只允许以下后缀名的图片文件上传

    protected $allowed_ext = ["png", "jpg", "gif", 'jpeg'];

 

    public function save($file, $folder, $file_prefix)

    {

        // 构建存储的文件夹规则,值如:uploads/images/avatars/201709/21/

        // 文件夹切割能让查找效率更高。

        $folder_name = "uploads/images/$folder/" . date("Ym/d/", time());

 

        // 文件具体存储的物理路径,`public_path()` 获取的是 `public` 文件夹的物理路径。

        // 值如:/home/vagrant/Code/larabbs/public/uploads/images/avatars/201709/21/

        $upload_path = public_path() . '/' . $folder_name;

 

        // 获取文件的后缀名,因图片从剪贴板里黏贴时后缀名为空,所以此处确保后缀一直存在

        $extension = strtolower($file->getClientOriginalExtension()) ?: 'png';

 

        // 拼接文件名,加前缀是为了增加辨析度,前缀可以是相关数据模型的 ID

        // 值如:1_1493521050_7BVc9v9ujP.png

        $filename = $file_prefix . '_' . time() . '_' . str_random(10) . '.' . $extension;

 

        // 如果上传的不是图片将终止操作

        if ( ! in_array($extension, $this->allowed_ext)) {

            return false;

        }

 

        // 将图片移动到我们的目标存储路径中

        $file->move($upload_path, $filename);

 

        return [

            'path' => config('app.url') . "/$folder_name$filename"

        ];

    }

}

 

我们将使用 app/Handlers 文件夹来存放本项目的工具类,『工具类(utility class)』是指一些跟业务逻辑相关性不强的类,Handlers 意为 处理器 ,ImageUploadHandler 意为图片上传处理器,简单易懂。

 

接下来我们需要在 UsersController 里调用:

 

app/Http/Controllers/UsersController.php

 

<?php

.

.

.

use App\Handlers\ImageUploadHandler;

 

class UsersController extends Controller

{

    .

    .

    .

 

    public function update(UserRequest $request, ImageUploadHandler $uploader, User $user)

    {

        $data = $request->all();

 

        if ($request->avatar) {

            $result = $uploader->save($request->avatar, 'avatars', $user->id);

            if ($result) {

                $data['avatar'] = $result['path'];

            }

        }

 

        $user->update($data);

        return redirect()->route('users.show', $user->id)->with('success', '个人资料更新成功!');

    }

}

因为我们使用了命名空间,所以需要在顶部加载 use App\Handlers\ImageUploadHandler;;

$data = $request->all(); 赋值 $data 变量,以便对更新数据的操作;

以下代码处理了图片上传的逻辑,注意 if ($result) 的判断是因为 ImageUploadHandler 对文件后缀名做了限定,不允许的情况下将返回 false:

if ($request->avatar) {

    $result = $uploader->save($request->avatar, 'avatars', $user->id);

    if ($result) {

        $data['avatar'] = $result['path'];

    }

}

$user->update($data); 这一步才是执行更新。

 

#标签:laravel验证码,laravel图片上传