简体   繁体   English

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

[英]Ajax Auth redirect on Laravel 5.6

I have a Like button that fires an Ajax Post, this route is protected by auth:api middleware:我有一个触发 Ajax Post 的 Like 按钮,该路由受 auth:api 中间件保护:

myproject/routes/api.php myproject/routes/api.php

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

When an authenticated user clicks the like button, no problem at all, everything works smoothly.当经过身份验证的用户单击“赞”按钮时,完全没有问题,一切正常。 But when guests click the button, I redirected them to login page via Javascript and after authentication they are redirected to the page specified in RedirectIfAuthenticated middleware, so usually to /home.但是当客人点击按钮时,我通过 Javascript 将他们重定向到登录页面,在身份验证后,他们被重定向到 RedirectIfAuthenticated 中间件中指定的页面,通常是 /home。 I modified that middleware as follows:我修改了该中间件如下:

myproject/app/Http/Middleware/RedirectIfAuthenticated.php 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);
    }
}

My Ajax call is this:我的 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" ;
        }
    });
};

The problem here's the intended() function, which for Ajax calls is not storing the correct session variable and I am not figuring out how to set it properly.这里的问题是 expected() 函数,对于 Ajax 调用,它没有存储正确的会话变量,我不知道如何正确设置它。 I am clearly missing something obvious, can anyone help?我显然遗漏了一些明显的东西,有人可以帮忙吗? Cheers!干杯!

EDIT编辑

What I want to achieve is this:我想要实现的是:

  1. GUEST is in //mysite/blabla来宾在 //mysite/blabla
  2. clicks Like button点击“喜欢”按钮
  3. gets redirected to login被重定向到登录
  4. logs in (or register)登录(或注册)
  5. gets redirected to //mysite/blabla with the Like already triggered on被重定向到 //mysite/blabla 并且 Like 已经被触发

What's happening is that in APIs sessions are not managed or in other words it's stateless.正在发生的事情是 API 中的会话不受管理,或者换句话说,它是无状态的。 So the session middleware is not implemented on Laravel framework for API requests.所以会话中间件没有在 Laravel 框架上实现 API 请求。 Though you can manually add, it's not idle to use.虽然可以手动添加,但使用起来也不是闲着。 So if the API does not use sessions and uses the redirect, fronted does not know about it, as API and frontend work as two separate apps.因此,如果 API 不使用会话并使用重定向,则前端不知道它,因为 API 和前端作为两个独立的应用程序工作。 SO you need to send the frontend the status of the response and let the frontend handle the redirect as you have done with ajax.因此,您需要向前端发送响应的状态,并让前端像使用 ajax 一样处理重定向。 Just remove the redirect if unauthenticated and let the API throw unauthorized exception.如果未经身份验证,只需删除重定向并让 API 抛出未经授权的异常。 Then, from the handler, handle the unauthorized exception.然后,从处理程序,处理未经授权的异常。

Here is how to do it.这是如何做到的。

Add this to app/Exceptions/Handler.php将此添加到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');
}

this will send the user a 401 with message Unauthenticated if the request was json(api request), else(if web request) will redirect to login如果请求是 json(api 请求),这将向用户发送带有消息 Unauthenticated 的 401,否则(如果 Web 请求)将重定向到登录

check the render method below or check it from source to understand what's happening.检查下面的render方法或从源代码检查它以了解发生了什么。 when an unauthorized exception is thrown we are telling to check the request type and if it's from an API request, we are sending a json response with 401 status code.当抛出未经授权的异常时,我们告诉检查请求类型,如果它来自 API 请求,我们将发送带有 401 状态代码的 json 响应。 So know from frontend we could redirect the user to login page after seeing the 401 status code.所以从前端知道我们可以在看到 401 状态代码后将用户重定向到登录页面。

From source从源头

/**
 * 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);
}

AFTER EDIT编辑后

The intended method() is only for web routes as it uses session to extract the intended route or manually passed value.预期的method()仅用于 Web 路由,因为它使用 session 来提取预期的路由或手动传递的值。 Here is the intended() 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);
}

To achive the redirect to the page the user is comming from you can要重定向到用户来自您的页面,您可以

1 - Manually pass some queries with url like /login?redirect=like(not the url, just a mapping for /comments/like url)&value=true(true is liked, false is unliked) and handle it manually. 1 - 手动传递一些带有 url 的查询,例如/login?redirect=like(not the url, just a mapping for /comments/like url)&value=true(true is liked, false is unliked)并手动处理。

2 - Get and check the query parameters from url 2 - 从 url 获取并检查查询参数

3 - Use to() method to pass the intended url instead of using intended(). 3 - 使用 to() 方法传递预期的 url 而不是使用 expected()。 here is the to() method.这是 to() 方法。 (see 4 to see the recommended way) (看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 - But, I would recommend sending redirect url (I mean the mapping ex: like) as a response to frontend and let the frontend handle the redirecting. 4 - 但是,我建议发送重定向 url(我的意思是映射 ex: like)作为对前端的响应,并让前端处理重定向。 As API redirecting will work if the API is used by websites only.如果 API 仅由网站使用,则 API 重定向将起作用。 Suppose if you are using this same api for a mobile app, wonder how API redirect will work.假设您在移动应用程序中使用相同的 api,想知道 API 重定向将如何工作。 It's not a work of API to redirect, unless if it's for things like OAuth Authentication, which would have a redirect url specified.重定向不是 API 的工作,除非它是用于 OAuth 身份验证之类的东西,它会指定一个重定向 url。

Remember to sanitize the url params to block XSS like stuff.请记住清理 url 参数以阻止 XSS 之类的东西。 Better Send some values and map it to the urls.最好发送一些值并将其映射到 url。 Like喜欢

[ //like is mapping //comments/like is the actual url 'like' => 'comments/like' ]

Then, get the mapping url from array or use frontend mappings.然后,从数组中获取映射 url 或使用前端映射。

You can make changes in your RedirectIfAuthenticated.php to distinguish between Ajax call & normal login like this:您可以在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);
    }
}

Update:更新:

Another solution is to remove Auth middleware from your route.另一种解决方案是从您的路由中删除Auth中间件。 In APIController@set_like function manually login user, trigger like & return json response.APIController@set_like函数中手动登录用户,触发 like & return json 响应。

If a button is not for a guest, then you shouldn't render it on page.如果一个按钮不是给客人的,那么你不应该在页面上呈现它。 Instead, you should render a link to login, then if the user logs in you will redirect him back to where he was before.相反,您应该呈现一个登录链接,然后如果用户登录,您会将他重定向回他之前的位置。 Now, user can see and click the button.现在,用户可以看到并单击该按钮。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM