hates the internet

Post thumbnail is file cabinets by waferboard

PHP Sessions in Red5.

The first stumbling block I came up against was how to handle authenticating users connecting to the server. My Red5 installation has access to the same database (and several parts of the application draw their data from it), so I have access to all the user and password information, but after the user’s initial login, why bother passing all that clag around when PHP already stores all the data I’d need in it’s session information. Also, why worry about long, cumbersome query strings when that data can all be stored in a session too?

PHP Setup

Getting PHP to store it’s session data somewhere else is completely trivial. Basically, here’s all it takes:

session_set_save_handler( '_open', '_close', '_read','_write',
    '_destroy', '_clean' );

More information on the PHP session_set_save_handler function can be found here, but basically this tells the PHP engine what functions to use to open, close, read, write, destroy and clean sessions (in that order). To save time, you can take a look at a sample MySQL session handler.

MySQL Setup

Now that we’ve taught PHP how to store it’s session data in a MySQL database, we need to create the table

in MySQL. The table needed is actually very simple:

CREATE TABLE php_sessions (
    id VARCHAR(32) NOT NULL,
    access INTEGER,
    data TEXT,
    PRIMARY KEY (id)
);

Believe it or not, that’s all it takes. Now, how do we actually git 'er done?

PHPSESSI-what?

To keep track of what session goes with what browser, PHP assigns each session a random string of characters that works as it’s session ID. This can either be passed on the query string or stored in a cookie. The setup I use in this example is for cookie based sessions.

The first thing we have to do is figure out our Session ID. This is done using JavaScript:

import flash.external.ExternalInterface;
import flash.net.NetConnection;

// Our Connection
var _con:NetConnection = new NetConnection();

// Our Session ID
var _SID:String = "";

// Make sure we have ExternalInterface
if(  !ExternalInterface.available ) {
    trace( "ExternalInterface not available!" );
} else {
    // Check for a valid session ID
    _SID = ExternalInterface.call( "getSessionId" );
    if( _SID == "" )
        trace( "PHP Session ID not available!" );
    else {
        _con.connect( "rtmp://red5.mydomain/myApp", _SID );
    }
}

Red5 Support Classes

Before diving any further, I had to write two support classes:

Database Manager Class
A simple class to handle all the database-connection muckey-muck. The class I wrote is a simple static class which doles out and closes database connections. It also keeps track of them and, every half hour, looks over a Map<> to expire connections that’ve been out longer than 30 minutes.
PHP Session Data Parser
All of the $_SESSION[] variables you make in PHP wind up getting serialized and put into the data column of the php_sessions table you created back in step 2. Basically, it’s a colon delimited list following this format:
var_name|{type}:{length}:{data}

If, for example, I create a session variable named username with the data jaypo@hatestheinternet.com:

$_SESSION['username'] = 'jaypo@hatestheinternet.com';

When I parse out my PHP session variables, username would look like this

username|s:26:"jaypo@hatestheinternet.com";

Now that we've figured out how to do that, let's move on to using it in our Red5 application.

The Nitty-Gritty

In my project, I perform all of the session checking data in the appConnect method of my application's ApplicationAdapter. The cheap and cheerful way:

public boolean appConnect( IConnection poCon, Object[] poParams ) {
// java.sql.Connection
Connection loCon = null;

// Return Value
boolean lbRet = true;

// Your deserializee class
PHPData loDat = null;

// Make sure that NetConnection.connect() sent an ID
if( poParams.length != 1 ) {
    rejectClient( "No session data passed!" );
    return false;
}

try {
    // Find session in the database
    loCon = Database.getConnection();
    Statement loStat = loCon.createStatement();
    String lsSQL = "SELECT data FROM php_sessions WHERE id = '"
                           + poParams[0] + "'";
    ResultSet loRS = loStat.executeQuery( lsSQL );

    // The session was not found
    if( !loRS>next() ) {
        rejectClient( "Session data not found" );
        lbRet = false;
    } else
        loDat = new PHPData( loRS.getString( "data" ) );

    if( lbRet && loDat.hasVariable( "username" ) ) {
        // Take various things and attach them to thise client
        loClient.setAttribute( "username", 
            loDat.getString( "username" )
        );
        // ... and whatever else you're storing
    } else if( lbRet ) {
        rejectClient( "You are not logged in" );
        return lbRet;
    }
} catch( Expression e ) {
    _log.error( "appConnection: Authentication exception", e );
    lbRet = false;
    rejectClient( "Error loading session data" );
} finally {
    Database.freeConnection( loCon );
}

return lbRet && super.appConnect( poCon, poParams );
}

After this mess executes, every time something happens in Red5, all I need is one line of code:

IClient loClient = Red5.getConnectionLocal().getClient();

... and I’ll be able to access the username as set by my PHP session using IClient.getAttribute().

Conclusion

This approach to authentication allows you to have a simple LAMP powered front end and not have to worry about unauthorized connections to your Red5 server. You can also use it to pass in other information, such as security level, email adivress, etc.

Note: As with my other "how-to"s, the sample code given in this article may or may not work, I wrote it off the top of my head purely for demonstrative purposes.

Post new comment

The content of this field is kept private and will not be shown publicly.