User.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. <?php
  2. namespace app\controller;
  3. use app\BaseController;
  4. use app\model\ConfigModel;
  5. use app\model\LinkFolderModel;
  6. use app\model\SettingModel;
  7. use app\model\TokenModel;
  8. use app\model\UserGroupModel;
  9. use app\model\UserModel;
  10. use think\facade\Cache;
  11. use think\facade\Db;
  12. use think\facade\Log;
  13. use think\facade\View;
  14. class User extends BaseController
  15. {
  16. protected $qq_bind_mode = false;
  17. public function login(): \think\response\Json
  18. {
  19. $user = $this->request->post('username', '0');
  20. $pass = $this->request->post('password', '0');
  21. $user = trim($user);
  22. $pass = trim($pass);
  23. $info = UserModel::where('mail', $user)->find();
  24. if (Cache::get('login.' . $user)) {
  25. return $this->error('账号已被安全锁定,您可以修改密码然后登录');
  26. }
  27. if (!$info) {
  28. return $this->error('账号不存在');
  29. }
  30. if ($info['login_fail_count'] == 10) {
  31. Cache::set('login.' . $user, 'lock', 7200);
  32. $info->login_fail_count = 0;
  33. $info->save();
  34. return $this->error('账号已被锁定2小时');
  35. }
  36. if ($info['password'] != md5($pass)) {
  37. $info->login_fail_count += 1;
  38. $info->save();
  39. return $this->error('账号不存在或密码错误');
  40. }
  41. if ($info['status'] === 1) {
  42. return $this->error('账号已被冻结');
  43. }
  44. $auth = $this->refreshToken($info);
  45. $info->login_ip = getRealIp();
  46. $info->login_time = date('Y-m-d H:i:s');
  47. $info->login_fail_count = 0; //登陆成功将失败次数归零
  48. $info->save();
  49. return $this->success('登录成功', $auth);
  50. }
  51. private function refreshToken($info): array
  52. {
  53. $token = renderToken($info['id']);
  54. $agent = $this->request->header('User-Agent');
  55. $agent = mb_substr($agent, 0, 250);
  56. $auth = ['user_id' => $info['id'], 'token' => $token, 'create_time' => time(), 'ip' => getRealIp(), 'user_agent' => $agent];
  57. if (isset($info['access_token'])) {
  58. $auth['access_token'] = $info['access_token'];
  59. }
  60. TokenModel::insert($auth);
  61. unset($auth['user_agent']);
  62. unset($auth['access_token']);
  63. unset($auth['ip']);
  64. return $auth;
  65. }
  66. function register(): \think\response\Json
  67. {
  68. if ($this->systemSetting("user_register", '0', true) === '1') {
  69. return $this->error('管理员已关闭用户注册功能');
  70. }
  71. $user = $this->request->post('username', false);
  72. $pass = $this->request->post('password', false);
  73. $code = $this->request->post('code', '0000');
  74. if ($user && $pass) {
  75. $user = trim($user);
  76. $pass = trim($pass);
  77. if (!validateEmail($user)) {
  78. return $this->error('邮箱格式错误');
  79. }
  80. if (strlen($pass) < 6) {
  81. return $this->error('密码过短');
  82. }
  83. $cacheCode = Cache::get('code' . $user);
  84. if (!$cacheCode || $cacheCode != $code) {
  85. return $this->error('验证码错误');
  86. }
  87. if (UserModel::where('mail', $user)->field('id,mail')->find()) {
  88. return $this->error('账号已存在');
  89. }
  90. $add = UserModel::insert(['mail' => $user, 'password' => md5($pass), 'create_time' => date('Y-m-d H:i:s'), 'register_ip' => getRealIp()]);
  91. if ($add) {
  92. Cache::delete('code' . $user);
  93. return $this->success('ok');
  94. }
  95. }
  96. return $this->error('注册失败');
  97. }
  98. public function forgetPass(): \think\response\Json
  99. {
  100. $user = $this->request->post('username', false);
  101. $pass = $this->request->post('password', false);
  102. $code = $this->request->post('code', '0000');
  103. if ($user && $pass) {
  104. $user = trim($user);
  105. $pass = trim($pass);
  106. if (!validateEmail($user)) {
  107. return $this->error('邮箱格式错误');
  108. }
  109. if (strlen($pass) < 6) {
  110. return $this->error('密码过短');
  111. }
  112. $info = UserModel::where('mail', $user)->field('id,mail')->find();
  113. if (!$info) {
  114. return $this->error('账号不存在');
  115. }
  116. $cacheCode = Cache::get('code' . $user);
  117. if ($cacheCode && $cacheCode == $code) {
  118. $info->password = md5($pass);
  119. $add = $info->save();
  120. if ($add) {
  121. TokenModel::where('user_id', $info['id'])->delete(); //删除所有登录记录
  122. Cache::delete('login.' . $user);
  123. return $this->success('ok');
  124. }
  125. } else {
  126. return $this->error('验证码错误');
  127. }
  128. }
  129. return $this->error('修改失败');
  130. }
  131. function newMail(): \think\response\Json
  132. {
  133. $userinfo = $this->getUser(true);
  134. $user = $this->request->post('mail', false);
  135. $code = $this->request->post('code', false);
  136. if ($user && $code) {
  137. $user = trim($user);
  138. if (!validateEmail($user)) {
  139. return $this->error('邮箱格式错误');
  140. }
  141. $cacheCode = Cache::get('code' . $user);
  142. if ($cacheCode && $cacheCode == $code) {
  143. $info = UserModel::where('mail', $user)->field('id,mail')->find();
  144. if ($info) {
  145. return $this->error('该邮箱已被使用!');
  146. }
  147. $info = UserModel::where('id', $userinfo['user_id'])->field('id,mail')->find();
  148. $info->mail = $user;
  149. $info->save();
  150. Cache::delete('code' . $user);
  151. return $this->success('修改成功');
  152. } else {
  153. return $this->error('验证码错误');
  154. }
  155. }
  156. return $this->error('请认真填写表单');
  157. }
  158. function loginOut(): \think\response\Json
  159. {
  160. $user = $this->getUser();
  161. if ($user) {
  162. TokenModel::where('user_id', $user['user_id'])->where('token', $user['token'])->delete();
  163. }
  164. return $this->success('ok');
  165. }
  166. public function get(): \think\response\Json
  167. {
  168. $info = $this->getUser(true);
  169. if ($info) {
  170. $info = UserModel::field('id,mail,manager,nickname,avatar,qq_open_id,active')->find($info['user_id']);
  171. if ($info['qq_open_id']) {
  172. $info['qqBind'] = true;
  173. unset($info['qq_open_id']);
  174. }
  175. if ($info['active'] !== date("Y-m-d")) {
  176. $info['active'] = date("Y-m-d");
  177. $info->save();
  178. }
  179. return $this->success('ok', $info);
  180. }
  181. return $this->error('获取失败');
  182. }
  183. public function unbindQQ(): \think\response\Json
  184. {
  185. $info = $this->getUser(true);
  186. if ($info) {
  187. $info = UserModel::field('id,mail,manager,nickname,avatar,qq_open_id')->find($info['user_id']);
  188. if (empty($info->mail)) {
  189. return $this->error("请先绑定邮箱后再解绑");
  190. }
  191. $info->qq_open_id = "";
  192. $info->save();
  193. }
  194. return $this->success('解绑成功', $info);
  195. }
  196. public function updateInfo(): \think\response\Json
  197. {
  198. $info = $this->getUser(true);
  199. $field = $this->request->post('field', false);
  200. $value = $this->request->post('value', false);
  201. //允许修改的字段
  202. $allow = ['nickname', 'avatar'];
  203. if ($info && $field && $value && in_array($field, $allow)) {
  204. UserModel::where('id', $info['user_id'])->update([$field => $value]);
  205. }
  206. return $this->success('修改成功');
  207. }
  208. function qLogin(): \think\response\Redirect
  209. {
  210. $appId = SettingModel::Config('qq_login_appid', false);
  211. $callback = 'https://' . $this->request->host() . '/qq_login';
  212. $type = $this->request->get('type', '');
  213. $query = [
  214. 'redirect_uri' => $callback,
  215. 'state' => md5(uniqid()),
  216. 'response_type' => 'code',
  217. 'scope' => 'get_user_info,list_album,upload_pic',
  218. 'client_id' => $appId
  219. ];
  220. if ($type === 'bind') {
  221. $query['state'] = $query['state'] . 'bind';
  222. }
  223. $http = http_build_query($query);
  224. return redirect('https://graph.qq.com/oauth2.0/authorize?' . $http);
  225. }
  226. function qq_login(): string
  227. {
  228. $appId = SettingModel::Config('qq_login_appid', false);
  229. $code = $this->request->get('code', false);
  230. $state = $this->request->get('state');
  231. if (strpos($state, 'bind')) {
  232. //绑定模式
  233. $this->qq_bind_mode = true;
  234. }
  235. $callback = 'https://' . $this->request->host() . '/qq_login';
  236. $result = \Axios::http()->get('https://graph.qq.com/oauth2.0/token', [
  237. 'query' => [
  238. 'grant_type' => 'authorization_code',
  239. 'client_id' => $appId,
  240. 'client_secret' => SettingModel::Config('qq_login_appkey', false),
  241. 'code' => $code,
  242. 'redirect_uri' => $callback,
  243. 'fmt' => 'json'
  244. ]
  245. ]);
  246. if ($result->getStatusCode() === 200) {
  247. $content = $result->getBody()->getContents();
  248. $js = \Axios::toJson($content);
  249. if (isset($js['access_token'])) {
  250. $access_token = $js['access_token'];
  251. return $this->getOpenId($access_token);
  252. }
  253. }
  254. return View::fetch('/qq_login_error');
  255. }
  256. //此方法禁止网络访问
  257. private function getOpenId($access_token): string
  258. {
  259. $result = \Axios::http()->get('https://graph.qq.com/oauth2.0/me', [
  260. 'query' => [
  261. 'access_token' => $access_token,
  262. 'fmt' => 'json'
  263. ]
  264. ]);
  265. if ($result->getStatusCode() === 200) {
  266. $content = $result->getBody()->getContents();
  267. $js = \Axios::toJson($content);
  268. if (isset($js['openid'])) {
  269. $openid = $js['openid'];
  270. if ($this->qq_bind_mode) {
  271. //绑定模式
  272. if (UserModel::where('qq_open_id', $openid)->field('id,qq_open_id')->find()) {
  273. return View::fetch('/qq_login_error');
  274. }
  275. //如果openid数据库不存在说明QQ没有被绑定过,可以绑定
  276. $this->BindQQ($openid); //绑定后需要替换Token,不然之前的QQ登录会失效
  277. }
  278. $info = UserModel::where('qq_open_id', $openid)->find();
  279. if (!$info) { //不存在就创建一个新用户,如果上一个步骤绑定成功的话,是不可能进入此步骤的
  280. UserModel::insert(['mail' => '', 'password' => md5(time()), 'create_time' => date('Y-m-d H:i:s'), 'register_ip' => getRealIp(), 'qq_open_id' => $openid]);
  281. $info = UserModel::where('qq_open_id', $openid)->find();
  282. $this->getUserOpenInfo($access_token, $openid); //获取一些用户的默认信息
  283. }
  284. if ($info) { //如果用户存在
  285. $info->login_ip = getRealIp();
  286. $info->login_time = date('Y-m-d H:i:s');
  287. $info->login_fail_count = 0; //登陆成功将失败次数归零
  288. $info->save();
  289. $info['access_token'] = $access_token;
  290. $auth = $this->refreshToken($info);
  291. if ($info['status'] === 1) {
  292. return View::fetch('/qq_login_error');
  293. }
  294. return View::fetch('/qq_login', ['info' => $auth]);
  295. }
  296. }
  297. }
  298. return View::fetch('/qq_login_error');
  299. }
  300. private function BindQQ($qq_open_id)
  301. {
  302. $user = $this->getUser();
  303. if ($user) {
  304. $info = UserModel::where('id', $user['user_id'])->field('id,mail,qq_open_id,password,login_fail_count,login_ip,login_time')->find();
  305. if ($info) {
  306. $info->qq_open_id = $qq_open_id;
  307. $info->save();
  308. }
  309. }
  310. }
  311. private function getUserOpenInfo($access_token, $openid)
  312. {
  313. $result = \Axios::http()->get('https://graph.qq.com/user/get_user_info', [
  314. 'query' => [
  315. 'openid' => $openid,
  316. 'oauth_consumer_key' => SettingModel::Config('qq_login_appid', false),
  317. 'access_token' => $access_token
  318. ]
  319. ]);
  320. if ($result->getStatusCode() === 200) {
  321. $content = $result->getBody()->getContents();
  322. $js = \Axios::toJson($content);
  323. if ($js['ret'] === 0) {
  324. UserModel::where('qq_open_id', $openid)->update(['nickname' => $js['nickname'], 'avatar' => $js['figureurl_qq_1']]);
  325. }
  326. }
  327. }
  328. function UserGroup(): \think\response\Json
  329. {
  330. is_demo_mode(true);
  331. $this->getAdmin();
  332. return $this->success("ok", UserGroupModel::order("sort", 'desc')->select()->toArray());
  333. }
  334. function createGroup(): \think\response\Json
  335. {
  336. is_demo_mode(true);
  337. $type = $this->request->post('type', false);
  338. $this->getAdmin();
  339. if ($type === 'edit') {
  340. $form = $this->request->post('info');
  341. $id = $this->request->post('info.id', false);
  342. if ($id && $id > 0) {
  343. $model = UserGroupModel::find($id);
  344. $model->update($form);
  345. } else {
  346. $model = new UserGroupModel();
  347. $model->insert($form);
  348. }
  349. } else if ($type === 'del') {
  350. $id = $this->request->post('id');
  351. $result = UserGroupModel::where('id', $id)->find();
  352. if ($result) {
  353. $result->delete();
  354. UserModel::where('group_id', $id)->update(['group_id' => 0]);
  355. }
  356. }
  357. return $this->success('处理完毕!');
  358. }
  359. function sortGroup(): \think\response\Json
  360. {
  361. $sort = (array)$this->request->post();
  362. foreach ($sort as $key => $value) {
  363. UserGroupModel::where('id', $value['id'])->update(['sort' => $value['sort']]);
  364. }
  365. return $this->success('ok');
  366. }
  367. }