206 lines
7.1 KiB
PHP
206 lines
7.1 KiB
PHP
<?php
|
|
namespace app\api\controller;
|
|
|
|
use support\Request;
|
|
use app\common\model\User;
|
|
use app\common\model\WechatApp;
|
|
use app\common\model\UserWechatIdentity;
|
|
use app\common\service\AuthService;
|
|
|
|
class WechatAuthController
|
|
{
|
|
public function appList(Request $request)
|
|
{
|
|
$type = trim((string)$request->get('type', ''));
|
|
$query = WechatApp::query()->where('status', 1);
|
|
if ($type !== '') {
|
|
$query->where('type', $type);
|
|
}
|
|
$list = $query->select(['id', 'name', 'type', 'app_id'])->orderByDesc('id')->get();
|
|
return jsonResponse($list);
|
|
}
|
|
|
|
public function miniLogin(Request $request)
|
|
{
|
|
$appId = trim((string)$request->post('app_id', ''));
|
|
$code = trim((string)$request->post('code', ''));
|
|
if ($appId === '' || $code === '') {
|
|
return jsonResponse(null, '参数错误', 400);
|
|
}
|
|
|
|
try {
|
|
$app = $this->getApp($appId, 'mini');
|
|
} catch (\Throwable $e) {
|
|
return jsonResponse(null, $e->getMessage(), 400);
|
|
}
|
|
$secret = (string)($app->app_secret ?? '');
|
|
if ($secret === '') {
|
|
return jsonResponse(null, '未配置 app_secret', 400);
|
|
}
|
|
|
|
$url = 'https://api.weixin.qq.com/sns/jscode2session?appid=' . urlencode($appId) .
|
|
'&secret=' . urlencode($secret) .
|
|
'&js_code=' . urlencode($code) .
|
|
'&grant_type=authorization_code';
|
|
$res = $this->httpGetJson($url);
|
|
$openid = isset($res['openid']) ? trim((string)$res['openid']) : '';
|
|
$unionid = isset($res['unionid']) ? trim((string)$res['unionid']) : '';
|
|
if ($openid === '') {
|
|
$msg = $res['errmsg'] ?? '获取 openid 失败';
|
|
return jsonResponse(null, (string)$msg, 400);
|
|
}
|
|
|
|
$user = $this->resolveUserByWechatIdentity($appId, $openid, $unionid, 'mini');
|
|
if (intval($user->status) !== 1) {
|
|
return jsonResponse(null, '账号已禁用', 403);
|
|
}
|
|
|
|
$this->upsertIdentity($user->id, $appId, $openid, $unionid, 'mini');
|
|
$user->openid = $openid;
|
|
$user->save();
|
|
|
|
$token = AuthService::issueUserToken($user);
|
|
return jsonResponse([
|
|
'token' => $token,
|
|
'user' => $user,
|
|
'openid' => $openid,
|
|
'app_id' => $appId,
|
|
], '登录成功');
|
|
}
|
|
|
|
public function h5Login(Request $request)
|
|
{
|
|
$appId = trim((string)$request->post('app_id', ''));
|
|
$code = trim((string)$request->post('code', ''));
|
|
if ($appId === '' || $code === '') {
|
|
return jsonResponse(null, '参数错误', 400);
|
|
}
|
|
|
|
try {
|
|
$app = $this->getApp($appId, 'h5');
|
|
} catch (\Throwable $e) {
|
|
return jsonResponse(null, $e->getMessage(), 400);
|
|
}
|
|
$secret = (string)($app->app_secret ?? '');
|
|
if ($secret === '') {
|
|
return jsonResponse(null, '未配置 app_secret', 400);
|
|
}
|
|
|
|
$url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' . urlencode($appId) .
|
|
'&secret=' . urlencode($secret) .
|
|
'&code=' . urlencode($code) .
|
|
'&grant_type=authorization_code';
|
|
$res = $this->httpGetJson($url);
|
|
$openid = isset($res['openid']) ? trim((string)$res['openid']) : '';
|
|
$unionid = isset($res['unionid']) ? trim((string)$res['unionid']) : '';
|
|
if ($openid === '') {
|
|
$msg = $res['errmsg'] ?? '获取 openid 失败';
|
|
return jsonResponse(null, (string)$msg, 400);
|
|
}
|
|
|
|
$user = $this->resolveUserByWechatIdentity($appId, $openid, $unionid, 'h5');
|
|
if (intval($user->status) !== 1) {
|
|
return jsonResponse(null, '账号已禁用', 403);
|
|
}
|
|
|
|
$this->upsertIdentity($user->id, $appId, $openid, $unionid, 'h5');
|
|
$user->openid = $openid;
|
|
$user->save();
|
|
|
|
$token = AuthService::issueUserToken($user);
|
|
return jsonResponse([
|
|
'token' => $token,
|
|
'user' => $user,
|
|
'openid' => $openid,
|
|
'app_id' => $appId,
|
|
], '登录成功');
|
|
}
|
|
|
|
public function h5AuthorizeUrl(Request $request)
|
|
{
|
|
$appId = trim((string)$request->get('app_id', ''));
|
|
$redirectUri = trim((string)$request->get('redirect_uri', ''));
|
|
$scope = trim((string)$request->get('scope', 'snsapi_base'));
|
|
$state = trim((string)$request->get('state', ''));
|
|
|
|
if ($appId === '' || $redirectUri === '') {
|
|
return jsonResponse(null, '参数错误', 400);
|
|
}
|
|
if (!in_array($scope, ['snsapi_base', 'snsapi_userinfo'], true)) {
|
|
return jsonResponse(null, 'scope 不合法', 400);
|
|
}
|
|
|
|
try {
|
|
$this->getApp($appId, 'h5');
|
|
} catch (\Throwable $e) {
|
|
return jsonResponse(null, $e->getMessage(), 400);
|
|
}
|
|
|
|
$url = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' . urlencode($appId) .
|
|
'&redirect_uri=' . urlencode($redirectUri) .
|
|
'&response_type=code&scope=' . urlencode($scope) .
|
|
'&state=' . urlencode($state) .
|
|
'#wechat_redirect';
|
|
|
|
return jsonResponse(['url' => $url]);
|
|
}
|
|
|
|
private function getApp(string $appId, string $type): WechatApp
|
|
{
|
|
$row = WechatApp::where('app_id', $appId)->where('status', 1)->first();
|
|
if (!$row) {
|
|
throw new \RuntimeException('AppID 未配置或已停用');
|
|
}
|
|
if ((string)$row->type !== $type) {
|
|
throw new \RuntimeException('AppID 类型不匹配');
|
|
}
|
|
return $row;
|
|
}
|
|
|
|
private function httpGetJson(string $url): array
|
|
{
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
|
$body = curl_exec($ch);
|
|
if ($body === false) {
|
|
$err = curl_error($ch);
|
|
curl_close($ch);
|
|
throw new \RuntimeException('微信接口请求失败: ' . $err);
|
|
}
|
|
curl_close($ch);
|
|
return json_decode($body, true) ?: [];
|
|
}
|
|
|
|
private function resolveUserByWechatIdentity(string $appId, string $openid, string $unionid, string $scene): User
|
|
{
|
|
if ($unionid !== '') {
|
|
$identity = UserWechatIdentity::where('unionid', $unionid)->first();
|
|
if ($identity) {
|
|
$user = User::find($identity->user_id);
|
|
if ($user) return $user;
|
|
}
|
|
}
|
|
|
|
$identity = UserWechatIdentity::where('app_id', $appId)->where('openid', $openid)->first();
|
|
if ($identity) {
|
|
$user = User::find($identity->user_id);
|
|
if ($user) return $user;
|
|
}
|
|
|
|
return User::create([
|
|
'openid' => $openid,
|
|
'nickname' => $scene === 'mini' ? '小程序用户' : '微信用户',
|
|
'status' => 1,
|
|
]);
|
|
}
|
|
|
|
private function upsertIdentity(int $userId, string $appId, string $openid, string $unionid, string $scene): void
|
|
{
|
|
UserWechatIdentity::updateOrCreate(
|
|
['user_id' => $userId, 'app_id' => $appId],
|
|
['openid' => $openid, 'unionid' => $unionid ?: null, 'scene' => $scene]
|
|
);
|
|
}
|
|
}
|