Laravel 5.1 / AngularJS:在Angular视图中重置密码(如何验证CSRF令牌?)

时间:2015-12-18 02:37:48

标签: php angularjs laravel laravel-5 csrf

我设法调整默认的Laravel auth,以便它可以作为我的AngularJS的API工作,到目前为止一切正常。可以转到/重置并输入电子邮件,并收到一封带有密码重置链接的电子邮件,该链接转至/ reset / {token},如果您没有收到任何验证错误,您的密码将成功更改。

唯一的问题是,由于我使用的是Angular视图,因此在显示reset-password状态之前,确实没有任何验证令牌并确保它不仅仅是乱码。我尝试将其添加到控制器的顶部:

    if ($stateParams.token != $cookies.get('XSRF_TOKEN')) {
        $state.go('reset');
    }

...基本上会看到令牌是否是当前的CSRF令牌,但是这不起作用,因为当发送密码重置链接时,CSRF令牌被更改或者某些东西......它不再是来自的令牌会议。

任何人都有任何想法我怎么能这样做?如果在`/ reset /:token'的url中输入的令牌无效,我想重定向用户。

这是我的代码..

App.js:

        .state('reset', {
            url: '/reset',
            data: {
                permissions: {
                    except: ['isLoggedIn'],
                    redirectTo: 'user.dashboard'
                }
            },
            templateUrl: 'views/auth/forgot-password.html',
            controller: 'ForgotPasswordController as forgot'
        })
        .state('reset-password', {
            url: '/reset/:token',
            data: {
                permissions: {
                    except: ['isLoggedIn'],
                    redirectTo: 'user.dashboard'
                }
            },
            templateUrl: 'views/auth/reset-password.html',
            controller: 'ResetPasswordController as reset'
        })

这是ResetsPassword.php中的ResetsPassword特征。大多数已经设置但我删除/更改了很多以作为API工作:

     /**
     * Send a reset link to the given user.
     */
    public function postEmail(EmailRequest $request)
    {
        $response = Password::sendResetLink($request->only('email'), function (Message $message) {
            $message->subject($this->getEmailSubject());
        });

        switch ($response) {
            case Password::RESET_LINK_SENT:
                return;

            case Password::INVALID_USER:
                return response()->json([
                    'denied' => 'We couldn\'t find your account with that information.'
                ], 404);
        }
    }

    /**
     * Get the e-mail subject line to be used for the reset link email.
     */
    protected function getEmailSubject()
    {
        return property_exists($this, 'subject') ? $this->subject : 'Your Password Reset Link';
    }

    /**
     * Reset the given user's password.
     */
    public function postReset(ResetRequest $request)
    {
        $credentials = $request->only(
            'password', 'password_confirmation', 'token'
        );

        $response = Password::reset($credentials, function ($user, $password) {
            $this->resetPassword($user, $password);
        });

        switch ($response) {
            case Password::PASSWORD_RESET:
                return;

            default:
                return response()->json([
                    'error' => [
                        'message' => 'Could not reset password'
                    ]
                ], 400);
        }
    }

    /**
     * Reset the given user's password.
     */
    protected function resetPassword($user, $password)
    {
        $user->password = bcrypt($password);

        $user->save();
    }

1 个答案:

答案 0 :(得分:5)

想出来了。

对于任何有类似问题的人......这就是我解决它的方法(以后可能会让它变得更好,但现在它可以了。)

我为API reset/password添加了另一个网址,它需要一个GET请求。我根据$stateParams值传递令牌,如果password_resets表中不存在该令牌,或者该令牌确实存在且已过期,则返回一些错误。在控制器中,我通过重定向处理错误。我不认为这是理想的,因为任何查看源代码的人都可以更改它并删除重定向,因此我必须找到更好的方法来实现它。

但同样,现在它仍然有效,但它仍然是一种解决方案。

ResetsPasswords.php(添加了get请求的方法):

public function verifyToken(Request $request)
    {
        $user = DB::table('password_resets')->where('token', $request->only('token'))->first();

        if ($user) {
            if ($user->created_at > Carbon::now()->subHours(2)) {
                return response()->json([
                    'success' => [
                        'message' => 'Token is valid and not expired.'
                    ]
                ], 200);
            } else {
                return response()->json([
                    'error' => [
                        'message' => 'Token is valid but it\'s expired.'
                    ]
                ], 401);
            }
        } else {
            return response()->json([
                'error' => [
                    'message' => 'Token is invalid.'
                ]
            ], 401);
        }
    }

在我的resetPasswordController.js中,我只是检查respose是否返回'success'或任何'error'响应,如果是'错误'响应,我会做$state.go('reset')之类的重定向他们回到“忘记密码”表单,在那里他们输入他们的电子邮件以获得重置密码链接。

修改

我认为在控制器中检查有效令牌是不好的,因为它总是会加载视图至少一瞬间。我正在寻找某种中间件,但后来我忘了我已经在使用angular-permission软件包,这种软件包充当了前端中间件。

我定义了一个角色isTokenValid,并将其设置为自动调用我拥有的authService中的函数,并根据令牌的有效性从我的API获取响应。如果成功,该角色允许用户进入该状态。否则,它会重定向到reset状态。这可以防止显示分秒的视图。行为与Laravel中间件非常相似。

唯一的问题是,因为它发生在前端,任何黑客都可以绕过它,但这没关系,因为服务器端代码仍然存在,所以即使他们访问视图他们也无法对他们输入的密码做任何事情导致令牌仍然无效,必须与特定用户匹配。

另一个改进是找到一种方法来禁止视图,即使没有前端中间件实现。如果我找到一种方法,我可能会再次更新。

<强>实施

角色:

        .defineRole('isTokenValid', function(stateParams) {
            var token = stateParams.token;
            var deferred = $q.defer();

            authService.verifyToken(token)
                .then(function(res) {
                    if (res.success) {
                        deferred.resolve();
                    }
                }, function(res) {
                    if (res.error) {
                        deferred.reject();
                    }
                });

            return deferred.promise;
        });

状态:

            .state('reset-password', {
                url: '/reset/:token',
                data: {
                    permissions: {
                        only: ['isTokenValid'],
                        redirectTo: 'reset'
                    }
                },
                templateUrl: 'views/auth/reset-password.html',
                controller: 'ResetPasswordController as reset'
            })

希望它可以帮助任何有同样问题的人。