Login.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. <?php
  2. namespace app\api\controller;
  3. use addons\third\model\Third;
  4. use app\common\library\Auth;
  5. use app\common\library\Sms;
  6. use app\common\library\Ems;
  7. use fast\Random;
  8. use think\Validate;
  9. use fast\Http;
  10. use addons\third\library\Service;
  11. use app\common\Enum\StatusEnum;
  12. use think\Config;
  13. use think\Env;
  14. use think\Session;
  15. use app\common\Enum\ChannelEnum;
  16. class Login extends Base
  17. {
  18. protected $noNeedLogin = ['*'];
  19. public function _initialize()
  20. {
  21. parent::_initialize();
  22. if (!$this->request->isPost()) {
  23. $this->error('请求错误');
  24. }
  25. Auth::instance()->setAllowFields(array_merge(['username'], $this->allowFields));
  26. }
  27. /**
  28. * 会员登录
  29. *
  30. * @param string $account 账号
  31. * @param string $password 密码
  32. */
  33. public function login()
  34. {
  35. $params = $this->request->param();
  36. $account = $params['account'] ?? '';
  37. $password = $params['password'] ?? '';
  38. //增加验证器
  39. $validate = new \app\api\validate\User();
  40. if (!$validate->check($params, [], 'login')) {
  41. $this->error($validate->getError());
  42. }
  43. $ret = $this->auth->login($account, $password);
  44. if ($ret) {
  45. $user = $this->auth->getUserinfo();
  46. $user['avatar'] = cdnurl($user['avatar'], true);
  47. $this->success(__('Logged in successful'), [
  48. 'token' => $this->auth->getToken(),
  49. 'user' => $user
  50. ]);
  51. } else {
  52. $this->error($this->auth->getError());
  53. }
  54. }
  55. /**
  56. * 重置密码
  57. *
  58. * @param string $mobile 手机号
  59. * @param string $newpassword 新密码
  60. * @param string $captcha 验证码
  61. */
  62. public function resetpwd()
  63. {
  64. $params = $this->request->param();
  65. $type = $params['type'] ?? '';
  66. $mobile = $params['mobile'] ?? '';
  67. $email = $params['email'] ?? '';
  68. $newpassword = $params['newpassword'] ?? '';
  69. $captcha = $params['captcha'] ?? '';
  70. //验证器
  71. $validate = new \app\api\validate\UserCancel();
  72. if (!$validate->check($params, [], 'resetpwd')) {
  73. $this->error($validate->getError());
  74. }
  75. if ($type == 'mobile') {
  76. $user = \app\common\model\User::getByMobile($mobile);
  77. if (!$user) {
  78. $this->error(__('User not found'));
  79. }
  80. if (!Env::get('app.app_debug') && $captcha != Env::get('app.DEFAULT_SMSCODE')) {
  81. $ret = Sms::check($mobile, $captcha, 'resetpwd');
  82. if (!$ret) {
  83. $this->error(__('Captcha is incorrect'));
  84. }
  85. }
  86. Sms::flush($mobile, 'resetpwd');
  87. } else {
  88. $user = \app\common\model\User::getByEmail($email);
  89. if (!$user) {
  90. $this->error(__('User not found'));
  91. }
  92. $ret = Ems::check($email, $captcha, 'resetpwd');
  93. if (!$ret) {
  94. $this->error(__('Captcha is incorrect'));
  95. }
  96. Ems::flush($email, 'resetpwd');
  97. }
  98. //模拟一次登录
  99. $this->auth->direct($user->id);
  100. $ret = $this->auth->changepwd($newpassword, '', true);
  101. if ($ret) {
  102. $this->success(__('Reset password successful'));
  103. } else {
  104. $this->error($this->auth->getError());
  105. }
  106. }
  107. /**
  108. * 手机验证码登录
  109. *
  110. * @param string $mobile 手机号
  111. * @param string $captcha 验证码
  112. */
  113. public function mobilelogin()
  114. {
  115. $params = $this->request->param();
  116. $mobile = $params['mobile'] ?? '';
  117. $captcha = $params['captcha'] ?? '';
  118. $invite_id = $params['invite_id'] ?? '';
  119. // 验证器
  120. $validate = new \app\api\validate\User();
  121. if (!$validate->check($params, [], 'mobilelogin')) {
  122. $this->error($validate->getError());
  123. }
  124. // 这里需要处理 测试环境 env('app_debug') 为 true 时,不进行验证码验证 校验 验证码固定为env的配置 DEFAULT_SMSCODE: 123456
  125. if (!Env::get('app.app_debug') && $captcha != Env::get('app.DEFAULT_SMSCODE')) {
  126. if (!Sms::check($mobile, $captcha, 'mobilelogin')) {
  127. $this->error(__('Captcha is incorrect'));
  128. }
  129. }
  130. $user = \app\common\model\User::getByMobile($mobile);
  131. if ($user) {
  132. if ($user->status != StatusEnum::ENABLED) {
  133. $this->error(__('Account is locked'));
  134. }
  135. //如果已经有账号则直接登录
  136. $ret = $this->auth->direct($user->id);
  137. } else {
  138. $ret = $this->auth->register($mobile, Random::alnum(), '', $mobile, []);
  139. }
  140. if ($ret) {
  141. Sms::flush($mobile, 'mobilelogin');
  142. $user = $this->auth->getUserinfo();
  143. $user['avatar'] = cdnurl($user['avatar'], true);
  144. $data = ['token' => $this->auth->getToken(), 'user' => $user];
  145. $this->success(__('Logged in successful'), $data);
  146. } else {
  147. $this->error($this->auth->getError());
  148. }
  149. }
  150. /**
  151. * 注册会员
  152. *
  153. * @param string $username 用户名
  154. * @param string $password 密码
  155. * @param string $email 邮箱
  156. * @param string $mobile 手机号
  157. * @param string $code 验证码
  158. */
  159. public function register()
  160. {
  161. $params = $this->request->param();
  162. $username = $params['username'] ?? '';
  163. $password = $params['password'] ?? '';
  164. $mobile = $params['mobile'] ?? '';
  165. $code = $params['captcha'] ?? '';
  166. // 验证器
  167. $validate = new \app\api\validate\User();
  168. if (!$validate->check($params, [], 'register')) {
  169. $this->error($validate->getError());
  170. }
  171. $invite_id = $this->request->post('invite_id');
  172. if ($invite_id) {
  173. \think\Cookie::set('inviter', $invite_id);
  174. }
  175. $ret = Sms::check($mobile, $code, 'register');
  176. if (!$ret) {
  177. $this->error(__('Captcha is incorrect'));
  178. }
  179. $ret = $this->auth->register($username, $password, '', $mobile, []);
  180. if ($ret) {
  181. $user = $this->auth->getUserinfo();
  182. $user['avatar'] = cdnurl($user['avatar'], true);
  183. $this->success(__('Sign up successful'), [
  184. 'token' => $this->auth->getToken(),
  185. 'user' => $user
  186. ]);
  187. } else {
  188. $this->error($this->auth->getError());
  189. }
  190. }
  191. /**
  192. * 第三方登录[绑定] 小程序
  193. */
  194. public function wxLogin()
  195. {
  196. $code = $this->request->post("code");
  197. $rawData = $this->request->post("rawData/a", '', 'trim');
  198. if (!$code) {
  199. $this->error("参数不正确");
  200. }
  201. $rawData = $rawData ?: ['nickName' => '微信用户'];
  202. $third = get_addon_info('third');
  203. if (!$third || !$third['state']) {
  204. $this->error("请在后台插件管理安装第三方登录插件并启用");
  205. }
  206. if (!config('shop.wx_appid') || !config('shop.wx_app_secret')) {
  207. $this->error("请在后台配置微信小程序参数");
  208. }
  209. $json = (new \addons\shop\library\Wechat\Service())->getWechatSession($code);
  210. if (isset($json['openid'])) {
  211. $userinfo = [
  212. 'platform' => 'wechat',
  213. 'apptype' => 'miniapp',
  214. 'openid' => $json['openid'],
  215. 'userinfo' => [
  216. 'nickname' => $rawData['nickName'] ?? '',
  217. 'avatar' => $rawData['avatarUrl'] ?? ''
  218. ],
  219. 'openname' => $rawData['nickName'] ?? '',
  220. 'access_token' => $json['session_key'],
  221. 'refresh_token' => '',
  222. 'expires_in' => $json['expires_in'] ?? 0,
  223. 'unionid' => $json['unionid'] ?? ''
  224. ];
  225. $third = [
  226. 'nickname' => $rawData['nickName'] ?? '',
  227. 'avatar' => $rawData['avatarUrl'] ?? ''
  228. ];
  229. $user = null;
  230. if ($this->auth->isLogin() || Service::isBindThird($userinfo['platform'], $userinfo['openid'])) {
  231. Service::connect($userinfo['platform'], $userinfo);
  232. } else {
  233. // 是否自动创建账号
  234. if (config('shop.wechatautocreate')) {
  235. Service::connect($userinfo['platform'], $userinfo);
  236. } else {
  237. Session::set('third-userinfo', $userinfo);
  238. $this->success('授权成功!', ['third' => $third, 'openid' => $json['openid'], 'bind' => true]);
  239. }
  240. }
  241. $user = $this->auth->getUserinfo();
  242. $this->success('授权成功!', ['user' => $user, 'third' => $third, 'openid' => $json['openid']]);
  243. }
  244. $this->error("授权失败," . ($json['errmsg'] ?? "未知错误"));
  245. }
  246. // 抖音登录
  247. public function douyinLogin()
  248. {
  249. $encryptedData = $this->request->post('encryptedData','','trim');
  250. $iv = $this->request->post('iv');
  251. $code = $this->request->post('code');
  252. $anonymous_code = $this->request->post('anonymousCode');
  253. if (empty($code)) {
  254. $this->error('请求数据不能为空');
  255. }
  256. if ($iv) {
  257. $iv = urldecode($iv);
  258. }
  259. if ($encryptedData){
  260. $encryptedData = urldecode($encryptedData);
  261. }
  262. $res = (new \app\common\library\Douyin\Service())->getUserInfo($code ,$anonymous_code,$encryptedData,$iv);
  263. if (empty($res)){
  264. $this->error("授权失败");
  265. }elseif(isset($res['err_no']) && $res['err_no'] != 0){
  266. $this->error("授权失败:".$res['err_tips']);
  267. }
  268. $msg = $res['err_no'] ?? '授权失败';
  269. $openId = $res['data']['openid'] ?? "";
  270. $mobile = isset($res['data']['mobile'])?$res['data']['mobile']:'';
  271. if (!empty($openId)) {
  272. $userinfo = [
  273. 'platform' => 'douyin',
  274. 'apptype' => ChannelEnum::CHANNEL_DOUYIN_MINI_PROGRAM,
  275. 'openid' => $openId,
  276. 'userinfo' => [
  277. 'nickname' => $rawData['nickName'] ?? '',
  278. 'avatar' => $rawData['avatarUrl'] ?? ''
  279. ],
  280. 'openname' => $rawData['nickName'] ?? '',
  281. 'access_token' => $res['data']['session_key'] ?? '',
  282. 'refresh_token' => '',
  283. 'expires_in' => $res['data']['expires_in'] ?? 0,
  284. 'unionid' => $res['data']['unionid'] ?? ''
  285. ];
  286. $third = [
  287. 'nickname' => $rawData['nickName'] ?? '',
  288. 'avatar' => $rawData['avatarUrl'] ?? ''
  289. ];
  290. $user = null;
  291. if ($this->auth->isLogin() || Service::isBindThird($userinfo['platform'], $userinfo['openid'])) {
  292. Service::connect($userinfo['platform'], $userinfo);
  293. } else {
  294. Service::connect($userinfo['platform'], $userinfo);
  295. }
  296. $user = $this->auth->getUserinfo();
  297. $this->success('授权成功!', ['user' => $user, 'third' => $third, 'openid' => $openId]);
  298. }
  299. $this->error("授权失败," . ( $msg));
  300. }
  301. /**
  302. * 微信手机号授权登录
  303. */
  304. public function wechatMobileLogin()
  305. {
  306. $code = $this->request->post("code");
  307. $logincode = $this->request->post("logincode");
  308. $bind = $this->request->post("bind");
  309. $data = (new \app\common\library\Wechat\Service())->getWechatMobile($code);
  310. if ($data) {
  311. $mobile = $data['phoneNumber'];
  312. //获取openid和unionid
  313. $json = (new \app\common\library\Wechat\Service())->getWechatSession($logincode);
  314. $openid = $json['openid'] ?? '';
  315. $unionid = $json['unionid'] ?? '';
  316. $user = \app\common\model\User::getByMobile($mobile);
  317. if ($user) {
  318. if ($user->status != StatusEnum::ENABLED) {
  319. $this->error(__('Account is locked'));
  320. }
  321. //如果已经有账号则直接登录
  322. $ret = $this->auth->direct($user->id);
  323. } else {
  324. $ret = $this->auth->register($mobile, Random::alnum(), '', $mobile, ['nickname' => substr_replace($mobile, '****', 3, 4)]);
  325. }
  326. //判断是否绑定模式,openid是否有关联,没有关联的情况下手动进行关联
  327. if ($bind && $openid) {
  328. if (Service::isBindThird('wechat', $openid)) {
  329. $this->error("手机号已经绑定其它账号");
  330. }
  331. // 在第三方登录表中创建关联
  332. $values = ['user_id' => $this->auth->id, 'platform' => 'wechat', 'openid' => $openid, 'unionid' => $unionid, 'openname' => '微信用户', 'apptype' => 'miniapp'];
  333. Third::create($values, true);
  334. }
  335. if ($ret) {
  336. $data = ['user' => $this->auth->getUserinfo(), 'token' => $this->auth->getToken(), 'openid' => $openid];
  337. $this->success('授权成功!', $data);
  338. } else {
  339. $this->error($this->auth->getError());
  340. }
  341. } else {
  342. $this->error("授权失败,请重试");
  343. }
  344. }
  345. /**
  346. * APP登录
  347. */
  348. public function appLogin()
  349. {
  350. $code = $this->request->post("code");
  351. $scope = $this->request->post("scope");
  352. if (!$code) {
  353. $this->error("参数不正确");
  354. }
  355. $third = get_addon_info('third');
  356. if (!$third || !$third['state']) {
  357. $this->error("请在后台插件管理安装第三方登录插件并启用");
  358. }
  359. Session::set('state', $code);
  360. $config = [
  361. 'app_id' => Config::get('shop.app_id'),
  362. 'app_secret' => Config::get('shop.app_secret'),
  363. 'scope' => $scope
  364. ];
  365. if (!$config['app_id'] || !$config['app_secret']) {
  366. $this->error("请在后台配置移动端APP参数");
  367. }
  368. $wechat = new \addons\third\library\Wechat($config);
  369. $userinfo = $wechat->getUserInfo(['code' => $code, 'state' => $code]);
  370. if (!$userinfo) {
  371. $this->error(__('操作失败'));
  372. }
  373. //判断是否需要绑定
  374. $userinfo['apptype'] = 'native';
  375. $userinfo['platform'] = 'wechat';
  376. $third = [
  377. 'avatar' => $userinfo['userinfo']['avatar'],
  378. 'nickname' => $userinfo['userinfo']['nickname']
  379. ];
  380. $user = null;
  381. if ($this->auth->isLogin() || Service::isBindThird($userinfo['platform'], $userinfo['openid'], $userinfo['apptype'], $userinfo['unionid'])) {
  382. Service::connect($userinfo['platform'], $userinfo);
  383. $user = $this->auth->getUserinfo();
  384. } else {
  385. Session::set('third-userinfo', $userinfo);
  386. }
  387. $this->success('授权成功!', ['user' => $user, 'third' => $third]);
  388. }
  389. /**
  390. * 获取Openid(仅用于微信小程序)
  391. */
  392. public function getWechatOpenid()
  393. {
  394. $code = $this->request->post("logincode");
  395. $json = (new \app\common\library\Wechat\Service())->getWechatSession($code);
  396. $this->success('获取成功!', ['openid' => $json['openid'] ?? '']);
  397. }
  398. }