Laravel 5.6 上的 Ajax 身份验证重定向

Posted

技术标签:

【中文标题】Laravel 5.6 上的 Ajax 身份验证重定向【英文标题】:Ajax Auth redirect on Laravel 5.6 【发布时间】:2018-11-25 04:24:50 【问题描述】:

我有一个触发 Ajax Post 的 Like 按钮,该路由受 auth:api 中间件保护:

我的项目/路由/api.php

Route::group(['middleware' => ['auth:api']], function () 
    Route::post('/v1/like', 'APIController@set_like');
);

当一个经过身份验证的用户点击like按钮时,一点问题都没有,一切都很顺利。但是当客人点击按钮时,我通过 javascript 将他们重定向到登录页面,并且在身份验证后,他们被重定向到 RedirectIfAuthenticated 中间件中指定的页面,因此通常是 /home。 我对该中间件进行了如下修改:

myproject/app/Http/Middleware/RedirectIfAuthenticated.php

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Auth;

class RedirectIfAuthenticated

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  string|null  $guard
     * @return mixed
     */
    public function handle($request, Closure $next, $guard = null)
    
        if (Auth::guard($guard)->check()) 
            return redirect()->intended('/home');
        
        return $next($request);
    

我的 Ajax 调用是这样的:

var toggleLike = function()
    var token = USER_TOKEN; //javascript variable
    var current_type = $(thisLikeable).attr("data-type");
    var current_id = $(thisLikeable).attr("data-id");
    var jqxhr = $.post( APP_URL + "/api/v1/like", api_token: token, type: current_type, id: current_id, function(data) 
        setLikeAppearance(data.message);
    )
    .fail(function(xhr, status, error)
        if (xhr.status == 401) 
            window.location = APP_URL + "/login" ;
        
    );
;

这里的问题是预期的()函数,对于 Ajax 调用,它没有存储正确的会话变量,我不知道如何正确设置它。 我显然遗漏了一些明显的东西,有人可以帮忙吗? 干杯!

编辑

我想要实现的是:

    GUEST 在 //mysite/blabla 点击喜欢按钮 被重定向到登录 登录(或注册) 被重定向到 //mysite/blabla 并且 Like 已经被触发

【问题讨论】:

api 上不会有会话。因为它是无状态的。 是的,但必须有办法处理这个.. 只需从 api 发送 401 响应。比你需要从前端处理它 不要从 api 重定向。 【参考方案1】:

发生的情况是,在 API 中会话不受管理,或者换句话说,它是无状态的。所以会话中间件并没有在 Laravel 框架上实现 API 请求。虽然可以手动添加,但也不是闲着用。因此,如果 API 不使用会话并使用重定向,fronted 不知道它,因为 API 和前端作为两个独立的应用程序工作。因此,您需要向前端发送响应的状态,并让前端像使用 ajax 一样处理重定向。如果未经身份验证,只需删除重定向并让 API 抛出未经授权的异常。然后,从处理程序,处理未经授权的异常。

这里是怎么做的。

将此添加到 app/Exceptions/Handler.php

/**
 * Convert an authentication exception into an unauthenticated response.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Illuminate\Auth\AuthenticationException  $exception
 * @return \Illuminate\Http\Response
 */
protected function unauthenticated($request, AuthenticationException $exception)

    if ($request->expectsJson()) 
        return response()->json(['error' => 'Unauthenticated.'], 401);
    

    return redirect()->guest('login');

如果请求是 json(api 请求),这将向用户发送 401 消息未验证,否则(如果 web 请求)将重定向到登录

检查下面的render 方法或从source 检查它以了解发生了什么。当抛出未经授权的异常时,我们会告诉检查请求类型,如果它来自 API 请求,我们将发送带有 401 状态代码的 json 响应。所以从前端知道我们可以在看到 401 状态码后将用户重定向到登录页面。

来源

/**
 * Render an exception into a response.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Exception  $e
 * @return \Symfony\Component\HttpFoundation\Response
 */
public function render($request, Exception $e)

    if (method_exists($e, 'render') && $response = $e->render($request)) 
        return Router::toResponse($request, $response);
     elseif ($e instanceof Responsable) 
        return $e->toResponse($request);
    
    $e = $this->prepareException($e);
    if ($e instanceof HttpResponseException) 
        return $e->getResponse();
     elseif ($e instanceof AuthenticationException) 
        return $this->unauthenticated($request, $e);
     elseif ($e instanceof ValidationException) 
        return $this->convertValidationExceptionToResponse($e, $request);
    
    return $request->expectsJson()
                    ? $this->prepareJsonResponse($request, $e)
                    : $this->prepareResponse($request, $e);

编辑后

预期的method() 仅用于网络路由,因为它使用会话来提取预期的路由或手动传递的值。这是预期的()方法。

/**
 * Create a new redirect response to the previously intended location.
 *
 * @param  string  $default
 * @param  int     $status
 * @param  array   $headers
 * @param  bool    $secure
 * @return \Illuminate\Http\RedirectResponse
 */
public function intended($default = '/', $status = 302, $headers = [], $secure = null)

    $path = $this->session->pull('url.intended', $default);

    return $this->to($path, $status, $headers, $secure);

要实现重定向到用户来自您的页面,您可以

1 - 手动传递一些带有 url 的查询,例如 /login?redirect=like(not the url, just a mapping for /comments/like url)&amp;value=true(true is liked, false is unliked) 并手动处理。

2 - 从 url 获取并检查查询参数

3 - 使用 to() 方法来传递预期的 url,而不是使用预期的()。这是 to() 方法。 (看4看推荐方式)

/**
 * Create a new redirect response to the given path.
 *
 * @param  string  $path
 * @param  int     $status
 * @param  array   $headers
 * @param  bool    $secure
 * @return \Illuminate\Http\RedirectResponse
 */
public function to($path, $status = 302, $headers = [], $secure = null)

    return $this->createRedirect($this->generator->to($path, [], $secure), $status, $headers);

4 - 但是,我建议发送重定向 url(我的意思是映射 ex:like)作为对前端的响应,并让前端处理重定向。因为如果 API 仅由网站使用,则 API 重定向将起作用。假设如果您在移动应用程序中使用相同的 api,想知道 API 重定向将如何工作。重定向不是 API 的工作,除非它是用于 OAuth 身份验证之类的东西,它会指定一个重定向 url。

记得清理 url 参数以阻止 XSS 之类的东西。更好的 发送一些值并将其映射到 url。喜欢

[
   //like is mapping
   //comments/like is the actual url

   'like' => 'comments/like'
]

然后,从数组中获取映射 url 或使用前端映射。

【讨论】:

我听不懂。在我的 Javascript 中,我已经收到错误 401,我的问题是我希望用户在单击按钮时登录,并被重定向到他们单击按钮的原始页面。所以这就是我最初使用预期()方法的原因。 intended() 仅适用于会话。所以预期()不能用于api调用。您应该将 url 作为 url 查询传递,手动验证它并对其进行清理并将其手动发送到 to() 方法而不是预期的() 方法。第一个参数是预期的网址。 我明白了,如果我直接在 URL 中设置参数,然后在重定向中处理它,它应该可以工作。老实说,在 URL 中设置参数并不是最好的解决方案,但如果我没有找到其他任何东西,我会使用你的建议。谢谢! 我赞成你的建议,因为它非常丰富和详细,非常感谢!我仍然不相信将参数传递给 URL,因为它打开了恶意的大门,所以我会等待其他人是否有同样的问题并用更简单的解决方案解决;)再次感谢! 最后,由于使用 Ajax 意味着无状态,正如您正确建议的那样,没有后端解决方案。有趣的是,在使用 Laravel 之前,我认为自己是一名前端开发人员:P 再次感谢!【参考方案2】:

您可以在 RedirectIfAuthenticated.php 中进行更改,以区分 Ajax 调用和正常登录,如下所示:

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Auth;

class RedirectIfAuthenticated

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  string|null  $guard
     * @return mixed
     */
    public function handle($request, Closure $next, $guard = null)
    
        if (Auth::guard($guard)->check()) 

            if ($request->has('api_token')) 
                // do whatever you want

                return response()->json('some response'); 
            

                return redirect()->intended('/home');
        
        return $next($request);
    

更新:

另一种解决方案是从您的路由中删除 Auth 中间件。在APIController@set_like函数中手动登录用户,触发like并返回json响应。

【讨论】:

因为我通过这个调用身份验证:window.location = APP_URL + "/login" ;这不是 Ajax 调用。 删除 Auth 中间件不是解决方案,抱歉 :)【参考方案3】:

如果按钮不适合访客,则不应在页面上呈现它。 相反,您应该呈现一个登录链接,然后如果用户登录,您会将他重定向回他之前的位置。现在,用户可以看到并点击按钮。

【讨论】:

这更像是一个 UI/UX 决定,就我而言,我更喜欢向用户展示原始操作并鼓励他们加入。 因此,您可以在 JavaScript 端处理它。如果你收到一个 401 状态码,那么可能会显示一个弹出模式来登录,如果登录成功,那么你再试一次 ajax 请求。我认为在 laravel 方面没有其他方法可以处理这个问题。我不确定

以上是关于Laravel 5.6 上的 Ajax 身份验证重定向的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 5.6 护照未经身份验证

该页面因不活动而过期。请刷新并重试。 laravel 5.6 使用 laravel 的身份验证

Laravel 5.6 对数据库中不同表的身份验证

减少对外部 API 的身份验证调用(Laravel 5.6)

Laravel 5.6 护照 API 身份验证在获取请求中不起作用

Laravel 5.6 上的 419 Ajax 错误 - 已编辑