做项目的时候,用户认证几乎是必不可少的,如果我们的项目由于一些原因不得不使用 users 之外的用户表进行认证,那么就需要多做一点工作来完成这个功能。

现在假设我们只需要修改登录用户的表,表名和表结构都与框架默认的表users不同,文档没有教我们如何去做,但是别慌,稍微看下框架实现用户认证的源码就能轻松实现。

首先,自定义一张表用来登录,表结构和模拟数据如下:

表 admins

id login_name login_pass
1 admin $2y$10$2MUhp7b6ghVOngb/.b/x6uuEW/yL3FqPKJztawrM0U577Clf07xda

从配置文件入手

用户认证相关的配置都保存在config/auth.php文件中,先来看看配置文件的内容:

        <?php

        return [

            /*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
|
| This option controls the default authentication "guard" and password
| reset options for your application. You may change these defaults
| as required, but they're a perfect start for most applications.
|
*/ 'defaults' => [
'guard' => 'web',
'passwords' => 'users',
], /*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| here which uses session storage and the Eloquent user provider.
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| Supported: "session", "token"
|
*/ 'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
], 'api' => [
'driver' => 'passport',
'provider' => 'users',
],
], /*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| If you have multiple user tables or models you may configure multiple
| sources which represent each model / table. These sources may then
| be assigned to any extra authentication guards you have defined.
|
| Supported: "database", "eloquent"
|
*/ 'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
], // 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
], /*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
|
| You may specify multiple password reset configurations if you have more
| than one user table or model in the application and you want to have
| separate password reset settings based on the specific user types.
|
| The expire time is the number of minutes that the reset token should be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
*/ 'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
],
], ];

默认使用的守卫是web,而web守卫使用的认证驱动是session,用户提供器是users。假设我们的需求只是将用户的提供器由users改为admins,那么我们需要做两步操作:

  • 修改默认的用户提供器,将provider=>'users'改为provider=>'admins'
          'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
],
  • 配置admins提供器,假设依旧使用eloquent作为驱动,并创建好了admins表的模型
    'providers' => [
'admins' => [
'driver' => 'eloquent',
'model' => App\Admin::class
]
],

使用Auth门面的attempt方法进行登录

SessionGuard 中的attempt方法:

    //Illuminate\Auth\SessionGuard
public function attempt(array $credentials = [], $remember = false)
{
$this->fireAttemptEvent($credentials, $remember); $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials); // If an implementation of UserInterface was returned, we'll ask the provider
// to validate the user against the given credentials, and if they are in
// fact valid we'll log the users into the application and return true.
if ($this->hasValidCredentials($user, $credentials)) {
$this->login($user, $remember); return true;
} // If the authentication attempt fails we will fire an event so that the user
// may be notified of any suspicious attempts to access their account from
// an unrecognized user. A developer may listen to this event as needed.
$this->fireFailedEvent($user, $credentials); return false;
}

该方法中调用 UserProvider 接口的retrieveByCredentials方法检索用户,根据我们的配置,UserProvider接口的具体实现应该是EloquentUserProvider,因此,我们定位到EloquentUserProviderretrieveByCredentials方法:

    //Illuminate\Auth\EloquentUserProvider
public function retrieveByCredentials(array $credentials)
{
if (empty($credentials) ||
(count($credentials) === 1 &&
array_key_exists('password', $credentials))) {
return;
} // First we will add each credential element to the query as a where clause.
// Then we can execute the query and, if we found a user, return it in a
// Eloquent User "model" that will be utilized by the Guard instances.
$query = $this->createModel()->newQuery(); foreach ($credentials as $key => $value) {
if (Str::contains($key, 'password')) {
continue;
} if (is_array($value) || $value instanceof Arrayable) {
$query->whereIn($key, $value);
} else {
$query->where($key, $value);
}
} return $query->first();
}

该方法会使用传入的参数(不包含password)到我们配置的数据表中搜索数据,查询到符合条件的数据之后返回对应的用户信息,然后attempt方法会进行密码校验,校验密码的方法为:

    //Illuminate\Auth\SessionGuard
/**
* Determine if the user matches the credentials.
*
* @param mixed $user
* @param array $credentials
* @return bool
*/
protected function hasValidCredentials($user, $credentials)
{
return ! is_null($user) && $this->provider->validateCredentials($user, $credentials);
}

进一步查看EloquentUserProvider中的validateCredentials方法

    //Illuminate\Auth\EloquentUserProvider
public function validateCredentials(UserContract $user, array $credentials)
{
$plain = $credentials['password']; return $this->hasher->check($plain, $user->getAuthPassword());
}

通过validateCredentials可以看出,提交的认证数据中密码字段名必须是password,这个无法自定义。同时可以看到,入参$user必须实现Illuminate\Contracts\Auth\Authenticatable接口(UserContract是别名)。

修改 Admin 模型

Admin模型必须实现Illuminate\Contracts\Auth\Authenticatable接口,可以借鉴一下User模型,让Admin直接继承Illuminate\Foundation\Auth\User 就可以,然后重写getAuthPassword方法,正确获取密码字段:

    // App\Admin
public function getAuthPassword()
{
return $this->login_pass;
}

不出意外的话,这个时候就能使用admins表进行登录了。


Larval 5.4的默认Auth登陆传入邮件和用户密码到attempt 方法来认证,通过email 的值获取,如果用户被找到,经哈希运算后存储在数据中的password将会和传递过来的经哈希运算处理的passwrod值进行比较。如果两个经哈希运算的密码相匹配那么将会为这个用户开启一个认证Session。

参考上面的分析,我们就需要对EloquentUserProvider中的validateCredentials方法进行重写,步骤如下

1. 修改 App\Models\User.php 添加如下代码

    public function getAuthPassword()
{
return ['password' => $this->attributes['password'], 'salt' => $this->attributes['salt']];
}

2. 建立一个自己的UserProvider.php 的实现

    <?php
namespace App\Foundation\Auth; use Illuminate\Auth\EloquentUserProvider;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Support\Str; /**
* 重写用户密码校验逻辑
* Class GfzxEloquentUserProvider
* @package App\Foundation\Auth
*/
class GfzxEloquentUserProvider extends EloquentUserProvider
{
/**
* Validate a user against the given credentials.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param array $credentials
* @return bool
*/
public function validateCredentials(Authenticatable $user, array $credentials)
{
$plain = $credentials['password'];
$authPassword = $user->getAuthPassword();
return md5($plain . $authPassword['salt']) == $authPassword['password'];
}
}

3. 将User Providers换成我们自己的GfzxEloquentUserProvider

修改 app/Providers/AuthServiceProvider.php

    <?php

    namespace App\Providers;

    use App\Foundation\Auth\GfzxEloquentUserProvider;
use Auth;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; class AuthServiceProvider extends ServiceProvider
{
.
.
. /**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies(); Auth::provider('gfzx-eloquent', function ($app, $config) {
return new GfzxEloquentUserProvider($this->app['hash'], $config['model']);
});
}
}

4. 修改 config/auth.php

       'providers' => [
'users' => [
'driver' => 'gfzx-eloquent',
'model' => App\Models\User::class,
],
],

这是就可以用过salt+passwrod的方式密码认证了

文章参考

laravel 修改用户模块密码验证

Laravel 中自定义用户登录的数据表

laravel修改用户模块的密码验证的更多相关文章

  1. linux 用户管理修改用户信息、密码状态、删除用户、退出登陆、切换用户

    修改用户信息usermoduseradd支持的选项usermod都支持passwd有两个选项-l(在密码串前面加了两个叹号),-u,usermod有两个选项:-L 临时锁定用户(Lock)(在密码串前 ...

  2. Ubuntu修改用户和root密码

    如果要修改root的密码:sudo passwd 如果要修改_当前_用户的密码:passwd 如果要修改其他用户的密码(你得有权限):sudo passwd USERNAME,USERNAME就是你要 ...

  3. laravel 修改时邮箱字段唯一性验证时忽略指定 ID

  4. MySQL8 修改密码验证插件

    MySQL8 修改密码验证插件 查看当前用户使用的密码验证插件 mysql> show variables like '%auth%'; +--------------------------- ...

  5. linux中用户信息及密码相关知识

    在linux中若修改用户信息.密码,组群信息.密码等.其实是在修改/etc/passwd,/etc/shadow,/etc/group,/etc/groupshadow等文件的内容. 这四个文件的意思 ...

  6. pure-ftp 修改用户信息

    1.修改用户test的密码 [root@localhost bin]# ./pure-pw passwd test #修改密码 Password: Enter it again: [root@loca ...

  7. Linux基础命令---修改用户密码

    passwd 更改用户密码,超级用户可以修改所有用户密码,普通用户只能修改自己的密码.这个任务是通过调用LinuxPAM和LibuserAPI来完成的.本质上,它使用LinuxPAM将自己初始化为一个 ...

  8. 2015年末分享:利用RS修改用户密码

    马上就要2016农历新年了,送点什么给大家呢?我觉得还是分享点技术吧.前不久用户在抱怨为什么登录Cognos Connection的密码不能让我们自己改?相信Cognos开发的很多人知道,Cognos ...

  9. 从零开始实现asp.net MVC4框架网站的用户登录以及权限验证模块 详细教程

    从零开始实现asp.net MVC4框架网站的用户登录以及权限验证模块 详细教程   用户登录与权限验证是网站不可缺少的一部分功能,asp.net MVC4框架内置了用于实现该功能的类库,只需要简单搭 ...

随机推荐

  1. JavaScript学习笔记之Array

    数组的定义: 1,var arr=new Array();      -->数组是特殊的对象,typeOf的返回值是object arr[0] arr[1] ... 2,var arr=new ...

  2. 利用node构建本地服务

    利用node构建本地服务 首先安装下node.js,地址为https://nodejs.org/en/,然后安装npm. node.js的中文api地址http://nodeapi.ucdok.com ...

  3. FFmpeg 1.2 for Android 生成一个动态库

    上一篇<FFmpeg 1.2 for Android 编译动态库>里沃特跟大家介绍了如何编译动态库,但当时所生成的动态库总共包含10个so文件,这样要是加载起来会严重影响软件的启动速度,后 ...

  4. HDU 2577(DP)

    题意:要求一个字符串输入,按键盘的最少次数.有Caps Lock和Shift两种转换大小写输入的方式 思路:用dpa与dpb数组分别记录Caps Lock的开关状态,dpa表示不开,dpb表示开 代码 ...

  5. global.asax、global.asax.compiled、PrecompiledApp.config三者关系

    global.asax用WebDeploy发布后,会在bin下面产生一个global.asax.compiled,同时根目录下产生PrecompiledApp.config. 正常情况下global. ...

  6. WPF串口通信数据采集

    使用WPF做的串口数据采集并动态显示在表格中的小程序,程序运行之后,自动打开串口,从COM1接收数据,并把接收到的数据进行处理显示在DataGrid中. 串口通信程序是从另外一个串口助手中扒的,去掉了 ...

  7. WinForm编程时窗体设计器中ComboBox控件大小的设置

    问题描述: 在VS中的窗体设计器中拖放一个ComboBox控件后想调整控件的大小.发现在控件上用鼠标只能拖动宽度(Width)无法拖动(Height). 解决过程: 1.控件无法拖动,就在属性窗口中设 ...

  8. window下如何搭建linux环境

    1.使用虚拟机 使用VMware虚拟机,下载linux内核系统,加载运行. 2.cygwin 安装cygwin,设置环境变量. 第二种方法还是比较简便的.优先考虑.

  9. java字符串输出

    package mytest; public class Mycode { public static void main(String[] args){ String[]seasons = {&qu ...

  10. Redis安装以及基本数据类型

    安装 1.阿里云CentOS 1.进入到centos系统,输入wget http://download.redis.io/releases/redis-3.2.8.tar.gz ,下载好目前最新版的R ...