资讯动态

Uni-app + FastAdmin/ThinkPHP5 自定义 Token 登录流程

发布时间:2026/9/5 17:38:10 来源:尧图企业网站定制
一、涉及的核心文件说明1、后端[TokenService.php](/application/api/service/TokenService.php)token 服务类,专门处理 token 相关逻辑。主要负责生成 token、校验 token、判断 token 是否过期、退出登录时作废 token。[AuthService.php](/application/api/service/AuthService.php)登录认证服务类,专门处理账号登录逻辑。主要负责查询账号、判断账号状态、校验密码、兼容旧明文密码、生成加密密码、登录成功后调用TokenService下发 token。[AuthToken.php](/application/api/middleware/AuthToken.php)token 鉴权拦截类。每次访问需要登录的接口时,它会先取请求头里的 token,调用TokenService校验。校验成功后,把当前用户信息挂到request()-tokenUser,控制器就可以直接获取当前登录用户。[tags.php](/application/tags.php)ThinkPHP5 行为注册文件。用来把AuthToken注册到module_init阶段,让 token 校验逻辑在接口执行前自动运行。如果这里不注册,AuthToken不会生效。[common.php](/application/common.php)全局公共函数文件。这里可以放一些全局助手函数,比如获取当前登录用户、当前用户 id、当前用户名等。这样控制器里就不用每次都写request()-tokenUser。[Userlogin.php](/application/api/controller/Userlogin.php)登录相关控制器。主要接收前端传来的账号密码,然后调用AuthService::login()完成登录。登录成功后,把用户信息和 token 返回给前端。[Mine1.php](/application/api/controller/Mine1.php)个人中心相关控制器。登录后获取个人信息、修改密码、退出登录等接口一般放这里。它可以通过request()-tokenUser或userid()获取当前登录用户,再执行对应业务。2、前端[request.js](/utils/request.js)前端统一请求封装文件。所有接口请求都建议走这里,统一设置请求地址、请求方式、请求头,并自动从缓存读取 token 放到 header。遇到401/403时,统一提示登录失效并跳转登录页。[userlogin.vue](/pages/login/userlogin/userlogin.vue)账号密码登录页面。用户输入账号和密码后,请求后端Userlogin/userlogin。登录成功后,把后端返回的 token、用户名、权限等信息存入本地缓存,然后跳转到主页面。[index.vue](/pages/mine/index/index.vue)个人中心首页。用来展示当前登录用户的基础信息,比如员工号、姓名、绑定状态等。页面进入时会请求个人信息接口。也可以在这里处理退出登录、微信绑定、进入个人资料页等操作。[user_info.vue](/pages/mine/user_info/user_info.vue)个人资料页面。用来展示当前用户的详细资料,比如账号、员工号、姓名、部门、手机号、邮箱、地址等。现在弹窗只允许修改密码,提交时只把新密码传给后端,由后端进行加密保存。二、整体流程前端登录页:用户输入账号密码 ↓ 前端请求 控制器方法:Userlogin/userlogin ↓ Userlogin 控制器接收参数,调用服务类AuthService ↓ AuthService::login 校验账号密码 ↓ 账号存在?账号未禁用?密码正确? ↓ 满足账号检验 调用TokenService::issue 生成 token ↓ token 写入 数据库表fa_account_info.token字段 ↓ 后端返回用户信息和 token 给前端 ↓ 前端缓存 token uni.setStorageSync('token', token); ↓ 后续前端发送其余请求:全部通过 request.js 自动携带 token ↓ 前端发送请求到控制器 执行中间件AuthToken::moduleInit 判断页面是否非登录页面 统一校验 token ↓ 中间件调用TokenService 验证token是否过期、失效等判断 ↓ 校验成功后挂载 request()-tokenUser 例如:$user = request()-tokenUser ↓ 业务控制器获取当前用户并执行接口三、后端操作说明1、token 服务路径:/application/api/service/TokenService.php这个类负责三件事:issue():生成 tokenverify():校验 tokenrevoke():退出时作废 token完整代码:?php namespace app\api\service; use think\Db; class TokenService { // token 有效期,默认 7 天 protected $expire = 604800; // 校验 token,并返回详细结果 public function verify($token) { if (!$token) { return ['ok' = false, 'code' = 401, 'msg' = '缺少token']; } $user = Db::table('fa_account_info')-where('token', $token)-find(); if (!$user) { return ['ok' = false, 'code' = 401, 'msg' = 'token无效,请重新登录']; } if ((int)$user['blocked'] === 1) { return ['ok' = false, 'code' = 403, 'msg' = '账号已失效']; } if ($this-isExpired($user['logintime'])) { return ['ok' = false, 'code' = 401, 'msg' = '登录已过期,请重新登录']; } Db::table('fa_account_info')-where('id', $user['id'])-update([ 'logintime' = time() ]); return ['ok' = true, 'code' = 1, 'msg' = '验证成功', 'user' = $user]; } // 判断 token 是否过期 public function isExpired($logintime) { return empty($logintime) || time() - intval($logintime) $this-expire; } // 生成 token public function issue($userId) { $token = bin2hex(random_bytes(32)); Db::table('fa_account_info')-where('id', $userId)-update([ 'token' = $token, 'logintime' = time() ]); return $token; } // 退出/作废 token public function revoke($userId) { return Db::table('fa_account_info')-where('id', $userId)-update([ 'token' = '', 'logintime' = 0, ]); } }2、登录服务路径:/application/api/service/AuthService.php作用是处理账号登录:查用户判断账号是否禁用校验密码生成 token更新登录信息示例逻辑:这里兼容了明文密码操作(如果是明文需要更改为加密密码)?php namespace app\api\service; use think\Db; class AuthService { // 账号密码登录 public function login($username, $password) { // 根据前端传来的账号查询用户信息 $user = Db::table('fa_account_info')-where('username', $username)-find(); // 如果没有查询到用户,说明账号不存在 if (!$user) { return ['code' = 0, 'msg' = '账号不存在', 'data' = null]; } // blocked 等于 1 表示账号已禁用或失效 if ((int)$user['blocked'] === 1) { return ['code' = 0, 'msg' = '账号已失效', 'data' = null]; } // 校验密码是否正确 // 这里兼容旧的明文密码和新的加密密码 if (!$this-verifyPassword($password, $user['password'])) { // 密码错误时,记录失败次数加 1 Db::table('fa_account_info')-where('id', $user['id'])-setInc('loginfailure'); return ['code' = 0, 'msg' = '密码错误', 'data' = null]; } // 实例化 token 服务,用于生成登录 token $tokenService = new TokenService(); // 根据当前用户 id 生成 token,并写入数据库 $token = $tokenService-issue($user['id']); // 更新登录相关信息 // 如果当前密码还是旧明文格式,则顺手升级为 password_hash 加密后的密码 Db::table('fa_account_info')-where('id', $user['id'])-update([ 'token' = $token, 'logintime' = time(), 'loginip' = request()-ip(), 'loginfailure' = 0, 'successions' = Db::raw('successions + 1'), 'password' = $this-needsUpgrade($user['password']) ? $this-hashPassword($password) : $user['password'], ]); // 返回给前端前,去掉密码字段,避免密码泄露 unset($user['password']); // 把本次登录生成的 token 放到返回数据中 $user['token'] = $token; // 登录成功,返回用户信息和 token return ['code' = 1, 'msg' = '登录成功', 'data' = $user]; } // 生成密码哈希 // PASSWORD_DEFAULT 会使用 PHP 当前推荐的安全算法 public function hashPassword($password) { return password_hash($password, PASSWORD_DEFAULT); } // 验证密码 // 兼容两种情况: // 1. 数据库里还是旧明文密码 // 2. 数据库里已经是 password_hash 加密后的密码 public function verifyPassword($input, $stored) { // 如果数据库中的密码不是 password_hash 格式,说明还是旧明文密码 if ($this-needsUpgrade($stored)) { // 使用 hash_equals 比较明文,避免普通字符串比较带来的时序攻击问题 return hash_equals((string)$stored, (string)$input); } // 如果数据库中已经是哈希密码,则用 password_verify 校验 return password_verify($input, $stored); } // 判断数据库中的密码是否需要升级 // 如果 password_get_info 取不到算法,说明不是 password_hash 生成的密码 public function needsUpgrade($stored) { return empty(password_get_info($stored)['algo']); } }3、后端:token 中间件路径:/application/api/middleware/AuthToken.php作用是拦截接口请求,统一校验 token。如果 token 无效、过期、账号禁用,就直接返回错误。?php namespace app\api\middleware; use app\api\service\TokenService; use think\exception\HttpResponseException; class AuthToken { // 不需要登录校验的接口白名单 protected $except = [ 'userlogin/userlogin', 'userlogin/getopenid', 'userlogin/isbind', ]; // 模块初始化时执行,负责拦截请求并校验 token public function moduleInit($request) { // 当前请求的 控制器/方法 $current = strtolower($request-controller() . '/' . $request-action()); // 白名单接口直接放行 if (in_array($current, $this-except)) { return; } // 从请求头或参数里取 token // 前端请求时会自动把 token 放到 header 里 $token = $request-server('HTTP_TOKEN', $request-request('token', '')); // 调用 TokenService 校验 token // 返回结构:['ok' = true/false, 'code' = xxx, 'msg' = 'xxx', 'user'

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价