0. 功能分析
我们知道用户它一般会包含两大类, 第一大类是后端管理的用户,?? 第二大类是前端的用户, 后端的用户怎么去理解? 后端用户,它其实就是我们后台系统??涉及到的一些用户,比如说管理员或者员工等等这些用户,??
前端用户主要是指面向于我们电商的用户,比如说??基于互联网用户的这些用户,比如去注册我们的电商,登录到我们的电商,我们把这些用户称之为前端用户,??所以我们需要对这两类用户进行相应的管理。?
?我们的项目会基于??后端登录以及前端用户登录的场景来做它的详细的分析,大体分为后端登录和前端登录,后端登录首先我们需要去设计后端用户的表,因为他要登录,??我们涉及到比如说它的用户名是多少,密码是多少,它的登录时间??等等这些表结构,我们需要去设置表结构,
我们需要把后端的 html 代码??引入到我们项目当中去,这就是模板引入。? ?关于后端的页面,我们是用到了 layui 的前端框架。?
?然后我们在登录的时候会加入一个验证码的场景,所以说这个时候需要用到验证码机制,?? 最后我们需要进行登录工作的编写,编写它的业务代码,
前端登录,首先同样我们也需要去设计用户表,然后我们这个地方是采用了前后端分离,简单是用 VUE ,??后端是写 API,也就是说我们登录 API,然后前端其实叫我们 API 的场景就可以了。?
?关于前端登录,?? 我们这个页面大家看看这个是我们电商的页面,??我们是用到了手机号加短信验证码的方式进行相应的登录,这样的话在这个地方其实我们需要有两个功能,??发送验证码这个过程是需要有一个API ,还有一个登录,??也就是说当我点击发送验证码,然后我手机收到了验证码,我把数据输入进去,这个时候再点登录,??所以它是两个功能,第一获取验证码,第二登录,所以有登录验证码,还有一个登录逻辑的开发。?
打开工具, 链接:【画图工具,非广告营销】 https://app.diagrams.net/
对于我们的后端服务登录的一些场景,我画了一个图,后端服务:
首先是登录页面,??然后我们登录页面它是通过 ajax(念阿贾克斯) 的形式去请求 后端的API,后端的 API 需要对我们的参数进行校验,??因为后端登录它可能会传递比如说用户名密码以及验证码,所以说我们需要进行相应的校验,??然后再根据用户名去数据库里面去获取用户的基本数据,看一下我们用户是否存在,??如果不存在那直接就提示不存在这个用户。?
?如果存在这个用户,我们看一下用户的密码,??和我们当前传递过的密码进行相应的对比。当然了这个密码传递肯定是进行加密的,然后进行相应的这么一个场景分析,??看一下是否正确。如果正确那说明可以登录,那么我就提示登陆成功。?
?在这个过程我们需要对我们的表进行一些字段内容的更新,比如说最后登录时间,??登录 IP 等等需要做一个简单的更新,然后我们会把 session存储到比如说文件里面,??后续我们会用到分布式的 session,然后最后进行页面跳转就可以了。?
?因为你登录了,你肯定让它跳转到 比如说后端的首页,是这么一个逻辑。? 这是后端的登录流程。
?再来看一下我们的前端的登录流程,如图:
在这个地方我们的用户他首先会去获取验证码,??点击获取 验证码,它会请求我们的 API ,API 我们会去请求第三方发送短信的??平台,众所周知,因为发送短信我们自身肯定是发送不了的,我们也是借助于第三方的平台,??比如说阿里云短信服务去发送相应的短信,??不管是阿里云还是腾讯云短信服务,??它都是借助于联通、电信、中国移动等等他们这些服务去发送短信的,??比如说我发一个联通的手机号到阿里云短信服务里面去,这个时候阿里云底层它会去直接对接联通,??然后把短信发送过去,是这么一个逻辑。?
小伙伴可能就有疑问了,比如说我直接对接联通,没有对接电信,没有对接中国移动,我发送一个??联通的到联通手机它能识别,如果我发送一个移动到联通,它能不能发送??? 可以的,在他们服务里面他都会去做一个中转,我们只需要了解就可以了。?? 比如说你联通的发到电信里面去,那么电信它内部它会中转到联通的这么一个服务,是这么一个思想和逻辑。?
获取验证码的流程中,??这个 API 里面它其实会做一些简单的场景,比如说我会先生成一个 5位数字的验证码,??然后把它发送到比如说对接到我们的阿里云短信或者腾讯云短信等等, 然后发送成功之后,这个时候的话相当于我会把它??数字记录到 redis 缓存里面去,可以给一个失效时间,比如说一分钟等等。?
?然后这样的话比如说我们的阿里云短信服务把短信发到了我们的手机上,这个时候我们收到短信验证码之后,??我们就会针对验证码进行相应的输入,输入完之后再点击登录,点击登录它会对接我们的登录 API,??那么我们登录API的场景其实就是比如说根据手机号码去拿到这个验证码,然后和我们当前传递过的验证码进行对比,??如果正确我们认为登录成功,否则登录失败。?
?那么输入验证码这种场这条流是指??我们还没登录的场景,如果我们已经登录了,所以说这条流就不会去走。?
1. 后端页面中部署到项目服务器中
?部署到我们服务器当中去,我们的后端页面是采用 layui 框架来进行编写的。?
?我们这个时候怎么去把它部署到我们项目服务器当中去,因为你不部署的话,这个时候你直接点击,比如说 index.html,??你是访问不到的,因为它提示我们这个东西是需要放到 webservice 里面去才能查看。??我们先来放到我们 webservice 里面去,然后查看一下我们页面有哪一些,把它关掉,复制。?
?然后在我们项目根目录下面有一个叫做 public 目录,public的目录然后有个 static,??然后 static 主要是存放一些静态资源,比如说css js等等,??我为了好管理,创建一个文件夹,取名叫做 admin,这样的话我的后端的 css js image 放在这个地方,??我把之前那个地方复制过来,他复制完之后,比如说css js image 还有 lib 库,然后还有一些 page 页面,
原则上这个配置里面的页面以及 index 的 html 的页面,??它是需要放在我们 admin 应用下面,后面我们会做详细分析,那么现在我们先来看一下我们这个页面访问是??长成什么样的,我怎么去访问index 的 html 的页面,这个时候的话我们打开我们的浏览器,我新建一个窗口,?? 输入ip+port: 127.0.0.1:8082/static/admin/index.html,回车,??这个时候就是我们后台管理的首页,
后台的管理主要是包括比如说商品管理,订单管理,用户信息管理, 商品管理包括商品列表,商品的分类管理,品牌管理,品牌分类,??然后我们支持无限极分类。? 排序,分页,修改状态,删除,都是需要我们来做的。
?因为我们的商品管理其实相对来说它是比较复杂的,它会涉及到图片上传、多图上传、SKU 设计,??它包括基本的增删改查操作,比如说列表、分页、排序、修改状态,删除、??编辑等等。
但如果小伙伴把商品管理的逻辑,你都学会了??,其他的一些管理,比如说订单管理、购物车管理,你都能够轻轻松松的解决,??所以说需要举一反三。?
?但是前端的内容,比如说商城的用户下订单,??用户的加入购物车,因为这些数据有了之后,你后台的管理只需要去把数据库表里面的数据在我们的后台管理里面呈现出来就可以了。??所以说后端管理我们只围绕订单管理和商品管理来做。?
我们先来看一下我们的??登录页面在什么地方, 登录页面,??我们可以看到我们登录页面会有用户名,然后密码,然后还有我们验证码 当我们登录成功之后,直接跳转到到首页里面去。?
2. 后端用户表设计
?在我们开发一个功能之前,表的结构设计是至关重要的,因为我们会使用它去较好的处理一些我们后端逻辑,??包括一些索引的设置等等都是至关重要的。
那么我们来设计一下我们的表结构, 获得用户表,新建个 table, 然后有个ID,??主键ID, type 是 int 形式,然后 length 给个10, Not Null 设置为 允许勾选。? ?然后 comment 就是我写一个注释,比如 后端用户主键 ID,?? 同样我们这个地方需要去做一个处理, Auto increment 相当于是自增的,然后点一下勾选,?? Unsigned 点一下勾选,??【无符号类型仅能表示大于等于0的值】
然后再创建一个字段,username, type 是 varchar,length 给个 100, Not Null 设置为 允许勾选【不为空】,给个索引,??为什么要给索引??【Key 钥匙】 ?因为后续我们会根据用户名去查询后端用户表的某一条记录,所以说需要给个索引它就会快??, Character Set 我们给下 utf8, 然后 Collation 这个地方改成 utf8_general_ci 。?
?然后我们还需要有密码,密码我们是存加密后的 passwrod 。 password 我们是给加密后的 通过 MD5 加密,所以这个时候 type 是 char,然后 length 是 32位,MD5加密后它是32位, 同样Not Null 设置为 允许勾选【不为空】,?? 然后 comment 给个注释比如是用户密码,同样这个地方我们需要去设置它的字符集, 因为是字符串所以Character Set 我们给下 utf8,?? 然后 Collation 这个地方改成 utf8_general_ci 。?
我们还需要有状态 status,?? type 设置成 tinyint,length 我们给个1 就可以了, 同样 Not Null 设置为 允许勾选【不为空】,??默认值我们给一个 0,默认值是 0, 然后 注释的话给一个 比如说状态码,状态码我们可以理解,比如 1 是正常??,0 是 待审核,99是删除,?? 它代表什么?? ?这个是我们自己来定的。
然后再给个字段,??创建时间 create_time, type 是 int, length 是 10, 同样 Not Null 设置为 允许勾选【不为空】,?? Default Value 默认值 是 0, comment 给个 创建时间,??
有创建时间之后,我们还需要有什么,还需要有更新时间update_time , type 是 int, length 是 10, 同样 Not Null 设置为 允许勾选【不为空】,?? comment 给个 更新时间, 然后Default Value 默认值 是 0,??
我们还需要有什么字段,我们还需要有一个 last_login_time,最后登录时间 type 是 int,length 设置成 10, 同样 Not Null 设置为 允许勾选【不为空】,?? comment 是 最后登录时间,?? 然后 Default Value 默认值 是 0,??
?还有最后登录的 IP,last_login_ip, type 设置成 varchar,length 设置成 100, 同样 Not Null 设置为 允许勾选【不为空】,?? comment 设置成 最后登录IP, 因为是字符串所以Character Set 我们给下 utf8,?? 然后 Collation 这个地方改成 utf8_general_ci 。?
还需要有一个操作人,??operate_user,为什么有这个字段? 因为后续??我们可能会对用户进行管理的时候,比如说我删除用户, 编辑用户,??这个时候在后端的所有操作用户的行为都是需要??记录的出来,因为这样的话我们后续排查问题就知道??到底是谁操作的,比如说你更新,删除,修改,我都知道是谁操作的,这样的话就方便排查问题。?
?当然我们表里面记录一份,最好在日志里面也记录一份,?? 后续我们涉及到表里面所有的这种操作用户的行为都用字段来表示,?? type 设置成 varchar,length 同样给个100就可以了, 同样 Not Null 设置为 允许勾选【不为空】,?? 然后 Default Value 默认值 是 空,??不填, comment 设置成 操作人, 因为是字符串所以Character Set 我们给下 utf8,?? 然后 Collation 这个地方改成 utf8_general_ci 。?
表名是 mall_admin_user,然后保存即可。 表结构就设计好了。 后面基于这个表做登录的场景。
3. 后端用户登录 API 逻辑开发
我们验证了这些数据之后,接下来我们需要根据用户输入的用户名,??然后去库里面查我们的数据,那么这个时候我们查完之后,我们还需要核对一下??它的密码是否正确,
密码之前我们说过它是存放 MD5 加密后的字符串,所以说我们需要??给出一个相应的场景,这个时候我就可以来给一个 MD5 加密后的内容,?? 比如说我写一个方法为function md5()。?
然后我需要给这个地方加个盐,??加盐处理,这样的话相对来说就安全一点, 比如说我的加盐的字符串是 keagen_51CTO_CSDN,??当然了不同的用户可以给出不同的加密盐,这也是可以的。?
后端用户加密盐,也就是说固定的字符串,??这样的话我生成密码的 MD5 加密后的字符串存入到我们的表里面,??后续我要根据 admin 用户去我们库里面查记录,然后??来做一个相应的对比,它的密码是否正确,同样也是根据这个算法去对做对比的。?
可以通过 127.0.0.1:8082/admin/login/md5,回车,会返回 MD5 加密后的字符串。
我们写我们的 逻辑,比如说根据用户名先去我们表里面查这条记录有没有? 如果有??我们做相应处理,如果没有我们认为他肯定是不能登录的,
Ok那么这个时候我们的逻辑是什么???查我们数据库里的数据,我查这个数据我们写到model 层,这个时候我在 model 层的 mysql 下面,我创建一个 adminuser。?
?然后创建一个方法,这个方法叫做 getAdminUserByUsername,通过用户名去获取??数据库表里面的一条记录。?
?这个时候给一个username 的形参, 严格意义上说我们需要判断,也就是说 如果 username 为空,??这个时候我要返回一个 false 就可以了。 否则我们就直接给 where 条件,然后让它对应到我们传递过来的use name。? ?最好严格意义上来说的话,我们做个类型转换, 去调用 where,??然后find()就可以了, 然后赋给 变量 result ,然后 return 一下。
?严格意义上来说的话,我们最好在函数/方法 上方 得加一个注释释,注释,根据用户名获取??后端表的数据,这个时候的话我就可以在其他地方去调用它,??去使用它。?
?在其他地方去调用刚我们写的方法 getAdminUserByUsername,?? 然后传递我的用户名, 然后赋给一个变量??叫做 username 或者说 user 或者叫 adminUser。? 这个时候可以测试一下有没有返回数据?
在浏览器 访问: 127.0.0.1:8082/admin/login
输入用户名 , 密码 ,验证码,然后点击登录,登录之后我们来看一下F12——>Network——>XHR,看一下check,ajax 的地址,?? 看下 Preview 返回的数据/对象,
通过看返回的数据来说明说它能否正常够获取到,??获取到之后, 我就可以进行接下来的一些场景逻辑的编写了。?
?那么怎么去编写?第一,首先我们需要判断一下,??判断一下我们的用户是否存在? 我可以这样写, 如果它是为空,并且我们的这个地方的什么状态??? 状态我们说过 1 就是正常的,0 就是临时,99就是删除,所以说我们必须这样来写。?
?然后我们的状态最好是在某个地方来体现一下,我们来写个配置文件,??config,然后我们之前有一个status.php,
然后在这个地方我们去写我们 sql 的一个状态,我们可以这样来写: "table_normal" => 1, "table_pedding" => 0, "table_delete" => 99,
我在其他地方我直接去调就可以了。 ?然后为了方便管理,其实这个地方我认为应该这样来写更合理,比如
// mysql 相关的状态配置
"mysql" => [
"table_normal" => 1, // 正常
"table_pedding" => 0, // 待审核
"table_delete" => 99, // 删除
],
这是个小技巧,这样来写我就知道 这是 跟 mysql 相关的状态配置。
需要用的时候就去配置文件里面去获取。
如果它不等,也就是我们这个地方输出表里面的状态,如果不等于1,那么我们认为它是??没有数据了,或者说这个数据不存在,这个时候我就给一个提示,给一个提示是什么?就是数据错误,
如果没有在页面正常返回, F12——>Network——>XHR,看一下check,ajax 它到底返回什么错误信息,??然后我们针对这个错误信息去做相应的分析和挖掘就可以了。?
?继续往下走,那么我们拿到数据了, 他这个逻辑能走通之后说明我们什么? 说明我们这个用户有了, 那么接下来我还需要去判断什么? 判断密码是否正确对吧??? 可以写if,我们可以去获取它的密码, 然后我们来判断它表里面的密码是否等于或者说如果不等于??,我们传递的这个密码,同样你也需要??和之前这个地方的加密算法一样,md5()算法还有盐字符串。?
?因为它原始的密码它入库的时候通过这种算法来入库的,所以说我们进行一个对比就可以了。??
如果不存在或者说如果不成立,那么我们就给出一个错误提示,比如说密码不正确,密码错误??保存, 做完之后我们可以做个测试。 分别拿正确的密码和错误的密码测试。
登录成功,它就会跳转到后台首页。?
我们首先把我们的验证数据处理好,?? 然后去库里面获取数据,获取完数据之后, 我们需要对我们的密码进行相应的判断,??以及用户是否存在, 然后密码是否正确,做相应的处理就可以了。?
?接下来其实我们还需要有??把记录到 session 里面去,因为后续在后台管理页面当中,??如果他登录了,我的所有操作都和登录有关,所以我要记录到 session 里面去。
记录到 session 里面去的目的,我要判断??它整个流程是否有 session,如果没有 session,后台的场景就不能够去操作,??并且我还要记录一些相应的信息到 mysql 里面去,比如说登录成功了,我要记录他的最后登录时间,??最后的一个登录的 IP 等等这些信息记录到 mysql 里面去。?
4.后台用户登录-数据更新和 session 处理
我们继续看之前的流程,我们判断密码是否正确,然后后面我们还需要对我们的数据表进行更新,存储我们的 session,然后页面跳转。
那么接下来我们来看一下流程,??我们来记录 session, 如何记录,之前 在 gin 的博文中已经讲过,这里不再赘述。?
?记录好之后,我们还需要记录信息到 mysql 表里面,我们怎么去做?
我们直接??同样也是在我们之前的model层创建一个方法,更新的方法,那么直接叫function updateById。
这个时候首先我传一个他的 id 过来,然后第二传到一个 data 过来,??这 data 的值就他要改的内容。?
?那么这样的话同样我需要做个判断,你先转换一下 int(id), 然后判断一下, 如果说我们的 id 是空的,或者说我们的?? data 是空,或者它不是数组, 那么我们认为它是不合法的,所以说这个时候我们直接return false, 然后这个时候同样我们写一个 where 条件,??ID 让它对应到我们接收到的 id。 ?那么这个时候??我们同样也写入注释,即 根据主键ID更新数据表里面的数据,?? 然后我就可以在我们登录的地方去这样来写就可以了,同样它的一个对象 调用 updateById 方法,
然后传递我们的 ID 和 updateData,
$updateData = [
"last_login_time" => time(),
]
然后我需要赋值,我更新什么内容,我更新的是??最后登录时间, 它的字段的话是 last_login_time,通过time()方法给个时间值就可以。?
?用 res 接受 updateById(
i
d
,
id,
id,updateDAta)的返回值, 原则上我们需要去做判断,??比如说如果 res 它是空的,那么我们需要给一个提示登录失败。?
我们还需要做什么场景,你看它都是和数据库进行交互,??包括下面也是和数据库进行交互,所以说这个地方我们严格意义上来说的话,需要加个try catch,你可以 try 用 {} 全包起来,??catch我就给一个错误信息叫做登录失败,或者内部异常登录失败,这个地方最好不要把它的抛异常的数据输出到 message 里面去,因为这样的话就会把它的一些??信息暴露给用户了。?用户就能看到真正的错误信息了,这样其实是非常不安全的。?
?这样的话其实我们可以记录一个日志,后面我们可以这个数据记到日志里面去,一般是这样来做的。?
接下来我们来做个测试,??测试什么? 第一,它是否满足这个场景,数据表是否更新了? 第二,session 是否生效???
输入用户名 密码 验证码 ,点击登录,如果 提示我们登录成功,然后它慢慢的会做一个跳转到后台的首页??,
这个时候,我们看一下我们库里面的数据是否做了更新。? 通过 PHPAdmin 和 mysql 视图终端来看。
如果更新成功,那么数据的更新流程走通了,
这个时候我们再来看一下 session 是否生效? 因为这个时候我们已经登录了,然后 session 已经存储了,??这个时候我们就可以看到我们的 session是不是有,我怎么去获取, 直接打印 session 的值即可。
5. 登录的流程
?这里我画了一个大致的图例,大家看一下: 这里我有三种颜色的箭头,红色、绿色、蓝色, 红色代表我们的正常流的一个走势,?? 绿色代表未登录下的一个流的走势, 蓝色代表已登录状态下的一个流程走势,??我们来一一过一遍。
比如说当我们用户直接去访问登录页面的时候,如果我们登录成功,我们就会跳转到??后端页面。?
如果你登录成功跳到后台首页,如果你登录失败或者说不成功,它就会直接回到我们的登录页面,??是这么一个场景,这是我们正常流走势。?
?我们再来看一下未登录状态下的一个流,??看绿色线条的走势。 当用户直接去访问后台页面,??我们说了他是带着一个未登录的状态下,此时如果你直接去访问后台首页,??那么这个时候的话肯定是不能够让他去访问的,这个时候我们需要让他去跳转到登录页面。?
?当用户没有登录,他直接去访问后台首页的时候,??这个时候我们会让它跳转到登录页面,这就是我们的绿色箭头的一个走势。?
?然后再来看一下蓝色线条的走势。代表它是登录状态下的,??比如说我们这个系统登录了,如果用户再次去访问登录页面,这个时候我们会让他跳转到后台页面,因为你已经登录了,你再回到登录页面是没有任何意义的,??我们让他做一个跳转。?
总体来说,比如说你登录,然后你去访问登录页面,我会让你跳转到首页,你没登录你访问首页,??或者说访问后台的所有页面,这个时候我会让你跳转到登录页面,这是我们的一个思想。?
?这种方式的判断和跳转,我们会在中间件里面去处理 是否登录,然后做相应的跳转。这是我们的一个思想。
|