PageRenderTime 60ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/service/user/srv/PwFindPassword.php

https://github.com/cuijinquan/nextwind
PHP | 323 lines | 153 code | 22 blank | 148 comment | 36 complexity | 61ea7d80809103bd5778ce7d790c0d1d MD5 | raw file
  1. <?php
  2. Wind::import('LIB:utility.PwMail');
  3. Wind::import('SRV:user.dm.PwUserInfoDm');
  4. /**
  5. * 用户找回密码服务
  6. *
  7. * @author xiaoxia.xu <xiaoxia.xuxx@aliyun-inc.com>
  8. * @copyright ©2003-2103 phpwind.com
  9. * @license http://www.phpwind.com
  10. * @version $Id: PwFindPassword.php 24177 2013-01-22 10:36:09Z xiaoxia.xuxx $
  11. * @package src.service.user.srv
  12. */
  13. class PwFindPassword {
  14. /* @var $byMobileNum int 用手机找回密码可以使用的次数限制 */
  15. private $byMobileNum = 5;
  16. /* @var $byEmaialNum int 用邮箱找回密码可以使用的次数限制 */
  17. private $byEmailNum = 5;
  18. /* @var $spaceDay int 达到次数上限的时候相隔下次可以使用的时间间隔(单位:天)*/
  19. private $spaceDay = 1;
  20. private $info = array();
  21. const WAY_EMAIL = 'email';
  22. const WAY_MOBILE = 'mobile';
  23. /**
  24. * 构造函数
  25. *
  26. * @param string $username
  27. */
  28. public function __construct($username) {
  29. /* @var $userDs PwUser */
  30. $userDs = Wekit::load('user.PwUser');
  31. $info = $userDs->getUserByName($username, PwUser::FETCH_MAIN | PwUser::FETCH_DATA | PwUser::FETCH_INFO);
  32. if (!$info) {
  33. $info = $this->_getWindid()->getUser($username, 2);
  34. if (!$info) return;
  35. Wind::import('SRV:user.srv.PwRegisterService');
  36. $registerService = new PwRegisterService();
  37. $info = $registerService->sysUser($info['uid']);
  38. if ($info) {
  39. $info = array_merge($info, $this->_getUserDs()->getUserByUid($info['uid'], PwUser::FETCH_INFO));
  40. }
  41. }
  42. $this->info = $info;
  43. }
  44. /**
  45. * 检查邮箱是否正确
  46. *
  47. * @param string $email 邮箱
  48. * @return boolean|PwError
  49. */
  50. public function checkEmail($email) {
  51. if (!$this->info) return new PwError('USER:illegal.request');
  52. if ($this->info['email'] != $email) {
  53. return new PwError('USER:findpwd.error.email');
  54. }
  55. if (true !== ($check = $this->allowFindBy(self::WAY_EMAIL))) return $check;
  56. return true;
  57. }
  58. /**
  59. * 发送重置邮件
  60. *
  61. * @param string $state 加密串
  62. * @return boolean
  63. */
  64. public function sendResetEmail($state) {
  65. if (true !== ($check = $this->allowFindBy(self::WAY_EMAIL))) return $check;
  66. //TODO 产生激活码的方法
  67. $code = substr(md5(Pw::getTime()), mt_rand(1, 8), 8);
  68. $url = WindUrlHelper::createUrl('u/findPwd/resetpwd', array('code' => $code, '_statu' => $state));
  69. list($title, $content) = $this->_buildTitleAndContent($this->info['username'], $url);
  70. /* @var $activeCodeDs PwUserActiveCode */
  71. $activeCodeDs = Wekit::load('user.PwUserActiveCode');
  72. $activeCodeDs->addActiveCode($this->info['uid'], $this->info['email'], $code, Pw::getTime(), PwUserActiveCode::RESETPWD);
  73. $mail = new PwMail();
  74. $mail->sendMail($this->info['email'], $title, $content);
  75. return true;
  76. }
  77. /**
  78. * 重置的邮箱验证码是否有效
  79. *
  80. * @param string $email 重置的email地址
  81. * @param string $code 重置码
  82. * @return PwError|boolean
  83. */
  84. public function checkResetEmail($email, $code) {
  85. if (empty($this->info) || $this->info['email'] != $email) {
  86. return new PwError('USER:illegal.request');
  87. }
  88. /* @var $activeCodeDs PwUserActiveCode */
  89. $activeCodeDs = Wekit::load('user.PwUserActiveCode');
  90. $info = $activeCodeDs->getInfoByUid($this->info['uid'], PwUserActiveCode::RESETPWD);
  91. if (!$info || $info['email'] != $email || $info['code'] != $code) return new PwError("USER:findpwd.email.code.expired");
  92. /*找回密码:验证码不需要过期及验证机制*/
  93. if ($info['active_time'] > 0 ) return new PwError('USER:findpwd.email.code.expired');
  94. /*$validTime = $this->activeCodeValidTime * 3600;
  95. if (($info['send_time'] + $validTime) < Pw::getTime()) return new PwError('USER:active.email.overtime');*/
  96. // $activeCodeDs->activeCode($this->info['uid'], Pw::getTime());
  97. return true;
  98. }
  99. /**
  100. * 获得信息的标题和内容
  101. *
  102. * @param string $titleKey 标题key
  103. * @param string $contentKey 内容key
  104. * @param string $username 用户名
  105. * @param string $url 链接地址
  106. * @return array
  107. */
  108. private function _buildTitleAndContent($username, $url = '') {
  109. $search = array('{username}', '{sitename}');
  110. $replace = array($username, Wekit::C('site', 'info.name'));
  111. $title = str_replace($search, $replace, Wekit::C('login', 'resetpwd.mail.title'));
  112. $search[] = '{time}';
  113. $search[] = '{url}';
  114. $replace[] = Pw::time2str(Pw::getTime(), 'Y-m-d H:i:s');
  115. $replace[] = $url ? sprintf('<a href="%s">%s</a>', $url, $url) : '';
  116. $content = str_replace($search, $replace, Wekit::C('login', 'resetpwd.mail.content'));
  117. return array($title, $content);
  118. }
  119. /**
  120. * 获得邮箱的模糊显示
  121. *
  122. * @return string
  123. */
  124. public function getFuzzyEmail() {
  125. $email = $this->info['email'];
  126. $info = explode('@', $email);
  127. $info[0] = $info[0][0] . str_repeat('*', strlen($info[0]) - 1);
  128. return implode('@', $info);
  129. }
  130. /**
  131. * 获得email链接地址
  132. *
  133. * @return string
  134. */
  135. public function getEmailUrl() {
  136. $email = $this->info['email'];
  137. $info = explode('@', $email);
  138. return 'http://mail.' . $info[1];
  139. }
  140. /**
  141. * 是否可以使用邮箱找回密码
  142. *
  143. * @return boolean
  144. */
  145. public function allowFindByMail() {
  146. if (false === $this->isBindMail()) return new PwError('USER:findpwd.notbind.email');
  147. return $this->allowFindBy(self::WAY_EMAIL);
  148. }
  149. /**
  150. * 是否可以使用手机号码找回密码
  151. *
  152. * @return boolean
  153. */
  154. public function allowFindByMobile() {
  155. if (false === $this->isBindMobile()) return new PwError('USER:findpwd.notbind.mobile');
  156. return $this->allowFindBy(self::WAY_MOBILE);
  157. }
  158. /**
  159. * 是否绑定email
  160. *
  161. * @return boolean
  162. */
  163. public function isBindMail() {
  164. return $this->info['email'] ? true : false;
  165. }
  166. /**
  167. * 是否绑定手机号码
  168. *
  169. * @return boolean
  170. */
  171. public function isBindMobile() {
  172. return $this->info['mobile'] ? true : false;
  173. }
  174. /**
  175. * 判断当天通过邮箱找回密码是否已经超过次数限制
  176. *
  177. * @return boolean
  178. */
  179. public function isOverByMail() {
  180. return $this->allowFindBy(self::WAY_EMAIL) instanceof PwError ? true : false;
  181. }
  182. /**
  183. * 判断当天通过手机找回密码是否已经超过次数限制
  184. *
  185. * @return boolean
  186. */
  187. public function isOverByMobile() {
  188. return $this->allowFindBy(self::WAY_MOBILE) instanceof PwError ? true : false;
  189. }
  190. /**
  191. * 更改成功
  192. *
  193. * @param string $type
  194. * @return boolean
  195. */
  196. public function success($type) {
  197. //更新找回密码验证码
  198. /* @var $activeCodeDs PwUserActiveCode */
  199. $activeCodeDs = Wekit::load('user.PwUserActiveCode');
  200. $activeCodeDs->activeCode($this->info['uid'], Pw::getTime());
  201. return $this->setRecode($type);
  202. }
  203. /**
  204. * 创建找回密码的唯一标识
  205. *
  206. * @param string $username 需要找回密码的用户名
  207. * @param string $way 找回方式标识
  208. * @param string $value 找回方式对应的值
  209. * @return string
  210. */
  211. public static function createFindPwdIdentify($username, $way, $value) {
  212. $code = Pw::encrypt($username . '|' . $way . '|' . $value, Wekit::C('site', 'hash') . '___findpwd');
  213. return rawurlencode($code);
  214. }
  215. /**
  216. * 解析找回密码的标识
  217. *
  218. * @param string $identify
  219. * @return array array($username, $way, $value)
  220. */
  221. public static function parserFindPwdIdentify($identify) {
  222. return explode("|", Pw::decrypt(rawurldecode($identify), Wekit::C('site', 'hash') . '___findpwd'));
  223. }
  224. /**
  225. * 根据方式获取对应的用户字段
  226. *
  227. * @param string $way
  228. * @return string
  229. */
  230. public static function getField($way) {
  231. return $way == self::WAY_MOBILE ? 'mobile' : 'email';
  232. }
  233. /**
  234. * 获得尝试错误记录
  235. *
  236. * 在findpwd中保存格式为:0000-00-00:num|0000-00-00:num
  237. * 0000-00-00:最后更新该记录的时间
  238. * num : 最后更新记录的时候此种方式已经尝试的次数
  239. * |: 在|左侧的,是用“邮箱”方式找回的记录,在|右侧,是用“手机”方式找回的记录
  240. * @param string $type 找回类型
  241. * @return boolean|PwError
  242. */
  243. private function allowFindBy($type = self::WAY_EMAIL) {
  244. $findPwd = $this->info['findpwd'];
  245. if (!$findPwd) return true;
  246. $typeCode = $type == self::WAY_MOBILE ? 0 : 1;
  247. $recodes = explode('|', $findPwd);
  248. if (empty($recodes[$typeCode])) return true;
  249. $tryNum = $type == self::WAY_MOBILE ? $this->byMobileNum : $this->byEmailNum;
  250. list($time, $num) = explode(':', $recodes[$typeCode]);
  251. if (($time != Pw::time2str(Pw::getTime(), 'Y-m-d')) || $num < $tryNum) return true;
  252. return new PwError('USER:findpwd.over.limit.' . $type);
  253. }
  254. /**
  255. * 设置重新找回记录
  256. *
  257. * 在findpwd中保存格式为:0000-00-00:num|0000-00-00:num
  258. * 0000-00-00:最后更新该记录的时间
  259. * num : 最后更新记录的时候此种方式已经尝试的次数
  260. * |: 在|左侧的,是用“邮箱”方式找回的记录,在|右侧,是用“手机”方式找回的记录
  261. * @param string $type
  262. * @return true
  263. */
  264. public function setRecode($type = self::WAY_EMAIL) {
  265. $findPwd = $this->info['findpwd'];
  266. $recodes = array(0 => '', 1 => '');
  267. $typeCode = $type == self::WAY_MOBILE ? 0 : 1;
  268. $tryTime = $type == self::WAY_MOBILE ? $this->byMobileNum : $this->byEmailNum;
  269. /*如果重来没有尝试找回过密码*/
  270. if (!$findPwd) {
  271. $recodes[$typeCode] = Pw::time2str(Pw::getTime(), 'Y-m-d') . ':1';
  272. } else {
  273. list($recodes[0], $recodes[1]) = explode('|', $findPwd);
  274. /*如果该方式的找回密码方式没有尝试过*/
  275. if (!($recode = $recodes[$typeCode])) {
  276. $recodes[$typeCode] = Pw::time2str(Pw::getTime(), 'Y-m-d') . ':1';
  277. /*如果该方式的找回密码方式尝试过*/
  278. } else {
  279. list($time, $num) = explode(':', $recode);
  280. /*如果该方式的上次找回密码不是在今天发生,那么这是今天第一次找回密码*/
  281. if (($time != Pw::time2str(Pw::getTime(), 'Y-m-d'))) {
  282. $recodes[$typeCode] = Pw::time2str(Pw::getTime(), 'Y-m-d') . ':1';
  283. /*如果今天不是第一次尝试找回密码,并且今天尝试找回密码的次数已经超过规定次数,抛出错误*/
  284. } elseif ($num >= $tryTime) {
  285. return new PwError('USER:findpwd.over.limit.' . $type);
  286. /*否则记录今天找回密码的次数*/
  287. } else {
  288. $recodes[$typeCode] = $time . ':' . ($num + 1);
  289. }
  290. }
  291. }
  292. /* @var $userDs PwUser */
  293. $userDs = Wekit::load('user.PwUser');
  294. $userdm = new PwUserInfoDm($this->info['uid']);
  295. $userdm->setFindpwd(implode('|', $recodes));
  296. return $userDs->editUser($userdm, PwUser::FETCH_DATA);
  297. }
  298. protected function _getWindid() {
  299. return WindidApi::api('user');
  300. }
  301. }