本文目录导读:

我来详细介绍ThinkPHP项目中实现微信登录与授权的完整方案。
基础准备
环境配置
// config/wechat.php 配置文件
return [
'app_id' => 'your_app_id',
'app_secret' => 'your_app_secret',
'redirect_uri' => 'https://yourdomain.com/wechat/callback',
'scope' => 'snsapi_userinfo', // snsapi_base 或 snsapi_userinfo
'oauth_url' => 'https://open.weixin.qq.com/connect/oauth2/authorize',
'token_url' => 'https://api.weixin.qq.com/sns/oauth2/access_token',
'userinfo_url' => 'https://api.weixin.qq.com/sns/userinfo'
];
安装依赖
composer require overtrue/wechat
网页授权登录
发起授权请求
<?php
namespace app\wechat\controller;
use think\Controller;
use think\facade\Config;
use think\facade\Session;
class Login extends Controller
{
protected $wechatConfig;
public function __construct()
{
$this->wechatConfig = Config::get('wechat.');
}
// 发起微信授权
public function authorize()
{
// 生成随机state参数防止CSRF
$state = md5(uniqid(rand(), true));
Session::set('wechat_state', $state);
$param = [
'appid' => $this->wechatConfig['app_id'],
'redirect_uri' => $this->wechatConfig['redirect_uri'],
'response_type' => 'code',
'scope' => $this->wechatConfig['scope'],
'state' => $state
];
$url = $this->wechatConfig['oauth_url'] . '?' . http_build_query($param) . '#wechat_redirect';
return redirect($url);
}
}
回调处理
// 回调处理
public function callback()
{
$code = input('get.code');
$state = input('get.state');
// 验证state防止CSRF
if ($state !== Session::get('wechat_state')) {
return json(['code' => -1, 'msg' => '非法请求']);
}
// 获取access_token
$tokenInfo = $this->getAccessToken($code);
if (isset($tokenInfo['errcode'])) {
return json(['code' => -1, 'msg' => $tokenInfo['errmsg']]);
}
// 获取用户信息
$userInfo = $this->getUserInfo($tokenInfo);
// 处理用户登录
$result = $this->processLogin($tokenInfo, $userInfo);
return $result;
}
// 获取access_token
private function getAccessToken($code)
{
$url = $this->wechatConfig['token_url'];
$params = [
'appid' => $this->wechatConfig['app_id'],
'secret' => $this->wechatConfig['app_secret'],
'code' => $code,
'grant_type' => 'authorization_code'
];
$response = Http::get($url, $params);
return json_decode($response, true);
}
// 获取用户信息
private function getUserInfo($tokenInfo)
{
$url = $this->wechatConfig['userinfo_url'];
$params = [
'access_token' => $tokenInfo['access_token'],
'openid' => $tokenInfo['openid'],
'lang' => 'zh_CN'
];
$response = Http::get($url, $params);
return json_decode($response, true);
}
用户登录处理
业务逻辑处理
// 处理用户登录
private function processLogin($tokenInfo, $userInfo)
{
$openid = $userInfo['openid'];
// 检查用户是否存在
$user = UsersModel::where('openid', $openid)->find();
if (!$user) {
// 新用户注册
$userData = [
'openid' => $openid,
'nickname' => $userInfo['nickname'] ?? '',
'avatar' => $userInfo['headimgurl'] ?? '',
'sex' => $userInfo['sex'] ?? 0,
'province' => $userInfo['province'] ?? '',
'city' => $userInfo['city'] ?? '',
'unionid' => $tokenInfo['unionid'] ?? '',
'create_time' => time()
];
$userId = UsersModel::insertGetId($userData);
$user = UsersModel::find($userId);
} else {
// 更新用户信息
$userData = [
'nickname' => $userInfo['nickname'] ?? $user['nickname'],
'avatar' => $userInfo['headimgurl'] ?? $user['avatar'],
'update_time' => time()
];
UsersModel::where('id', $user['id'])->update($userData);
$user = UsersModel::find($user['id']);
}
// 生成登录凭证
$token = $this->createToken($user['id']);
// 记录登录日志
$this->saveLoginLog($user['id']);
// 返回登录信息
return json([
'code' => 1,
'msg' => '登录成功',
'data' => [
'token' => $token,
'user' => $user
]
]);
}
// 生成登录token
private function createToken($userId)
{
$payload = [
'user_id' => $userId,
'iat' => time(),
'exp' => time() + 7200 // 有效期2小时
];
$token = base64_encode(json_encode($payload));
// 保存到缓存
Cache::set('login_token_' . $token, $userId, 7200);
return $token;
}
移动端H5登录
namespace app\wechat\controller;
use think\Controller;
use Overtrue\WeChat\Factory;
class MobileLogin extends Controller
{
protected $app;
public function __construct()
{
$this->app = Factory::officialAccount([
'app_id' => config('wechat.app_id'),
'secret' => config('wechat.app_secret'),
'response_type' => 'array',
'log' => [
'level' => 'debug',
'file' => runtime_path() . '/wechat.log',
],
]);
}
// 微信授权登录
public function oauthLogin()
{
$oauth = $this->app->oauth;
// 设置回调地址
$callbackUrl = url('wechat/MobileLogin/oauthCallback', [], true, true);
// 跳转到授权页面
return redirect($oauth->redirect($callbackUrl));
}
// 回调函数
public function oauthCallback()
{
$oauth = $this->app->oauth;
// 获取OAuth用户信息
$user = $oauth->user();
// 获取用户基本信息
$userInfo = $user->toArray();
// 处理登录逻辑
return $this->processOauthLogin($userInfo);
}
}
扫码登录(网站应用)
生成二维码
namespace app\wechat\controller;
class QrLogin extends Controller
{
// 获取登录二维码
public function getQrCode()
{
$state = md5(rand());
Session::set('qr_state', $state);
// 生成二维码内容
$qrContent = url('wechat/QrLogin/qrCallback', [
'state' => $state
], true, true);
// 生成二维码
$qrCode = new QrCode($qrContent);
// 返回二维码图片
return $qrCode->setSize(220)->setMargin(10)->writeString();
}
// 轮询检查登录状态
public function checkLogin()
{
$state = input('state');
$key = 'qr_login_' . $state;
if (Cache::has($key)) {
$userInfo = Cache::get($key);
Cache::delete($key);
// 完成登录
return json([
'code' => 1,
'msg' => '登录成功',
'data' => $userInfo
]);
}
return json([
'code' => 0,
'msg' => '等待扫码'
]);
}
}
小程序登录
namespace app\wechat\controller;
use think\Controller;
use think\facade\Cache;
class MiniProgram extends Controller
{
protected $config;
public function __construct()
{
$this->config = [
'app_id' => config('wechat.mini_app_id'),
'app_secret' => config('wechat.mini_app_secret'),
];
}
// 小程序登录
public function login()
{
$code = input('code');
$userInfo = input('userInfo/a', []);
// 获取微信登录凭证
$sessionData = $this->code2Session($code);
if (isset($sessionData['errcode'])) {
return json(['code' => -1, 'msg' => $sessionData['errmsg']]);
}
$openid = $sessionData['openid'];
$unionid = $sessionData['unionid'] ?? '';
// 查询或创建用户
$user = $this->findOrCreateUser($openid, $unionid, $userInfo);
// 生成登录凭证
$token = $this->createLoginToken($user['id']);
return json([
'code' => 1,
'msg' => '登录成功',
'data' => [
'token' => $token,
'user_id' => $user['id']
]
]);
}
// code换session
private function code2Session($code)
{
$url = 'https://api.weixin.qq.com/sns/jscode2session';
$params = [
'appid' => $this->config['app_id'],
'secret' => $this->config['app_secret'],
'js_code' => $code,
'grant_type' => 'authorization_code'
];
$response = Http::get($url, $params);
return json_decode($response, true);
}
}
数据库设计
-- 用户表 CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `openid` varchar(50) NOT NULL COMMENT '微信openid', `unionid` varchar(50) DEFAULT NULL COMMENT '微信unionid', `nickname` varchar(100) DEFAULT NULL COMMENT '昵称', `avatar` varchar(255) DEFAULT NULL COMMENT '头像', `sex` tinyint(4) DEFAULT '0' COMMENT '性别 0未知 1男 2女', `province` varchar(50) DEFAULT NULL COMMENT '省份', `city` varchar(50) DEFAULT NULL COMMENT '城市', `country` varchar(50) DEFAULT NULL COMMENT '国家', `phone` varchar(20) DEFAULT NULL COMMENT '手机号', `status` tinyint(4) DEFAULT '1' COMMENT '状态 1正常 0禁用', `last_login_time` int(11) DEFAULT NULL COMMENT '最后登录时间', `create_time` int(11) DEFAULT NULL COMMENT '创建时间', `update_time` int(11) DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `openid` (`openid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 登录日志表 CREATE TABLE `user_login_log` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL COMMENT '用户ID', `login_type` varchar(20) DEFAULT 'wechat' COMMENT '登录类型', `ip` varchar(50) DEFAULT NULL COMMENT '登录IP', `user_agent` varchar(255) DEFAULT NULL COMMENT '浏览器信息', `create_time` int(11) DEFAULT NULL COMMENT '登录时间', PRIMARY KEY (`id`), KEY `user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
安全措施
// 安全防护示例
class Security implements Middleware
{
public function handle($request, \Closure $next)
{
// 1. 验证请求时间戳
$timestamp = $request->header('timestamp');
if (abs(time() - $timestamp) > 300) {
return json(['code' => -1, 'msg' => '请求已过期']);
}
// 2. 验证签名
$sign = $request->header('sign');
$params = $request->param();
ksort($params);
$signStr = http_build_query($params) . '&key=' . config('wechat.sign_key');
if (md5($signStr) !== $sign) {
return json(['code' => -1, 'msg' => '签名验证失败']);
}
// 3. 防止重放攻击
$nonce = $request->header('nonce');
if (Cache::has($nonce)) {
return json(['code' => -1, 'msg' => '重复请求']);
}
Cache::set($nonce, time(), 300);
return $next($request);
}
}
前端集成
<!-- 前端登录按钮 -->
<script>
// 微信登录
function wechatLogin() {
window.location.href = '/wechat/Login/authorize';
}
// 获取登录状态
function checkLoginStatus() {
$.ajax({
url: '/user/check-login',
method: 'GET',
success: function(response) {
if (response.code === 1) {
// 已登录
showUserInfo(response.data.user);
}
}
});
}
// 保存token到本地
function saveToken(token) {
localStorage.setItem('wechat_token', token);
}
// 请求时携带token
$.ajaxSetup({
beforeSend: function(xhr) {
const token = localStorage.getItem('wechat_token');
if (token) {
xhr.setRequestHeader('Authorization', 'Bearer ' + token);
}
}
});
</script>
注意事项
-
安全方面:
- 使用HTTPS协议
- 验证state参数防止CSRF
- 对敏感信息进行加密存储
- 限制回调地址白名单
-
开发环境:
- 内网穿透工具:ngrok、natapp等
- 配置域名白名单
- 使用测试号进行开发测试
-
生产环境:
- 配置正式AppID和AppSecret
- 服务器IP白名单
- 日志监控和错误告警
这个方案涵盖了微信网页授权、小程序登录、扫码登录等场景,可根据实际需求选择使用。