User.php 13 KB

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