做项目的时候,用户认证几乎是必不可少的,如果我们的项目由于一些原因不得不使用 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. k8s节点NotReady问题处理

    我把三台虚拟机重启,发现2个节点一直处于NotReady状态,便去查找问题,到最后是因为子节点的kubelet的状态异常了,restart一下就好了,下面转一下解决的思路 昨天晚上,针对K8S环境做了 ...

  2. cp:复制文件和目录

    cp 命令,主要用来复制文件和目录,同时借助某些选项,还可以实现复制整个目录,以及比对两文件的新旧而予以升级等功能. cp 命令的基本格式如下:cp [选项] 源文件 目标目录/文件 选项: -a:相 ...

  3. HDU6579 Operation

    题目链接 问题分析 区间求异或和最大,比较自然的想到了线性基.而每次求一个区间的线性基显然是行不通的.我们考虑在每个位置求出首位置到当前位置的线性基.同时我们要使线性基中高位的位置所选的数尽量靠后.这 ...

  4. sqli-labs(43)

    0X01和42关比起来 只是闭合变了 那么我们可以构造 ');insert into users values(98,'zhong','zhong')# 成功注入

  5. kill 与 killall和过滤后杀掉

    1.绝杀 kill -9 PID  杀掉单一进程  例如:kill -9 pid号   同意的 kill -s SIGKILL   这个强大和危险的命令迫使进程在运行时突然终止,进程在结束后不能自我清 ...

  6. assertion的用法

    一.assertion的语法和语义     在软件开发中,assertion是一种经典的调试.测试方式,本文将深入解析assertion功能的使用以及其设计理念,并给出相关的例子. 清软国际java学 ...

  7. LeetCode 74. 搜索二维矩阵(Search a 2D Matrix)

    题目描述 编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值.该矩阵具有如下特性: 每行中的整数从左到右按升序排列. 每行的第一个整数大于前一行的最后一个整数. 示例 1: 输入: ma ...

  8. SpringBoot&Dubbo&Zookeeper远程调用项目搭建

    序言 Dubbo一款分布式服务框架,作为阿里巴巴SOA服务化治理方案的核心框架,通过高性能和透明化的RPC实现服务的远程调用,对服务的负载均衡以及项目的耦合性提供很强的解决方式;具体Dubbo的介绍和 ...

  9. Android 多分辨率与不同语言适配

    一.适配不同国家语言 智能手机系统设置里各国语言的选项,然后我们项目里可以通过资源目录实现适配语言.我们知道工程的根目录有个res/的目录,res/下有一个资源类型的目录,其中有个values/str ...

  10. 四、日志输出Reporter.log

    一.Reporter.log import org.testng.Reporter; public class TestLog { public static void main(String[] a ...