## **token的作用**:
### 1.防止表单重复提交
我们在客户端提交表单时,带上一个在隐藏域(<input type="hidden")的token,后台会验证这个token是否被用过.如果不符合,就弹出提示 :Token验证错误!
这样如果用户连续点击提交,也不怕重复提交了.
有些也在前端做了预防措施:比如点击提交按钮后,这个按钮变成灰色不可点击.
具体的例子:
前端生成token:
在application\\admin\\view\\user\\user\\edit.html 中 有 {:token()} ,这个是调用token函数生成一个token.
在thinkphp\\helper.php 的
```
function token($name = '__token__', $type = 'md5')
{
$token = Request::instance()->token($name, $type);
return '<input type= "hidden" name="' . $name . '" value="' . $token . '" />';
}
```
直接生成对应的html代码到视图文件;
后台验证token:
在application\common\controller\Backend.php 的
```
protected function token()
{
$token = $this->request->param('__token__');
//验证Token
if (!Validate::make()->check(['__token__' => $token], ['__token__' => 'require|token'])) {
$this->error(__('Token verification error'), '', ['__token__' => $this->request->token()]);
}
//刷新Token,重新生成一个token,旧的也就失效了
$this->request->token();
}
```
后台是否进行token验证并不是强制和自动的,如果有需要进行验证,则需要手动调用这个token()函数,
比如:在edit 方法中
```
if ($this->request->isPost()) {
$this->token();
$this->modelValidate = true; //开启验证器
$this->modelSceneValidate =true;//开启场景验证
}
```
=======================================================================
### 2.用来做身份验证, 流程如下:
(1) 客户端使用用户名与密码请求登录
(2) 服务端收到请求, 去验证用户名与密码
(3) 验证成功后 , 服务端会签发一个token, 再把这个token发送给客户端
(4) 客户端收到token后, 可以把它存储起来, 比如放在cookie 中或者Local Storage中
(5) 客户端每次向服务端请求资源时带着服务端签发的token
(6) 服务端收到请求, 然后去验证客户端请求里带的token, 如果验证成功, 就向客户端返回请求的数据
在FA里面的流程:(FA的管理后台部分的admin管理员登录状态是使用session的,前台user用户才是使用token)
1. 普通网页登录生成token:
application\index\controller\User.php 的 public function login(),验证通过账号密码后,调用
application\common\library\Auth.php 的public function direct($user\_id);
在里面生成token:$this\->\_token = Random::uuid(); 生成的token 是8位-4位-4位-4位-12位, 如:289d3e43-838c-4178-add2-b8e3a276a956
然后把token 写入到 token驱动器里面, 可以选择使用mysql保存或者redis保存,默认是mysql.
token数据库驱动器:application\common\library\token\driver\Mysql.php
tokenRedis驱动器:application\common\library\token\driver\Redis.php
FA默认是使用File驱动器,但实际上并不存在这个文件驱动器.
登录成功后,触发钩子 user\_login\_successed ,
钩子实际定义在: application\index\controller\User.php 的public function \_initialize() 中
```
Hook::add('user_login_successed', function ($user) use ($auth) {
$expire = input('post.keeplogin') ? 30 * 86400 : 0;
Cookie::set('uid', $user->id, $expire);
Cookie::set('token', $auth->getToken(), $expire);
});
```
作用就是把token写入到cookie中,并且有效期是30天.随着cookie返回给客户端.
这样在服务端 和客户端都保存了token;
服务端代码要获取当前管理员的用户信息:
2.用API登录生成token:
application\api\controller\User.php 的public function login() ;接收账号密码后,很第一步一样的操作,生成token,保存token, 只是在返回给客户端时,不是使用cookies,而是把token 随着接口返回数据一起返回给客户端;
在application\common\library\Auth.php 的public function getUserinfo() 中,
```
$userinfo = array\_merge($userinfo, Token::get($this\->\_token));
return $userinfo;
```
把Token 并入到用户数据中一起返回,客户端在收到数据后,自己把token 保存起来,一般是写入到Local Storage 里面;
3.使用和验证token: 已修改用户信息为例:
客户端调用application\api\controller\User.php 的public function profile() ,进行用户信息修改
application\common\controller\Api.php 是所有api类的基类,在Api.php的_initialize()初始化函数中,
```
$token = $this->request->server('HTTP_TOKEN', $this->request->request('token', \think\Cookie::get('token')));
//这句话的意思是:先从Header中查有没有叫HTTP_TOKEN的值,不存在的话,再去请求值中查名称是token的值,还不存在的话, 最后去cookies中查token
```
拿到token 然后去查找:$this\->auth\->init($token);
最终从数据库中查到, 或者从redis中找到这个值, 找不到的话,则会返回错误提示.
```
$this->error(__('Please login first'), null, 401);
```
上面获取token 用了3种方式,这3种可以对应3种场景:
HTTP_TOKEN:对应的一般是app场景,app里面开发者是可以自主设置请求头的内容;
request:对应的一般是纯接口场景,比如开放接口给第三方调用,此时不方便要求对方修改或者对方没有权限修请求头的内容,此时,每次调用都需要在请求参数中带上token;
cookies:对应的一般是网页场景,使用网页端,不跨域,网站内部调用居多;
### 3.token的加密方式:
FA展示给用户看的token是形如:289d3e43-838c-4178-add2-b8e3a276a956;
而在数据库中存储的值是形如:2a86f525289ba66e9e71136c88b5862a1407ad13;
中间使用了加密转换:
使用了application\common\library\token\Driver.php 的
```
/**
* 获取加密后的Token
* @param string $token Token标识
* @return string
*/
protected function getEncryptedToken($token)
{
$config = \think\Config::get('token');
return hash_hmac($config['hashalgo'], $token, $config['key']);
}
```
在配置文件application\config.php 中
```
// +----------------------------------------------------------------------
// | Token设置
// +----------------------------------------------------------------------
'token' => [
// 驱动方式
'type' => 'Mysql',
// 缓存前缀
'key' => 'r1CTmdF8ogXL5M9HGyeQPA7I4ZKS2nUE',
// 加密方式
'hashalgo' => 'ripemd160',
// 缓存有效期 0表示永久缓存
'expire' => 0,
],
```
- FA的JS调用机制说明
- FA的JS之Fast.api逐个详解
- FA页面渲染时后端传递数据给前端的方式
- FA的ajax查询数据的前后台流程
- FA特有的函数解释
- FA的鉴权Auth类
- extend\fast\Auth.php详解
- application\admin\library\Auth.php详解
- application\common\library\Auth.php详解
- FA的Token机制
- FA管理员(后台)的权限机制
- FA用户(前台和API)的权限机制
- FA在前台模板文件中进行鉴权
- FA的登录页面
- TP类Hook:钩子机制
- TP类Lang:多语言机制
- TP类Config:参数配置机制
- TP类Request:请求类
- TP的模型关联详解
- think-queue队列组件
- Queue.php
- \queue\Connector.php
- \queue\connector\Redis.php
- \queue\Job.php
- queue\job\Redis.php
- PHP规则:正则表达式
- PHP规则:闭包与匿名函数
- 项目架构说明
- 代码架构
- TP数据库where条件的各种写法
