【问题标题】:Laravel Socialite - Save URL before redirectionLaravel Socialite - 在重定向之前保存 URL
【发布时间】:2017-11-05 21:37:17
【问题描述】:

我正在使用Laravel 5.4Socialite,因此我的用户可以使用Facebook 登录。

我的网站使用子域 newyork.example.com, paris.example.com example.com

回调登录的Facebook URL重定向必须是absolute所以我设置http://example.com

登录/facebook

public function redirectToProvider()
    {

        $_SESSION['originalURL'] ="http://paris.example.com";

        return Socialite::driver('facebook')
            ->scopes(['rsvp_event', 'public_profile'])
            ->redirect();
    }

登录/facebook/回调

 public function handleProviderCallback(SocialAccountService $service)
    {
        $user = $service->createOrGetUser(Socialite::driver('facebook')->user());
        // $user->token;

        Auth::login($user, true);

        $originalURL = $_SESSION['originalURL'];

        return redirect()->to($originalURL);
    }

问题

当我在 route 登录/facebook 时,我可以查看原始 URL paris.example.comHTTP_POST

当我在路由 login/facebook/callback 中时,HTTP_POSTexample.com,因为重定向的 URL 是 example.com。我尝试将 URL 保存在 session var 中,但 $_SESSION 为空。

问题

如何在 facebook 登录回调重定向后获取原始 url。 ?因此,如果我使用 paris.example.com 开始登录过程,我将被重定向到 example.com,然后我会重定向保存 url

sessions.php

'cookie' => 'laravel_session',

    /*
    |--------------------------------------------------------------------------
    | Session Cookie Path
    |--------------------------------------------------------------------------
    |
    | The session cookie path determines the path for which the cookie will
    | be regarded as available. Typically, this will be the root path of
    | your application but you are free to change this when necessary.
    |
    */

    'path' => '/',

    /*
    |--------------------------------------------------------------------------
    | Session Cookie Domain
    |--------------------------------------------------------------------------
    |
    | Here you may change the domain of the cookie used to identify a session
    | in your application. This will determine which domains the cookie is
    | available to in your application. A sensible default has been set.
    |
    */

    'domain' => env('SESSION_DOMAIN', '.localhost.com'),

    /*
    |--------------------------------------------------------------------------
    | HTTPS Only Cookies
    |--------------------------------------------------------------------------
    |
    | By setting this option to true, session cookies will only be sent back
    | to the server if the browser has a HTTPS connection. This will keep
    | the cookie from being sent to you if it can not be done securely.
    |
    */

    'secure' => env('SESSION_SECURE_COOKIE', false),

    /*
    |--------------------------------------------------------------------------
    | HTTP Access Only
    |--------------------------------------------------------------------------
    |
    | Setting this value to true will prevent JavaScript from accessing the
    | value of the cookie and the cookie will only be accessible through
    | the HTTP protocol. You are free to modify this option if needed.
    |
    */

    'http_only' => true,

【问题讨论】:

  • 您是否将所有 cookie 存储在 example.com 级别的域中?如果是这样,会话可以在所有子域之间共享,您可以从其中任何一个访问您放入会话中的数据
  • 您也可以将数据放在发送给 Facebook 的 state 中。我很想为此写一个答案,但我目前正面临社交名流的另一个问题(在寻找另一个问题时发现了这个问题)
  • @Kaktus,我复制了sessions 文件。因此,它可能会回答您有关 cookie 存储的问题。 state 在哪里?既然回调 url 是预先固定的,它不会被删除吗?

标签: php redirect laravel-5.4 laravel-socialite


【解决方案1】:

我用这种方式,行得通

public function socialConnectRedirect($type, Request $request)
{
    Session::put('redirect', $request->input('redirectTo'));
    if($type=='facebook'){
        return Socialite::driver($type)->scopes(['email', 'public_profile', 'user_birthday', 'user_location'])->redirect();
    }

    return Socialite::driver($type)->redirect();
}

在handleSocialCallback函数中,用户登录后

Auth::login($checkUser);
return redirect(Session::get('redirect'));
Session::forget('redirect');

【讨论】:

    【解决方案2】:

    如果您无法在网站之间共享会话,可以使用state OAuth 参数携带值。

    此代码已在自定义 OAuth 提供程序中进行了测试。以下是 Facebook 实现的样子(此特定代码未经测试)。

    在更改 state 的工作方式时需要牢记安全隐患。这是一篇关于http://www.thread-safe.com/2014/05/the-correct-use-of-state-parameter-in.html 的有趣文章,甚至还有一份关于如何将数据存储和签名到state 参数https://datatracker.ietf.org/doc/html/draft-bradley-oauth-jwt-encoded-state-00 的IETF 草案(我在下面的实现使用JSON 并且未签名)。

    <?php
    
    namespace App\Socialite;
    
    use Laravel\Socialite\Two\FacebookProvider;
    use Laravel\Socialite\Two\User;
    
    class CustomFacebookProvider extends FacebookProvider
    {
        protected $statePreviousUrl = null;
    
        public function withPreviousUrl($url)
        {
            $this->statePreviousUrl = $url;
    
            return $this;
        }
    
        protected function getState()
        {
            // The state becomes a JSON object with both the XRSF protection token and the url
            return json_encode([
                'state' => parent::getState(),
                'url' => $this->statePreviousUrl,
            ]);
        }
    
        protected function hasInvalidState()
        {
            if ($this->isStateless()) {
                return false;
            }
    
            $storedState = $this->request->session()->pull('state');
            $requestState = $this->request->input('state');
            $requestStateData = json_decode($requestState, true);
    
            // If the JSON is valid we extract the url here
            if (!is_null($requestStateData) && array_key_exists('url', $requestStateData)) {
                // Don't forget, this value is unsafe. Do additional checks before redirecting to that url
                $this->statePreviousUrl = $requestStateData['url'];
            }
    
            // If you don't share your session between your instances you can play it "stateless" by always returning false here
            // Doing so you loose all XRSF protection ! (but this might be the only way if you don't share your cookies)
            // return false;
    
            // If the session is shared, we finish by checking the full state
            // We compare the full json objects, no need to extract the state parameter
            return ! (strlen($storedState) > 0 && $requestState === $storedState);
        }
    
        protected function mapUserToObject(array $user)
        {
            return (new User)->setRaw($user)->map([
                // Data here will vary from provider to provider. The Facebook one is a bit more complex
                'id'    => $user['id'],
                'email' => $user['email'],
                // We add the extracted URL here so it can be access from the controller
                'previous_url' => $this->statePreviousUrl,
            ]);
        }
    }
    

    注册自定义控制器:

    <?php
    
    namespace App\Socialite;
    
    use Illuminate\Support\ServiceProvider as BaseServiceProvider;
    use Laravel\Socialite\Contracts\Factory;
    
    class ServiceProvider extends BaseServiceProvider
    {
        public function boot()
        {
            // @see https://medium.com/laravel-news/adding-auth-providers-to-laravel-socialite-ca0335929e42
            $socialite = $this->app->make(Factory::class);
            $socialite->extend(
                'custom-facebook',
                function ($app) use ($socialite) {
                    $config = $app['config']['services.facebook'];
                    return $socialite->buildProvider(CustomFacebookProvider::class, $config);
                }
            );
        }
    }
    

    用法:

    <?php
    
    namespace App\Http\Controllers;
    
    use App\User;
    use Laravel\Socialite\Contracts\Factory;
    
    class FacebookLoginController extends Controller
    {
        /**
         * @var Factory
         */
        protected $socialite;
    
        public function __construct(Factory $socialite)
        {
            $this->socialite = $socialite;
        }
    
        public function redirectToProvider()
        {
            return $this->socialite->driver('custom-facebook')->withPreviousUrl('https://paris.example.com/')->redirect();
        }
    
        public function handleProviderCallback()
        {
            $data = $this->socialite->driver('custom-facebook')->user();
    
            dd($data->previous_url);
        }
    }
    

    【讨论】:

    • 谢谢,我将您的提供者示例用于另一个提供者(用于战网),效果很好。这应该是理想的解决方案,在 OAuth 本身的设计中使用状态。
    【解决方案3】:

    关键是不要使用$_SESSION,而是使用session(['city' =&gt; 'paris']);和session('city') 来检索值。

    【讨论】:

      猜你喜欢
      • 2018-10-21
      • 1970-01-01
      • 2016-05-03
      • 1970-01-01
      • 2018-02-22
      • 2016-07-11
      • 1970-01-01
      • 2020-03-04
      • 1970-01-01
      相关资源
      最近更新 更多