开发者

User/Login Module

开发者 https://www.devze.com 2023-04-03 09:19 出处:网络
I am trying to build a user-module that can be used in most of the web applications I am using zend-framework to build it and so far I have done following work:

I am trying to build a user-module that can be used in most of the web applications I am using zend-framework to build it and so far I have done following work:

User and Access Control Related Tables

user

user_id (int 11 auto increment PK)
username (varchar 50 unique)
password (varchar 25)
email (varchar 50)
created_on (datetime)
updated-on (datetime)
lastvisit (datetime)
status (boolean default 1) 

role

role_id (int 11 auto increment PK)
role_name (varchar 25 unique)
description (varchar 100) 

user_role

user_id (int 11 foreign key to user table)
role_id (int 11 foreign key to role table) 

user_reset

reset_id (int 11 auto increment PK)
user_email (varchar 50)
uniqid(varchar 20)
reset_flag (tinyint 1) 

Controllers and Actions

UserControllers

  • registerAction

user_status is set to zero

  • enableUserAction

enable user uri will be sent via email, user_status to one

  • forgotPasswordAction

  • loginAction

  • resetPasswordAction

  • editInfoAction

  • changePasswordAction

  • accountInfoAction

  • disableAccountAction

  • logoutAction

Models, Logic and DbInteraction Layers

Class UserTable()

Methods

  • save ($data)
  • update ($data, $condition, $value)
  • fetch ($fields, $condition, $value)
  • delete ($condition, $value)
  • getUserById($uid)
  • getUserByEmail($email)

Class User()

  • validation (most of the validation are done using zend library)
    • email validation
    • username validation (duplication, no. of characters)
    • password validation etc...

Methods:

  • user_emailNotification

@params toEmail String

{email settings, email templates, send mail}

@params username | emalilAddress String, String

returns Boolean

  • user_checkUniqueEmail

@params email String

returns Boolean

  • user_checkResetFlag

@params email String

returns Boolean

I am trying to make this module as reusable as possible so I would love to recieve any of ur suggestions regarding the class structure, database/table structure or anything regarding the module to make it more modular. So If you have any suggestions do post it.


I think your model is fine, the only thing I would change is your USerTable Class and split it into 2 separate classes :

  • A class that manages low-level interaction with the data persistence service (your DB, but it could be a webservice too)
  • a table similar to this one, which manages business rules for login, but which calls the low level class just mentioned (like a delegate) for real data access/persistence.

With this system, your UserTable doe not need to be rewritten if you change the way you store users data, and each class has only a single responsability.

But as far as I can guess, you store your passwords in plain text (25 chars are not enough for md5 nor sha*). You should really not do that : in your db, edit your password column to be at least 64 chars long.

Then use a strong hash with salt for storing passwords (like sha256, here is a sample) :

<?php
 define('SALT', 'myVery53cr3tpA55w0rd!');
 //I assume the password is already defined
 $pass =  hash('sha256',SALT.$pass);

This is the very minimum. You can perform multiple hash loops on the password to make them stronger, or use some frameworks for managing password security, like phpass.

that's a bit off-topic, but there are too many hacked websites'dumps with plaintext passwords out there

Hope that helps ! Benjamin

Edit : Here are some precisions for UserTable separation :

The main objective is to have a business class that contains the business model and operations, not matter what system is used for storing and fetching the data. This class will use a specific "storage" adapter for each data persistence service.

This class could look like this :

<?php
class UserManager
{
    private $adapter; 


public function __construct($adapter)
{
    $this->adapter = $adapter; 
}

public function save($user)
{
    if ($this->adapter->userExists()) {
        return false;
    }
    try {
        $this->adapter->save($user);
    } catch(Exception $e) {
        //Log or do whatever you want with the exception
        return false;
    }
    return true;
}

public function deleteUser($user)
{
    if(!$this->adapter->userExists()) {
        throw new Exception ("User : ".$user->getName()." does not exists");
    }
}

//add all required operations here (fetching all users, finding a user,...) 
}

This class performs all necessary operations for managing an user, but it needs an adapter that will be responsible for the real data manipulation, and will depend on the underlying data storage service. Let's create an interface first, for being able to manage multiple data storage services :

<?php
interface UserStorageAdapterInterface
{
public function save(User $user);
public function delete(User $user);
public function userExists(User $user);
/** TO be continued **/
}

This interface allows us to ensure that the adapter will contain the required methods for working with our UserManager, whenever we use a RDBMS, a NoSQL or a webservice for data storage.

Then, we can implement a specific class for our current RDBMS :

<?php
Class UserStorageAdapterPDO implements  UserStorageAdapterInterface
{
private $dbAdapter;
public function __construct($dbAdapter)
{
    $this->dbAdapter = $sbAdapter;
}

public function userExists(User $user)
{
    $sql = "SELECT COUNT * FROM user WHERE user_id = ".(int)$user->getId().";";
    $rs = $dbAdapter->fetchAll($sql);
    if($rs[0]!= 0) {
        return true;
    }
    return false;
}
}

We can now implement another class for a Soap webservice :

<?php
Class UserStorageAdapterSoap implements  UserStorageAdapterInterface
{
private $soapClient;
public function __construct($soapClient)
{
    $this->soapClient = $soapClient;
}

public function userExists(User $user)
{
    $rs = $this->soapClient->userExists($user);
    if($rs === 1) {
        return true;
    }
    return false;
}
}

This architecture adds a small level of complexity, but gives you several benefices :

  • Testability : you just need to mock your adapter in order to test your business rules on user management
  • Separation of concerns : With this architecture, you can update your storage adapter without worrying after your business model, that stays untouched
  • Extensibility : if one of your next apps uses a Nosql db (for instance), it will be easy to create a new storage adapter without extending nor rewriting your Manager class, which could lead to more errors are more code is rewritten.

IS that more clear for you ? If not, tell me :)

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号