User.php 12 KB

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