UserAuth.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: leo
  5. * Date: 2018/2/28
  6. * Time: 上午10:31
  7. */
  8. namespace backendApi\modules\v1\components;
  9. use backendApi\modules\v1\models\Admin;
  10. use backendApi\modules\v1\models\AdminRole;
  11. use backendApi\modules\v1\models\AdminToken;
  12. use backendApi\modules\v1\models\EmailLog;
  13. use common\components\Request;
  14. use common\helpers\Cache;
  15. use common\helpers\Date;
  16. use common\helpers\Email;
  17. use common\helpers\Form;
  18. use common\helpers\Log;
  19. use common\helpers\Tool;
  20. use common\models\UserInfo;
  21. use common\models\UserToken;
  22. use Yii;
  23. use yii\db\ActiveRecordInterface;
  24. use yii\helpers\Json;
  25. use yii\web\HttpException;
  26. use yii\web\IdentityInterface;
  27. use yii\web\User;
  28. class UserAuth extends User
  29. {
  30. private $_userId = null;
  31. private $_apiIdentity = null;
  32. private $_token = null;
  33. private $_userInfo = null;
  34. private $_device = null;
  35. private $_deviceInfo = null;
  36. /**
  37. * 初始化设备信息
  38. * @throws \yii\base\InvalidConfigException
  39. */
  40. public function init()
  41. {
  42. parent::init();
  43. $this->_device = Yii::$app->request->getDevice();
  44. $this->_deviceInfo = Yii::$app->request->getDeviceInfo();
  45. }
  46. /**
  47. * 首次以用户名和密码的方式登录
  48. * @param IdentityInterface $identity
  49. * @return bool
  50. * @throws HttpException
  51. */
  52. public function loginWithUAndP(IdentityInterface $identity){
  53. if ($this->beforeLogin($identity, false, 0)) {
  54. $id = $identity->getId();
  55. $ip = Yii::$app->getRequest()->getUserIP();
  56. $this->_userId = $identity['ID'];
  57. $this->_apiIdentity = $identity;
  58. $this->_userInfo = [
  59. 'id' => $identity['ID'],
  60. 'adminName' => $identity['ADMIN_NAME'],
  61. 'roleId' => $identity['ROLE_ID'],
  62. 'accessTokenUpdatedAt' => Date::nowTime(),
  63. 'ip' => $ip,
  64. ];
  65. Admin::updateAll(['LAST_LOGIN_IP' => $ip, 'LAST_LOGIN_AT' => Date::nowTime()], 'ID=:ID', [':ID'=>$identity['ID']]);
  66. $userToken = AdminToken::findOne(['ADMIN_ID' => $identity['ID']]);
  67. if(!$userToken){
  68. $userToken = new AdminToken();
  69. $userToken->ADMIN_ID = $identity['ID'];
  70. $userToken->CREATED_AT = Date::nowTime();
  71. $userToken->save();
  72. }
  73. $this->updateToken($userToken, $appType = $this->_device, $typeToken = 'access');
  74. $this->updateToken($userToken, $appType = $this->_device, $typeToken = 'refresh');
  75. $this->afterLogin($identity, false, 0);
  76. }
  77. return !$this->getIsGuest();
  78. }
  79. public function afterLogin($identity, $cookieBased, $duration)
  80. {
  81. parent::afterLogin($identity, $cookieBased, $duration);
  82. // 记录管理员登录日志
  83. // Log::adminLogin();
  84. }
  85. /**
  86. * 已AccessToken方式登录(即平时直接访问)
  87. * @param string $token
  88. * @param null $type
  89. * @return null|IdentityInterface
  90. */
  91. public function loginByAccessToken($token, $type = null)
  92. {
  93. /* @var $class IdentityInterface */
  94. $class = $this->identityClass;
  95. $userId = $this->_userId = $class::findIdentityByAccessToken($token, $type);
  96. if($userId){
  97. $this->_userInfo = [
  98. 'id' => $userId,
  99. 'adminName' => Yii::$app->tokenRedis->hget($token, 'ADMIN_NAME'),
  100. 'roleId' => Yii::$app->tokenRedis->hget($token, 'ROLE_ID'),
  101. 'accessTokenUpdatedAt' => Yii::$app->tokenRedis->hget($token, 'TOKEN_UPDATED_AT'),
  102. 'ip' => Yii::$app->getRequest()->getUserIP(),
  103. ];
  104. return $userId;
  105. } else {
  106. return null;
  107. }
  108. }
  109. /**
  110. * 用refreshToken生成新的accessToken和refreshToken
  111. * @param $refreshToken
  112. * @return bool
  113. * @throws HttpException
  114. */
  115. public function refreshToken($refreshToken){
  116. if(!$refreshToken){
  117. return false;
  118. }
  119. $userId = Yii::$app->tokenRedis->hget($refreshToken, 'ID');
  120. if(!$userId){
  121. return false;
  122. }
  123. $userToken = AdminToken::findOne(['ADMIN_ID' => $userId]);
  124. $this->updateToken($userToken, $appType = $this->_device, $typeToken = 'access', $userId);
  125. $this->updateToken($userToken, $appType = $this->_device, $typeToken = 'refresh', $userId);
  126. return true;
  127. }
  128. /**
  129. * 用refreshToken生成新的accessToken
  130. * @param $refreshToken
  131. * @return bool
  132. * @throws HttpException
  133. */
  134. public function refreshAccessToken($refreshToken){
  135. if(!$refreshToken){
  136. return false;
  137. }
  138. $userId = Yii::$app->tokenRedis->hget($refreshToken, 'ID');
  139. if(!$userId){
  140. return false;
  141. }
  142. $userToken = AdminToken::findOne(['ADMIN_ID' => $userId]);
  143. return $this->updateToken($userToken, $appType = $this->_device, $typeToken = 'access', $userId);
  144. }
  145. /**
  146. * 用refreshToken生成新的refreshToken
  147. * @param $refreshToken
  148. * @return bool
  149. * @throws HttpException
  150. */
  151. public function refreshRefreshToken($refreshToken){
  152. if(!$refreshToken){
  153. return false;
  154. }
  155. $userId = Yii::$app->tokenRedis->hget($refreshToken, 'ID');
  156. if(!$userId){
  157. return false;
  158. }
  159. $userToken = AdminToken::findOne(['ADMIN_ID' => $userId]);
  160. return $this->updateToken($userToken, $appType = $this->_device, $typeToken = 'refresh', $userId);
  161. }
  162. /**
  163. * 更新token 的具体方法
  164. * @param ActiveRecordInterface $userTokenModel
  165. * @param string $appType (pc|app)
  166. * @param string $typeToken
  167. * @param $userId
  168. * @return bool
  169. * @throws HttpException
  170. */
  171. public function updateToken(ActiveRecordInterface $userTokenModel, $appType = Request::DEVICE_PC, $typeToken = 'access', $userId = 0){
  172. $tokenField = strtoupper($appType.'_'.$typeToken.'_TOKEN');
  173. $updateField = '';
  174. $expiresIn = 0;
  175. if($appType === Request::DEVICE_PC){
  176. if($typeToken === 'access'){
  177. $updateField = 'PAT_UPDATED_AT';
  178. $expiresIn = Yii::$app->params['backAccessTokenExpiresIn'];
  179. } elseif ($typeToken === 'refresh'){
  180. $updateField = 'PRT_UPDATED_AT';
  181. $expiresIn = Yii::$app->params['backRefreshTokenExpiresIn'];
  182. } else {
  183. throw new HttpException(500, 'token字段错误', 500);
  184. }
  185. } elseif ($appType === Request::DEVICE_APP){
  186. if($typeToken === 'access'){
  187. $updateField = 'AAT_UPDATED_AT';
  188. $expiresIn = Yii::$app->params['backAccessTokenExpiresIn'];
  189. } elseif ($typeToken === 'refresh'){
  190. $updateField = 'ART_UPDATED_AT';
  191. $expiresIn = Yii::$app->params['backRefreshTokenExpiresIn'];
  192. } else {
  193. throw new HttpException(500, 'token字段错误', 500);
  194. }
  195. }
  196. // 老token
  197. $oldToken = $userTokenModel->$tokenField;
  198. // 生成 access_token
  199. /* @var $identityClass IdentityInterface */
  200. $identityClass = $this->identityClass;
  201. $generateTokenMethodName = 'generate'.ucfirst($typeToken).'Token';
  202. //$token = $identityClass::generateAccessToken();
  203. $token = call_user_func([$identityClass, $generateTokenMethodName], $appType);
  204. $userTokenModel->$tokenField = $token;
  205. $userTokenModel->$updateField = Date::nowTime();
  206. if(!$userTokenModel->save()){
  207. throw new HttpException(500, 'token更新失败', 500);
  208. }
  209. // 查找TOKEN中是否有同一用户产生的垃圾token,有的话就清除
  210. Yii::$app->tokenRedis->del($oldToken);
  211. $identity = $this->_apiIdentity;
  212. if(!$this->_apiIdentity){
  213. if(!$userId){
  214. throw new HttpException(500, 'userId不能为空', 500);
  215. }
  216. $identity = $identityClass::findIdentity($userId);
  217. }
  218. // 把 accessToken 当做key存入redis中内容为会员的ID和用户名
  219. Yii::$app->tokenRedis->hset($token, 'ID', $identity['ID']);
  220. Yii::$app->tokenRedis->hset($token, 'ADMIN_NAME', $identity['ADMIN_NAME']);
  221. Yii::$app->tokenRedis->hset($token, 'ROLE_ID', $identity['ROLE_ID']);
  222. Yii::$app->tokenRedis->hset($token, 'TOKEN_UPDATED_AT', $userTokenModel->$updateField);
  223. Yii::$app->tokenRedis->expire($token, $expiresIn);
  224. $this->_token = array_merge($this->_token ? $this->_token : [], [
  225. $typeToken.'Token' => $token,
  226. $typeToken.'TokenExpiresIn' => $expiresIn,
  227. $typeToken.'TokenUpdateAt' => $userTokenModel->$updateField,
  228. ]);
  229. return true;
  230. }
  231. /**
  232. * 获取管理员ID
  233. * @return int|null|string
  234. */
  235. public function getId()
  236. {
  237. return $this->_userId;
  238. }
  239. /**
  240. * 获取token
  241. * @return null
  242. */
  243. public function getToken(){
  244. return $this->_token;
  245. }
  246. /**
  247. * 获取管理员信息
  248. * @return null
  249. */
  250. public function getUserInfo(){
  251. return $this->_userInfo;
  252. }
  253. /**
  254. * 获取身份信息
  255. * @param bool $autoRenew
  256. * @return null|IdentityInterface
  257. */
  258. public function getIdentity($autoRenew = true)
  259. {
  260. if($this->_apiIdentity){
  261. return $this->_apiIdentity;
  262. } else {
  263. if($this->_userId){
  264. /* @var $class IdentityInterface */
  265. $class = $this->identityClass;
  266. return $class::findOne(['ID' => $this->_userId]);
  267. } else {
  268. return null;
  269. }
  270. }
  271. }
  272. /**
  273. * 获取管理员权限
  274. * @return mixed
  275. */
  276. public function getAdminPermission(){
  277. $userInfo = $this->_userInfo;
  278. $adminRoles = Cache::getAdminRole();
  279. //$role = AdminRole::findOne(['ID'=>$userInfo['roleId']]);
  280. return $adminRoles[$userInfo['roleId']]['PERMISSION'];
  281. }
  282. /**
  283. * 校验管理员权限
  284. * @param $controller
  285. * @param string $action
  286. * @return bool
  287. */
  288. public function validateAdminAction($controller, $action = ''){
  289. // 查看控制器是否在白名单中,如果在白名单中则直接返回true
  290. $noCheckActions = Yii::$app->params['noCheckPermissionActions'];
  291. if(in_array($controller.'/'.$action, $noCheckActions)){
  292. return true;
  293. }
  294. $userInfo = $this->_userInfo;
  295. if($userInfo && isset($userInfo) && $userInfo['roleId'] === Yii::$app->params['superAdminRoleId']){
  296. return true;
  297. }
  298. // 获取管理员权限
  299. $permission = $this->getAdminPermission();
  300. if(!$permission){
  301. return false;
  302. }
  303. if($controller && $action){
  304. if(in_array($controller.'/'.$action, $permission)){
  305. return true;
  306. }
  307. }
  308. // elseif ($controller){
  309. // $pattern = '/'.$controller.'\//';
  310. // foreach($permission as $value){
  311. // if(preg_match($pattern, $value)){
  312. // return true;
  313. // }
  314. // }
  315. // }
  316. return false;
  317. }
  318. /**
  319. * 查看是否有该控制器的权限
  320. * @param $controller
  321. * @return bool
  322. */
  323. public function validateAdminController($controller){
  324. $userInfo = $this->_userInfo;
  325. if($userInfo && isset($userInfo) && $userInfo['roleId'] === Yii::$app->params['superAdminRoleId']){
  326. return true;
  327. }
  328. $result = false;
  329. // 查看控制器是否在白名单中,如果在白名单中则直接返回true
  330. $noCheckActions = Yii::$app->params['noCheckPermissionActions'];
  331. foreach($noCheckActions as $action){
  332. if(preg_match('/^'.$controller.'\//', $action)){
  333. $result = true;
  334. break;
  335. }
  336. }
  337. // 查看会员的权限
  338. $permissions = $this->getAdminPermission();
  339. if($permissions){
  340. foreach($permissions as $permission){
  341. if(preg_match('/^'.$controller.'\//', $permission)){
  342. $result = true;
  343. break;
  344. }
  345. }
  346. }
  347. return $result;
  348. }
  349. /**
  350. * 不需要检查管理员权限的控制器
  351. * @param $controller
  352. * @return bool
  353. */
  354. public function noCheckAdminController($controller){
  355. $userInfo = $this->_userInfo;
  356. if($userInfo && isset($userInfo) && $userInfo['roleId'] === Yii::$app->params['superAdminRoleId']){
  357. return false;
  358. }
  359. $result = false;
  360. // 查看控制器是否在白名单中,如果在白名单中则直接返回true
  361. $noCheckActions = Yii::$app->params['noCheckPermissionActions'];
  362. foreach($noCheckActions as $action){
  363. if(preg_match('/^'.$controller.'\//', $action)){
  364. $result = true;
  365. break;
  366. }
  367. }
  368. // 查看会员的权限
  369. $permissions = $this->getAdminPermission();
  370. if($permissions){
  371. foreach($permissions as $permission){
  372. if(preg_match('/^'.$controller.'\//', $permission)){
  373. $result = false;
  374. break;
  375. }
  376. }
  377. }
  378. return $result;
  379. }
  380. public static function sendEmailCode($adminName): array
  381. {
  382. // 管理员
  383. $admin = Admin::findOneAsArray('ADMIN_NAME=:ADMIN_NAME', [':ADMIN_NAME' => $adminName]);
  384. if (!$admin) {
  385. return [
  386. 'code' => 500,
  387. 'message' => '管理员账号错误',
  388. ];
  389. }
  390. if (!$admin['EMAIL'] || !filter_var($admin['EMAIL'], FILTER_VALIDATE_EMAIL)) {
  391. return [
  392. 'code' => 500,
  393. 'message' => '管理员未绑定邮箱, 或邮箱无效',
  394. ];
  395. }
  396. // 发送验证码
  397. $result = Email::sendAdminLoginCode($admin['EMAIL']);
  398. if (!$result) {
  399. return [
  400. 'code' => 500,
  401. 'message' => '验证码发送失败',
  402. ];
  403. }
  404. // 验证码入库
  405. $emailLogObj = new EmailLog();
  406. $emailLogObj->ADMIN_ID = $admin['ID'];
  407. $emailLogObj->EMAIL = $admin['EMAIL'];
  408. $emailLogObj->CODE = $result;
  409. $emailLogObj->CREATED_AT = time();
  410. $emailLogObj->UPDATED_AT = time();
  411. $emailLogObj->save();
  412. return [
  413. 'code' => 200,
  414. 'message' => sprintf('验证码已发送, 请检查邮箱%s(%s)', Tool::hideEmail($admin['EMAIL']), $result),
  415. ];
  416. }
  417. }