流明的HTTP基本身份验证,不使用数据库

流明的HTTP基本身份验证,不使用数据库

问题描述:

I'm creating a RESTful API using Lumen and would like to add HTTP basic Authentication for security.

On the routes.php file, it set the auth.basic middle for every routes:

$app->get('profile', ['middleware' => 'auth.basic', function() {
     // logic here
}]);

Now when I access http://example-api.local/profile I am now prompted with the HTTP basic authentication, which is good. But when I try to login, I get this error message: Fatal error: Class '\App\User' not found in C:\..\vendor\illuminate\auth\EloquentUserProvider.php on line 126

I do not want the validation of users to be done on a database since I will just have one credential so most likely it will just get the username and password on a variable and validate it from there.

Btw, I reference it thru this laracast tutorial. Though it is a Laravel app tutorial, I am implementing it on Lumen app.

I am answering my own question as I was able to make it work but would still like to know more insights from others regarding my solution and the proper laravel way of doing it.

I was able to work on this by creating a custom middleware that does this:

<?php

namespace App\Http\Middleware;

use Closure;

class HttpBasicAuth
{

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $envs = [
            'staging',
            'production'
        ];

        if(in_array(app()->environment(), $envs)) {
            if($request->getUser() != env('API_USERNAME') || $request->getPassword() != env('API_PASSWORD')) {
                $headers = array('WWW-Authenticate' => 'Basic');
                return response('Unauthorized', 401, $headers);
            }
        }

        return $next($request);
    }

}

If you'll look into the code, it is pretty basic and works well. Though I am wondering if there is a "Laravel" way of doing this as the code above is a plain PHP code that does HTTP basic authentication.

If you'll notice, validation of username and password is hard coded on the .env file as I do not see the need for database access for validation.

Check your bootstrap/app.php. Make sure you have registered your auth.basic middleware, something like this:

$app->routeMiddleware([
    'auth.basic' => Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
]);

After that, change your routes:

$app->get('/profile', ['middleware' => 'auth.basic', function() {
    // Logic
}]);

EDIT

If you want to use database instead of eloquent authentication, you may call:

Auth::setDefaultDriver('database');

Before you attempt to authenticate:

Auth::attempt([
    'email' => 'info@foo.bar',
    'password' => 'secret',
]);

Edit #2

If you wish to authenticate in hardcode ways, you may define your own driver for AuthManager class:

Auth::setDefaultDriver('basic');

Auth::extend('basic', function () {
    return new App\Auth\Basic();
});

And then below is the basic of App\Auth\Basic class:

<?php

namespace App\Auth;

use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Auth\Authenticatable;

class Basic implements UserProvider
{
    /**
     * Retrieve a user by their unique identifier.
     *
     * @param  mixed  $identifier
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveById($identifier)
    {

    }

    /**
     * Retrieve a user by their unique identifier and "remember me" token.
     *
     * @param  mixed   $identifier
     * @param  string  $token
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveByToken($identifier, $token)
    {

    }

    /**
     * Update the "remember me" token for the given user in storage.
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
     * @param  string  $token
     * @return void
     */
    public function updateRememberToken(Authenticatable $user, $token)
    {

    }

    /**
     * Retrieve a user by the given credentials.
     *
     * @param  array  $credentials
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveByCredentials(array $credentials)
    {
        return new User($credentials);
    }

    /**
     * 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)
    {
        $identifier = $user->getAuthIdentifier();
        $password = $user->getAuthPassword();

        return ($identifier === 'info@foobarinc.com' && $password === 'password');
    }
}

Note that validateCredentials method needs first argument is an implementation of Illuminate\Contracts\Auth\Authenticatable interface, so you need to create you own User class:

<?php

namespace App\Auth;

use Illuminate\Support\Fluent;
use Illuminate\Contracts\Auth\Authenticatable;

class User extends Fluent implements Authenticatable
{
    /**
     * Get the unique identifier for the user.
     *
     * @return mixed
     */
    public function getAuthIdentifier()
    {
        return $this->email;
    }

    /**
     * Get the password for the user.
     *
     * @return string
     */
    public function getAuthPassword()
    {
        return $this->password;
    }

    /**
     * Get the token value for the "remember me" session.
     *
     * @return string
     */
    public function getRememberToken()
    {

    }

    /**
     * Set the token value for the "remember me" session.
     *
     * @param  string  $value
     * @return void
     */
    public function setRememberToken($value)
    {

    }

    /**
     * Get the column name for the "remember me" token.
     *
     * @return string
     */
    public function getRememberTokenName()
    {

    }
}

And you may test your own driver via Auth::attempt method:

Auth::setDefaultDriver('basic');

Auth::extend('basic', function () {
    return new App\Auth\Basic();
});

dd(Auth::attempt([
    'email' => 'info@foobarinc.com',
    'password' => 'password',
])); // return true

First we will extend AuthenticateWithBasicAuth in our middleware.

<?php

 namespace App\Http\Middleware;
 use \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;


 class HttpBasicAuth extends AuthenticateWithBasicAuth
 {

 }

In config/auth.php create custom guard and we will use custom_http_guard with HttpBasicAuth.

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'token',
        'provider' => 'users',
    ],

    'custom_http_guard' => [
        'driver' => 'token',
        'provider' => 'custom_http_provider',
    ],
],

We will use Laravel's default 'token' driver.

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

 'custom_http_provider' => [
     'data' => [
         'email' => 'info@foo.bar',
         'password' => 'secret',
      ]
 ],
],

If you can find the way to return data like above.Then you rock and get code following laravel standards.

Hope you got the idea! Looking for final solution. If someone can complete :)

Vaibhav Arora