AngularJS 和 PHP 应用程序中的会话

Posted

技术标签:

【中文标题】AngularJS 和 PHP 应用程序中的会话【英文标题】:Sessions in AngularJS and PHP application 【发布时间】:2017-12-19 21:23:24 【问题描述】:

我有一个 AngularJS 应用程序,我正在更新它以使用 php 7。目前我有一个用于会话的自定义会话处理程序设置:

自定义会话处理程序(session.php)

function sess_open( $path, $name ) 
    return true;


function sess_close( ) 
    $sessionId = session_id(); 
    return true;


function sess_read( $id ) 
    $db = dbConn::getConnection();

    $stmt = "SELECT session_data FROM session where session_id =" . $db->quote($id);
    $result = $db->query($stmt);
    $data = $result->fetchColumn();
    $result->closeCursor();
    return $data;


function sess_write( $id, $data ) 
    $db = dbConn::getConnection();

    $tstData = sess_read( $id );
    if (!is_null($tstData)) 
        // if it does then do an update
        $stmt = "UPDATE session SET session_data =" . $db->quote($data) . " WHERE session_id=" . $db->quote($id);
        $db->query($stmt);
    
    else 
        // else do an insert
        $stmt = "INSERT INTO session (session_id, session_data) SELECT ". $db->quote($id) . ", ". $db->quote($data) . " WHERE NOT EXISTS (SELECT 1 FROM session WHERE session_id=" . $db->quote($id) . ")";
        $db->query($stmt);
    

    return true;


function sess_destroy( $id ) 

    $db = dbConn::getConnection();

    $stmt = "DELETE FROM session WHERE session_id =" . $db->quote($id);
    setcookie(session_name(), "", time() - 3600);
    return $db->query($stmt);


function sess_gc( $lifetime ) 

    $db = dbConn::getConnection();

    $stmt = "DELETE FROM session WHERE timestamp < NOW() - INTERVAL '" . $lifetime . " second'";
    return $db->query($stmt);


session_name('PROJECT_CUPSAW_WEB_APP');
session_set_save_handler("sess_open", "sess_close", "sess_read", "sess_write", "sess_destroy", "sess_gc");
session_start();
ob_flush();

在我的app.js 中,我不断检查用户是否已通过身份验证并可以访问应用程序。

App.js

/*
 * Continuous check for authenticated permission to access application and route
 */
app.run(function($rootScope, $state, authenticationService, ngToast) 
    $rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams) 
        authenticationService.isAuthenticated()
            .success(function () 
                if(toState.permissions) 
                    ngToast.dismiss();
                    event.preventDefault();
                    $state.go("logout"); // NEEDS TO CHANGE - Unauthorized access view
                    return;
                
            )
            .error(function () 
                ngToast.dismiss();
                event.preventDefault();
                localStorage.clear();
                $state.go("authentication"); // User is not authenticated; return to login view
                return;
            );
        ngToast.dismiss();
    );
);

在上面的代码中,isAuthenticated 运行 isUserAuthorized.php

经过身份验证

/*
 * Check if user is authenticated; set role/permissions
 */
this.isAuthenticated = function() 
    return $http.post(baseUrl + '/isUserAuthorized.php');
;

isUserAuthorized.php

<?php

require_once 'session.php';
// Check to ensure user is authenticated to initiate request
if (array_key_exists('authenticated', $_SESSION) && $_SESSION['authenticated']) 
    return http_response_code(200);
 else 
    // Clear out all cookies and destroy session
    if( array_key_exists('HTTP_COOKIE', $_SERVER))
      $cookies = explode(';', $_SERVER['HTTP_COOKIE']);
      foreach($cookies as $cookie) 
          $parts = explode('=', $cookie);
          $name = trim($parts[0]);
          setcookie($name, '', time()-1000);
          setcookie($name, '', time()-1000, '/');
      
    
    session_destroy();

    return http_response_code(401);

会话应在需要session.php 时启动。不过,这似乎并没有发生。访问应用程序时,会显示登录页面,但isUserAuthorized.php 正在抛出警告:

警告:session_start():无法读取会话数据:session.php 中的用户(路径:/var/lib/php/mod_php/session)

当我选择Login 按钮时,login.php 被调用,但用户会直接进入应用程序,尽管凭据不正确。

login.php

<?php

require_once '../database.php';
require_once 'session.php';
require_once 'ldap.php';

$_SESSION['authenticated'] = false;

//$conn = connect_db();

try 

    $data = json_decode(file_get_contents('php://input'));

    $username = strtolower($data->username);
    $password = $data->password;

    // Check domain credentials; return user token if verified
    if(ldap_authenticate($username, $password)) 
        $_SESSION['authenticated'] = true;
    
    else 
        echo('Invalid username and/or password!');
        return http_response_code(400);
    


catch(PDOException $e) 
    return http_response_code(400);

我不完全确定是什么导致了这种奇怪的行为,以及为什么没有创建会话。我需要显式调用sess_write 函数吗?


更新

我发现从login.php 中删除require_once 'session.php' 会导致正确的行为。用户可以在提供有效凭据时登录。但是,会话数据仍然从未被写入数据库。知道为什么吗?

【问题讨论】:

您的会话处理程序有很多问题。从 Github 和 phpclasses 可以免费下载许多更好的示例。这可能是一个很好的起点。如果您仍然有问题,请开始单独调试会话处理程序。 @symcbean 您能否更具体地说明这里出了什么问题,或者提供一个示例? @SaraTibbetts ob_flush() 在会话处理程序中做了什么? @Snoozer 会发送输出缓冲区 【参考方案1】:

问题归结为我的会话处理程序。从 PHP 7 开始,sess_read 函数必须返回一个字符串。这导致了警告:

警告:session_start():无法读取会话数据:session.php 中的用户(路径:/var/lib/php/mod_php/session)

$datanull 时,我通过返回'' 解决了这个问题。

这导致我的sess_write 函数无法知道何时插入和何时更新。我通过更改 SQL 解决了这个问题。

最终我将会话处理程序变成了一个类,如最终结果所示:

<?php

require_once ('../database.php');

class CustomSessionHandler implements SessionHandlerInterface
    public function open( $path, $name ) 
        return true;
    

    public function close( ) 
        return true;
    

    public function read( $id ) 
        $db = dbConn::getConnection();

        $stmt = "SELECT session_data FROM session where session_id =" . $db->quote($id);
        $result = $db->query($stmt);
        $data = $result->fetchColumn();
        $result->closeCursor();
        if(!$data)
                return '';
        
        return $data;
    

    public function write( $id, $data ) 
        $db = dbConn::getConnection();

        //Works with Postgres >= 9.5
        //$stmt = "INSERT INTO session (session_id, session_data) VALUES (" . $db->quote($id) . ", " . $db->quote($data) . ") ON CONFLICT (session_id) DO UPDATE SET session_data=" . $db->quote($data) . ";";

        //Works with Postgres < 9.5
        $stmt = "UPDATE session SET session_data=" . $db->quote($data) . " WHERE session_id=" . $db->quote($id) . ";";
        $db->query($stmt);

        $stmt = "INSERT INTO session (session_id, session_data) SELECT ". $db->quote($id) . ", ". $db->quote($data) . " WHERE NOT EXISTS (SELECT 1 FROM session WHERE session_id=" . $db->quote($id) . ");";
        $db->query($stmt);

        return true;
    

    public function destroy( $id ) 
        $db = dbConn::getConnection();

        $stmt = "DELETE FROM session WHERE session_id =" . $db->quote($id);
        setcookie(session_name(), "", time() - 3600);
        $data = $db->query($stmt);

        return true;
    

    public function gc( $lifetime ) 
        $db = dbConn::getConnection();

        $stmt = "DELETE FROM session WHERE timestamp < NOW() - INTERVAL '" . $lifetime . " second'";
        $data = $db->query($stmt);
        return true;
    


session_name('PROJECT_CUPSAW_WEB_APP');
$handler = new CustomSessionHandler();
session_set_save_handler($handler, false);
session_start();
ob_flush();

【讨论】:

以上是关于AngularJS 和 PHP 应用程序中的会话的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 5:会话没有按应有的方式工作

基于PHP Memcache(d)的会话:是否应禁用垃圾收集?

使用 Angularjs 和 Laravel 注销会话过期

使用 AngularJS 进行快速会话

angularjs + PHP应用程序中的CORS错误

如何在 Angular JS 应用程序中处理身份验证