ThinkPHP(九)登陆:cookie和session、登陆流程、C层M层分离、密码加密及验证

一、cookie 与 session

在这里插入图片描述
我们也正是利用了cookie与session的唯一性,来实现了用户认证。基本的验证流程如下:在这里插入图片描述

二、静态方法

在前面的章节中,我们接触到了很多类似于这样的方法类名::方法名()。比如:我们使用Db::name()来获取某个数据表模型,使用Resquest::instance()来获取请求模型使用了::get()来获取单条数据模型,使用::select()来获取多条数据模型,使用::destroy来销毁数据。

ThinkPHP5大量的使用了这种可以直接使用::调用的方法,它们有一个很响亮的名字:静态方法。静态方法的引用,大幅提升了程序的运行效率,降低了资源的占用。在实际项目开发中,由于静态方法的优越性,我们还会大量使用。虽然本教程的前面也简单的对静态方法进行过讲解,但距离『理解』这个程度还不够。

争取通过本章的学习,能够让静态方法在我们心中留下更深的印象。

静态方法

为什么要有静态方法?

  1. 可以降低内存的占用。
  2. 提高程序的执行效率。

什么时候用静态方法?

  1. 当这个方法与具体的对象无关时。

其它略详见:https://www.kancloud.cn/diehl/thinkphp/930241

三、规划URL跳转

本节,我们对登录成功与失败进行URL的跳转规划。

登录 --> 提示登录成功 --> 跳转到 首页

登录 --> 提示登录失败 --> 跳转到 登录页

在软件工程中,我们往往会专门针对用户看到的各个界面做一个规划,这个图我们叫做“用例图”。在以后我们的实际项目中,我们将更多的在软件工程的规范下进行开发。

四、登录流程

1)功能分析

a) 和“添加”、“编辑”一样,“登录”也是两个action在共同起作用:

  1. 第一个action提供与用户交互的界面,
  2. 第二个action处理用户传入的数据。

b) 新建一个控制器LoginController,用于实现用户登录与注销的相关功能。
在这个类中,我们新建两个action:

  1. index:提供 【显示登录表单】功能
  2. login:提供【处理用户提交的登录数据】功能。

2)写基本功能

1.新建app\controller\LoginController.php

<?php
namespace app\index\controller;

class LoginController{
	//用于展示登录页登录表单
	public function index(){
		return 'index';
	}

	// 用于处理用户提交的登录数据
	public function login(){
		return 'login';
	}
}
测试基础代码是否正常:http://tp5/index.php/index/login/index

显示 index
http://tp5/index.php/index/login/login
显示 login

2.新建application\index\view\login\index.html

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
<!--action连接login函数-->
    <form action="{:url('login')}" method="post">
        <label for="username">username:</label><input type="text" name="username" id="username" />
        <label for="password">password:</label><input type="password" name="password" id="password" />
        <button type="submit">submit</button>
    </form>
</body>
</html>

3)确定流程

测试完action与对应的URL没有问题后,我们按流程来加入注释:

用户登录表单index-action::

// 显示登录表单

处理用户提交的登录数据login-action:

// 验证用户名是否存在
// 验证密码是否正确
// 用户名密码正确,将teacherId 存session
// 用户名密码错误,跳转到登录界面 

4)index-action

<?php
namespace app\index\controller; 
use think\Controller; /1】必须引入controller类,否则报错找不到类
/2】继承自tp的controller类
class LoginController extends Controller
{
	//用于展示登录页登录表单
	public function index(){
		/3】显示v层登录表单
		return $this->fetch();
	}

	// 用于处理用户提交的登录数据
	public function login(){
		return 'login';
	}
}
效果:http://tp5/index.php/index/login/index

在这里插入图片描述

5)login-action

5.1插入password字段:类型varchar 长40 不为空

效果
在这里插入图片描述

5.2增加测试密码

直接双击增加
在这里插入图片描述
input()助手函数用法:
https://blog.csdn.net/xinyflove/article/details/89486213

5.3测试代码是否有问题

// 用于处理用户提交的登录数据
	public function login(){
		var_dump(input()); //同 input('post.')
		// return 'login';
	}
效果 http://tp5/index.php/index/login/index

提交后:

array(2) {
  ["username"]=>
  string(4) "lisi"
  ["password"]=>
  string(3) "123"
}

5.4C层逻辑

// 处理用户提交的登录数据
public function login()
{
    // 1.验证用户名是否存在
    // 2.验证密码是否正确
    // 3.用户名密码正确,将teacherId存session。
    // 4.用户名密码错误,跳转到登录界面。
}

5.5 实现

<?php
namespace app\index\controller; 
use think\Controller; //必须引入controller类
use think\Request; //获取表单数据
use app\common\model\Teacher; //使用teacher模型

class LoginController extends Controller
{
	//用于展示登录页登录表单
	public function index(){
		//显示v层登录表单
		return $this->fetch();
	}

	// 用于处理用户提交的登录数据
	public function login(){
		//var_dump(input());
		// return 'login';
		// 接收post数据
		$postData=Request::instance()->post();
		// 验证用户名是否存在
		$map=['username'=>$postData['username']];
		$Teacher=Teacher::get($map);

        // 验证密码是否正确
        if(!is_null($Teacher)){
        	// 用户名密码错误,跳转到登录界面
        	if($Teacher->getData('password')!==$postData['password']){
        		return $this->error('密码错误',url('index'));
        	}else{
        		// 用户名密码正确,将teacherId存session
        		session('teacher_id',$Teacher->getData('id'));
        		return $this->success('登录成功',url('teacher/index'));
        	}
        
        }else{
        	return $this->error('用户名不存在',url('index'));
        }
	}

}
知识点

由于$this->error()本身就是在抛出异常,所以以后我们在C层中的代码,将所有需要抛出异常的代码都统一写为$this->error()。异常抛出后,交给ThinkPHP为我们进行处理。

这也意味着:
我们以后在C层将不再有类似throw new \Exception这样直接抛出异常的代码。

效果:http://tp5/index.php/index/login/index.html

输入正确密码:登录成功,跳转到teacher表格列表页
输入错误密码:密码错误,回登录页
输入错误名:用户名不存在,回登录页

5.6简化重构

//login函数简化重构
	public function login(){
		//接收登录传来数据
		$postData=Request::instance()->post();
		//查询对应的用户
		$map=['username'=>$postData['password']]; //username对应的密码数组
		$Teacher=Teacher::get($map); //查询对应用户的密码返回1个对象

		//判断用户对象是否存在,且密码等于表单传过来的数据
		if (!is_null($Teacher) && $Teacher->getData('password')===$postData['password'] ) {
			//把id保存到session中
			session('teacher_id',$Teacher->getData('id'));
			//如果密码正确返回正确信息且登录到teacher/index
			return $this->success('登录成功',url('teacher/index'));		
		}else{
			//如果密码错误则返回错误信息
			return $this->error('用户名或密码不正确',url('index'));
		}
	}
说明

!is_null($Teacher) && ($Teacher->getData('password') === $postData['password'])这行代码而言,我们是不需要担心,如果$Teacher为null的话,执行到$Teacher->getData()会报不能在一个bool类型的变量上调用method的错误的。因为如果$Teacher的值是null,那么当执行到!is_null($Teacher), 系统已经判断条件不被满足,当然也就不会执行**&&**后面的语句了。

再举个简单的例子:

$a = 1;
$b = 2;
if ($a !== 1 && $b++)
{
    echo 'hello';
}
echo $b; // 值没有发生变化? 没有变化,还是2。

五、把登录逻辑改到M层

C层是个指挥官,它并不负责代码的具体实现。它的作用就是:指挥!然后,查看指挥结果,进而做出下一步的指挥。
因此要把登录逻辑改到M层去

1.c层登录代码重构

application\index\controller\loginController.php

//把登录逻辑放到M层
	public function login(){
		$postData=Request::instance()->post();

		if(Teacher::login($postData['username'],$postData['password'])){
			return $this->success('登录成功',url('teacher/index'));
		}else{
			return $this->error('登录失败',url('index'));
		}
	}

2.M层登录及判断逻辑编写

application\common\model\teacher.php

<?php
// 简单的原理重复记: namespace说明了该文件位于application\common\model 文件夹
namespace app\common\model;
use think\Model;    //  导入think\Model类

/**
 * Teacher 教师表
 */ 
/*我的类名叫做Teacher,对应的文件名为Teacher.php.
该类继承了Model类,Model我们在文件头中,提前使用use进行了导入*/
class Teacher extends Model
{
	/**
	 * @param string $username 用户名
	 * @param string $password 密码
	 * @return bool 成功true,失败false
	 */
	static public function login($username,$password){
		// 查询用户名将其对象赋值给$Teacher
		$map=['username'=>$username];
		$Teacher=self::get($map);

		//如果上步返回对象不为空,证明用户存在继续:
		if(!is_null($Teacher)){
			//查询对应用户的密码是否正确,调用checkpassword()
			if($Teacher->checkPassword($password)){
			//正确则把用户id存入session
				session('teacherId',$Teacher->getData('id'));
				//返回true,证明登录成功
				return true;
			}			
		}
		return false;	//其它情况登录失败
	}


	/**
     * 验证密码是否正确
     * @param  string $password 密码
     * @return bool           
     */
    public function checkPassword($password)
    {	//查询对应用户密码,是否等于传过来的密码
        if($this->getData('password')===$password){
        	return true;
        }else{
        	return false;
        }
    }

}

六、密码加密及验证

1.加密及验证

application\common\model\teacher.php

/**
     * 验证密码是否正确
     * @param  string $password 密码
     * @return bool           
     */
    public function checkPassword($password)
    {
    	//【1】数据库密码 === 前端提交密码,加密后
        if($this->getData('password') === $this::encryptPassword($password)){
        	return true;
        }else{
        	return false;
        }
    }


    /**
     * 【2】加密算法
     * @param  string 待加密字串
     * @return string 加密后字串
     */  
    static public function encryptPassword($password)
    {
    	// 实际的过程中,我还还可以借助其它字符串算法,来实现不同的加密
    	//md5加密后+pasa 再sha1加密
    	return sha1(md5($password).'pas');
    }

2.生成默认密码加密码

application\index\controller\loginController.php

	//生成测试密码123 的加密码,并加入数据库
	public function test(){
		echo Teacher::encryptPassword('123');

		// $map=['username'=>'zhangsan']; //生成关联数组
		// $Teacher=Teacher::get($map); //获取对应zhangsan对象
		// return $Teacher->getData('password'); //返回zhangsan密码
	}

测试登录:http://tp5/index.php/index/login/index.html

zhangsan 123 提示登录成功

七、订制异常

1.尝试把数组传入加密函数

<?php
namespace app\index\controller; 
use think\Controller; //必须引入controller类
use think\Request;
use app\common\model\Teacher;

class LoginController extends Controller
{
...
//生成测试密码
	public function test(){
		echo Teacher::encryptPassword(['123']);

		// $map=['username'=>'zhangsan']; //生成关联数组
		// $Teacher=Teacher::get($map); //获取对应zhangsan对象
		// return $Teacher->getData('password'); //返回zhangsan密码
	}

报错:http://tp5/index.php/index/login/test

在这里插入图片描述

2.订制异常

<?php
// 简单的原理重复记: namespace说明了该文件位于application\common\model 文件夹
namespace app\common\model;
use think\Model;   //  导入think\Model类

class Teacher extends Model
{

...
    /**
     * 加密算法
     * @param  string 待加密字串
     * @return string 加密后字串
     */  
    static public function encryptPassword($password)
    {
    //【1】如果传入的不是字符串,则抛出异常
    	if(!is_string($password))
    	{
    		throw new \RuntimeException("传入的不是字符串,请检查后重试", 2);  		
    	}
    	// 实际的过程中,我还还可以借助其它字符串算法,来实现不同的加密
    	//md5加密后+pasa 再sha1加密
    	return sha1(md5($password).'pas');
    }

订制的异常效果

1
手动的抛出了一个RuntimeException异常来替代Exception异常,这样做的目的是细化异常类型。后面还抛出了一个状态码,如果在加上一个我们后期应该完善的错误码说明表,那么可以快速的定位错误类型。比如,微信的官方文档中,就专门有对返回码说明的章节:
https://mp.weixin.qq.com/wiki/17/fa4e1434e57290788bde25603fa2fcbd.html
在实际的开发过程中,我们需要定制系统的返回码,完善错误码说明表。按不同的异常类型进行异常的抛出。每个方法中,都在使用异常来处理一些非正常的输入,来保证程序的健壮性。
在处理异常的路上,我们才刚刚起步。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值