Tải bản đầy đủ (.pdf) (10 trang)

Thiết kế mạng xã hội với PHP - 9 pps

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (5.47 MB, 10 trang )

Users, Registration, and Authentication
[ 62 ]
Privacy policies
When users sign up to any website, they generally agree to the terms and conditions
of the website, and the privacy policy. While the terms and conditions generally set
out information about liability, conduct on the site, and so on, the privacy policy
explains what will be done with the users' data.
It is important to be clear and honest with users about their data, and reassuring
about the security of their data. Facebook has had a lot of bad press recently relating
to its privacy policy and the tools available to their users to protect their data. In
particular, one of their recent changes resulted in a document that was over 5,800
words long—something that most users won't read or understand (
http://www.
huffingtonpost.com/2010/05/12/facebook-privacy-policy-s_n_574389.
html
). When stating your privacy policies:
• Be clear and concise
• Make it clear who can access the data they add to the site:
° Are all proles public?
° How much information is available to what type of user?
° How can the information be restricted?
• Explain who owns the data—does the user retain ownership or do they grant
a licence of use to us?
It is also important for us to think about how we might allow users to change their
own privacy settings, including which prole information they would like to make
public, public only to their network, or completely private—particularly with
regards to contact details and dates of birth.
Some countries also have legislation in place governing the management of user
data, such as the Data Protection Act in the UK. This covers issues such as:
• Security—ensuring data is held securely, and isn't easy for others to access,
unless the user's permission has been given


• Relevancy—ensuring data held is kept up to date and is relevant
• Removal—allowing users to request full removal of their data
• Access—allowing users to request copies of all data held about them
Download from Wow! eBook <www.wowebook.com>
Chapter 3
[ 63 ]
Users
At their core, users can be represented by a few simple pieces of information:
• A unique identier such as a user ID
• A unique identier that the user themselves can easily remember, such as
their chosen username or their e-mail address
• A password, which is used to authenticate the user—to prove they are who
they say they are
As far as our authentication system is concerned, this will be a user. We will of
course extend this with a user prole, but in terms of authentication, this is all the
information we need.
Our user object
Our user object is created when a user tries to log in, either based on submitting
a login form supplying their username and password, or based on session data
for the user ID.
If username and password are supplied, then it checks the credentials and populates
its variables if such a user exists. If only an ID is supplied, then it populates based on
whether there is a user of that ID. Since the authentication class controls whether the
current user is logged in or not, we can use this object to view or perform actions on
other users if we wished, as by separating the two we won't be automatically logged
in as the user populated within this object. As a result, we can extend this object to
reset the user's password, edit the user, deactivate the user, and so on.
The constructor takes four arguments, the registry (dependency injection, so it
can communicate with the rest of the framework), a user ID, a username, and a
password, the latter three being optional, and used as described above.

public function __construct( Registry $registry, $id=0,
$username='', $password='' )
{
$this->registry = $registry;
If we haven't set a user ID (that is, $id is 0) and we have set a username and a
password, we should look up the user to see whether these are valid credentials:
if( $id=0 && $username != '' && $password != '' )
{
$user = $this->registry->getObject('db')-
>sanitizeData( $username );
Download from Wow! eBook <www.wowebook.com>
Users, Registration, and Authentication
[ 64 ]
As our passwords are hashed in the database, we need to hash the password we
were supplied. We can hash the password directly in the query (by using the MySQL
function MD5), however, this exposes the password in plain text more than required,
as it would be processed and accessed by both PHP and the MySQL server
(which may be stored on a remote machine):
$hash = md5( $password );
$sql = "SELECT * FROM users WHERE username='{$user}' AND
password_hash='{$hash}' AND deleted=0";
$this->registry->getObject('db')->executeQuery( $sql );
if( $this->registry->getObject('db')->numRows() == 1 )
{
We have a record in the database, so the user is valid, so we set the various
properties of our user object:
$data = $this->registry->getObject('db')->getRows();
$this->id = $data['ID'];
$this->username = $data['username'];
$this->active = $data['active'];

$this->banned = $data['banned'];
$this->admin = $data['admin'];
$this->email = $data['email'];
$this->pwd_reset_key = $data['pwd_reset_key'];
$this->valid = true;
}
}
elseif( $id > 0 )
{
If we supplied a user ID, then we look up the user with that ID and populate the
object with their details. As discussed above, we don't want to set them as logged-in
here, because we may use this object to edit, delete, and create users, and integrating
authentication would log out the administrator and log them in as someone else if
they tried to edit an existing user.
$id = intval( $id );
$sql = "SELECT * FROM users WHERE ID='{$id}' AND deleted=0";
$this->registry->getObject('db')->executeQuery( $sql );
if( $this->registry->getObject('db')->numRows() == 1 )
{
$data = $this->registry->getObject('db')->getRows();
$this->id = $data['ID'];
$this->username = $data['username'];
$this->active = $data['active'];
$this->banned = $data['banned'];
Download from Wow! eBook <www.wowebook.com>
Chapter 3
[ 65 ]
$this->admin = $data['admin'];
$this->email = $data['email'];
$this->pwd_reset_key = $data['pwd_reset_key'];

$this->valid = true;
}
}
}
Our authentication registry object
One of the rst things our framework needs to do, once it is connected to the
database, and some core settings are loaded, is to check whether the current user
is logged in. This is simply done by checking for an active session, and if one exists,
building the user object from that, or checking to see if a username and password
have been supplied, and building the user from that.
This will make up part of our authentication object (
registry/authentication.
class.php
), which will reside in our registry and interact with the user object.
The
checkForAuthentication method checks both for an active session and user
credentials being passed in POST data, and calls additional methods to build the
user object if appropriate.
public function checkForAuthentication()
{
Initially, we remove any error template tags on the page (which we would use to
inform the user of an invalid login):
$this->registry->getObject('template')->getPage()-
>addTag('error', '');

if( isset( $_SESSION['sn_auth_session_uid'] ) && intval( $_
SESSION['sn_auth_session_uid'] ) > 0 )
{
If session data is set, we call the sessionAuthenticate method:
$this->sessionAuthenticate( intval( $_SESSION['sn_auth_

session_uid'] ) );

Download from Wow! eBook <www.wowebook.com>
Users, Registration, and Authentication
[ 66 ]
The sessionAuthenticate method then sets the loggedIn property to indicate
whether the user is logged in or not:
if( $this->loggedIn == true )
{
$this->registry->getObject('template')->getPage()-
>addTag('error', '');

}
else
{
If the user is not logged in, and we have a valid session, then something went wrong
somewhere, so we should inform the user their login attempt was not successful:
$this->registry->getObject('template')->getPage()-
>addTag('error', '<p><strong>Error: Your username or
password was not correct,
please try again</p><strong>');
}
}
If session data was not set, we check for post data, and call the postAuthenticate
method if appropriate, following the same steps as above.
elseif( isset( $_POST['sn_auth_user'] ) &&
$_POST['sn_auth_user'] != '' && isset(
$_POST['sn_auth_pass'] ) && $_POST['sn_auth_pass'] != '')
{
$this->postAuthenticate( $_POST['sn_auth_user'] , $_

POST['sn_auth_pass'] );
if( $this->loggedIn == true )
{
$this->registry->getObject('template')->getPage()-
>addTag('error', '');

}
else
{
$this->registry->getObject('template')->getPage()-
>addTag('error', '<p><strong>Error: Your username or
password was not correct,
please try again</p><strong>');
}
}
elseif( isset( $_POST['login']) )
{
Download from Wow! eBook <www.wowebook.com>
Chapter 3
[ 67 ]
If the login post variable has been set, but neither session data or POST login data
has been submitted, then the user didn't enter a username or a password, so we
should tell them this:
$this->registry->getObject('template')->getPage()-
>addTag('error', '<p><strong>Error:
Your must enter a username and a password</p><strong>');
}
}
This method also sets suitable template tag variables for standard errors if there was
a problem authenticating the user.

POST authentication
In the code above, if the user has tried to log in by submitting a login form, the
postAuthenticate method is called. This method is shown below. It utilizes the
user object to query the database, if the user exists and is logged in, then it sets the
appropriate session data, as highlighted below:
private function postAuthenticate( $u, $p )
{
$this->justProcessed = true;
require_once(FRAMEWORK_PATH.'registry/user.class.php');
$this->user = new User( $this->registry, 0, $u, $p );

if( $this->user->isValid() )
{
if( $this->user->isActive() == false )
{
$this->loggedIn = false;
$this->loginFailureReason = 'inactive';
}
elseif( $this->user->isBanned() == true )
{
$this->loggedIn = false;
$this->loginFailureReason = 'banned';
}
else
{
$this->loggedIn = true;
$_SESSION['sn_auth_session_uid'] = $this->user-
>getUserID();
}
Download from Wow! eBook <www.wowebook.com>

Users, Registration, and Authentication
[ 68 ]

}
else
{
$this->loggedIn = false;
$this->loginFailureReason = 'invalidcredentials';
}
}
SESSION authentication
If the user hasn't tried to log in by submitting a form, but has some session data set,
we try and authenticate them based on the session data:
private function sessionAuthenticate( $uid )
{
require_once(FRAMEWORK_PATH.'registry/user.class.php');
$this->user = new User( $this->registry, intval( $_SESSION['sn_
auth_session_uid'] ), '', '' );

if( $this->user->isValid() )
{
if( $this->user->isActive() == false )
{
$this->loggedIn = false;
$this->loginFailureReason = 'inactive';
}
elseif( $this->user->isBanned() == true )
{
$this->loggedIn = false;
$this->loginFailureReason = 'banned';

}
else
{
$this->loggedIn = true;
}

}
else
{
$this->loggedIn = false;
$this->loginFailureReason = 'nouser';
}
if( $this->loggedIn == false )
{
$this->logout();
}

}
Download from Wow! eBook <www.wowebook.com>
Chapter 3
[ 69 ]
Salt your passwords!
Our passwords are stored in the database as an MD5 one-way hash.
This means we don't keep a copy of the user's password; instead, we
hash the password when they try to log in, and compare the hash to
the password in the database. If our database was compromised, our
users' passwords should be safe. This hashing cannot be reversed, but
there are dictionaries available for common words or phrases, which
means it is possible to work out some passwords from the hashes. We
can prevent this further by salting the password; this involves adding

a "salt" to the password and then hashing it. This is typically done by
creating a random string for each user and storing it in their row in the
users table. Passwords in the Dino Space code are currently not salted,
to make it easier should you wish to change how the passwords are
hashed, or integrate with other login systems.
Structuring the database
For our users table (without social prole data), we need the following elds:
Field Type Description
ID Integer, Primary
Key, Auto-increment
The unique user ID
Username Varchar The username
Password_hash Varchar The MD5 hash of the user's password
Password_salt Varchar(5) If we decide to salt our passwords
Email Varchar The user's e-mail address
Active Bool Denes whether the user account is active or not
Admin Bool Denes whether the user account is an
administrator or not
Banner Bool Denes whether the user account has been
banned
reset_key Varchar Random string used for resetting the password
when the user forgets it
Reset_expires Timestamp Time at which that reset string expires—
preventing someone spamming a user by
constantly requesting a new key
Download from Wow! eBook <www.wowebook.com>
Users, Registration, and Authentication
[ 70 ]
Registration
We currently have two primary database tables for our users. A users table,

containing the core user data, and a users_profile table, containing other
(non-essential) information.
Standard details
Our core registration elds are dened in our registration controller; they
are stored as array pairs, referencing the eld name with a more descriptive
name (the more descriptive name is used for error messages).
/**
* Standard registration fields
*/
private $fields = array( 'user' => 'username', 'password' =>
'password', 'password_confirm' => 'password confirmation',
'email' => 'email address');

/**
* Any errors in the registration
*/
private $registrationErrors = array();

/**
* Array of error label classes - allows us to make a field a
different color, to indicate there were errors
*/
private $registrationErrorLabels = array();

/**
* The values the user has submitted when registering
*/
private $submittedValues = array();

/**

* The santized versions of the values the user has submitted -
these are database ready
*/
private $sanitizedValues = array();

/**
* Should our users automatically be "active" or should they
require email verification?
*/
Download from Wow! eBook <www.wowebook.com>
Chapter 3
[ 71 ]
private $activeValue = 1;
private function checkRegistration()
{
We set an allClear variable, to indicate that the values submitted are all acceptable.
Each time an error is encountered, this is set to false, so that we can report the error
back to the user:
$allClear = true;
The rst stage is to check whether the user has actually submitted all of the required
elds, if any of them are blank, then we ag these errors to the user.
// blank fields
foreach( $this->fields as $field => $name )
{
if( ! isset( $_POST[ 'register_' . $field ] ) ||
$_POST[ 'register_' . $field ] == '' )
{
If any are blank, our allClear variable is set to false, and we generate error strings,
and store them in our errors array:
$allClear = false;

$this->registrationErrors[] = 'You must enter a ' . $name;
$this->registrationErrorLabels['register_' . $field . '_
label'] = 'error';
}
}
Next, we can check the values in more detail. Let's start with the password!
We will want the password to be at least seven characters, to help ensure it is secure.
To prevent issues of a user not knowing their password because they entered it
incorrectly, we ask the user to verify their password, so we must also check the
password and its verication match:
// passwords match
if( $_POST[ 'register_password' ]!= $_POST[ 'register_password_
confirm' ] )
{
$allClear = false;
$this->registrationErrors[] = 'You must confirm your
password';
$this->registrationErrorLabels['register_password_label'] =
'error';
$this->registrationErrorLabels['register_password_confirm_
label'] = 'error';
Download from Wow! eBook <www.wowebook.com>

×