我昨天遇到一個問題,就是我springboot項目里面有一個提供代辦服務審核的dubbo接口,這個接口給房源項目調用,但是碰到一個問題就是,房源項目每天凌晨5點會查詢滿足條件過期的數據,然后調用我這邊的代辦審核dubbo接口,將這個代辦任務變成自動拒絕
@Override @Transactional public WorkListDataResult auditWorkList(WorkListAuditCmd workListAuditCmd, WorkListLoginUserVo loginUserVo) {log.info("auditWorkList WorkListAuditCmd={},WorkListLoginUserVo={}", JSON.toJSONString(workListAuditCmd), JSON.toJSONString(loginUserVo));LoginUserUtil.setCurrentUser(loginUserVo);WorkListAuditAdapterCmd workListAuditAdapterCmd = AutoMapper.transform(WorkListAuditAdapterCmd.class, workListAuditCmd);workListAuditAdapterCmd.setAuditComments(AuditStatusEnum.PASS.getCode().equals(workListAuditCmd.getAuditStatus()) || AuditStatusEnum.PENDING_REEVALUATION.getCode().equals(workListAuditCmd.getAuditStatus()) ? "系統通過" : "系統拒絕");workListAuditAdapterCmd.setExtAuditComments(workListAuditAdapterCmd.getAuditComments());auditWorkListInternal(workListAuditAdapterCmd);WorkListAuditReq workListAuditReq = AutoMapper.transform(WorkListAuditReq.class, workListAuditCmd);Boolean needCallback = workListAuditAdapterCmd.getNeedCallback();if (needCallback != null && needCallback) {//如果dubbo接口里面參數需要回調則回調// 注冊事務同步器,在事務提交后執行異步任務TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {@Overridepublic void afterCommit() {// 確保數據提交后執行異步任務CompletableFuture.runAsync(() -> pushAuditResultToBusiness(workListAuditReq), threadPoolExecutor);}});}return new WorkListDataResult(); }
這個定時任務調用這個rpc接口時候,第二個參數WorkListLoginUserVo是傳的null,這里到后面審核的時候會根據這個 LoginUserUtil.getCurrentUser 判斷不為空,就不會進入權限校驗的邏輯
protected void auditMultiProcessWorkTaskInternal(WorkListAuditAdapterCmd workListAuditAdapterCmd) {log.info("auditMultiProcessWorkTaskInternal WorkListAuditAdapterCmd={}",JSON.toJSONString(workListAuditAdapterCmd));//校驗是否有審核的WorkListRespVo workListInfo = workListQueryService.getWorkListInfoByKeyId(workListAuditAdapterCmd.getWorkListKeyId());setAuditInfo(workListAuditAdapterCmd);Integer auditStatus = workListInfo.getAuditStatus();LoginUser currentUser = Identify.getCurrentUser();if(AuditStatusEnum.PENDING_REVIEW.getCode().equals(auditStatus)){workListAuditAdapterCmd.setFirstAuditDeptKeyId(currentUser == null ? workListAuditAdapterCmd.getAuditDeptKeyId() : currentUser.getDepartmentKeyId());workListAuditAdapterCmd.setFirstAuditUserKeyId(currentUser == null ? workListAuditAdapterCmd.getAuditUserKeyId() : currentUser.getUserKeyId());workListAuditAdapterCmd.setAuditStatus(AuditStatusEnum.PASS.getCode().equals(workListAuditAdapterCmd.getAuditStatus()) || AuditStatusEnum.PENDING_REEVALUATION.getCode().equals(workListAuditAdapterCmd.getAuditStatus()) ? AuditStatusEnum.PENDING_REEVALUATION.getCode() : AuditStatusEnum.REFUSE.getCode());workListAuditAdapterCmd.setFirstAuditTime(LocalDateTime.now());}else {workListAuditAdapterCmd.setSecondAuditUserKeyId(currentUser == null ? workListAuditAdapterCmd.getAuditUserKeyId() : currentUser.getUserKeyId());workListAuditAdapterCmd.setSecondAuditDeptKeyId(currentUser == null ? workListAuditAdapterCmd.getAuditDeptKeyId() : currentUser.getDepartmentKeyId());workListAuditAdapterCmd.setSecondAuditTime(LocalDateTime.now());workListAuditAdapterCmd.setFirstAuditTime(null);workListAuditAdapterCmd.setFirstAuditDeptKeyId(null);workListAuditAdapterCmd.setFirstAuditUserKeyId(null);}log.info("auditMultiProcessWorkTaskInternal WorkListAuditAdapterCmd={},WorkListRespVo={}",JSON.toJSONString(workListAuditAdapterCmd),JSON.toJSONString(workListInfo));// 獲取權限代碼setPermissionCode(workListAuditAdapterCmd, workListInfo);//校驗是否有審核權限,排除房源定時Job調用的情況if(currentUser != null && StringUtils.isNotBlank(currentUser.getUserKeyId()) && StringUtils.isNotBlank(currentUser.getRoleKeyId())){checkAuditPermission(currentUser,workListAuditAdapterCmd,workListInfo);}//更新代辦任務數據updateWorkList(workListAuditAdapterCmd); }
生產上面卻發現了一個問題,就是有一定概率會有些數據進行了權限校驗,也就是currentUser不為空了,后面跟蹤房源那邊代碼才發現,那邊調用這個審核的dubbo接口,會調用另外一個查詢的rpc接口,
public WorkListDataResult<List<WorkListRespDto>> getWorkListStatusByCondion(WorkListStatusReq req, WorkListLoginUserVo loginUserVo) {log.info("getWorkListStatusByCondion WorkListStatusReq={},WorkListLoginUserVo={}", JSON.toJSONString(req), JSON.toJSONString(loginUserVo));LoginUserUtil.setCurrentUser(loginUserVo);WorkListDataResult<List<WorkListRespDto>> result = new WorkListDataResult<>();if (StringUtils.isBlank(req.getApplyType())) {result.setFlag(false);result.setErrorMessage("待辦類型不能為空!");return result;}if (StringUtils.isBlank(req.getPropertyKeyId())) {result.setFlag(false);result.setErrorMessage("房源ID不能為空!");return result;}List<WorkListRespDto> listResps = new ArrayList<>();List<UserByIdOrNumberRpcDto> userList = new ArrayList<>();//用于存放用戶ID的集合List<String> userKeyIdList = new ArrayList<>();List<DepartmentByIdOrNoRpcDto> deptList = new ArrayList<>();//用于存放部門ID的集合List<String> deptIdList = new ArrayList<>();//將空值置為nullSpringBeanUtil.convertEmptyToNull(req);WorkListSearchVo workListSearchVo = AutoMapper.transform(WorkListSearchVo.class, req);log.info("getWorkListStatusByCondion WorkListSearchVo={}", JSON.toJSONString(workListSearchVo));List<WorkListRespVo> list = workListQueryService.getWorkListStatusByCondion(workListSearchVo);log.info("getWorkListStatusByCondion list.total=" + list.size());if (list != null && list.size() > 0) {for (WorkListRespVo viewResp : list) {if (viewResp != null) {//設置userId和deptId集合setDeptOrUserList(viewResp, userKeyIdList, deptIdList);}}if (userKeyIdList != null && userKeyIdList.size() > 0) {//批量查詢用戶信息userList = userAndDeptDubboService.queryUserByIdOrNumber(userKeyIdList);log.info("getWorkListStatusByCondion userList={}", userList.toString());}if (deptIdList != null && deptIdList.size() > 0) {//批量查詢部門信息deptList = userAndDeptDubboService.queryByKeyIdOrNo(deptIdList);log.info("getWorkListStatusByCondion deptList={}", deptList.toString());}for (WorkListRespVo viewResp : list) {//設置枚舉描述setWorkListVoDesc(viewResp);//設置待辦對象用戶和部門信息setWorkListRespVoDeptOrUser(viewResp, userList, deptList);WorkListRespDto workListRespVo = AutoMapper.transform(WorkListRespDto.class, viewResp);listResps.add(workListRespVo);}}result.setFlag(true);result.setData(listResps);return result; }
這個查詢的接口也進行了LoginUserUtil.setCurrentUser(loginUserVo),這段代碼會放到ThreadLocal 的線程本地變量里面
而整個這個查詢和審核的接口都沒有進行這個ThreadLocal線程變量的清除,因為dubbo提供的rpc接口,本質上是使用了線程池技術,會復用一些線程,比如說19號先執行了這個查詢代辦任務的rpc接口,這個時候設置了currentUser,然后后面調用這個代辦審核的接口時候,又剛好拿到了這個19號線程,這個時候通過getCuurentUser拿到的用戶就不為空,然后就進入了權限校驗的邏輯,解決辦法通過自定義dubbo的過濾器,在過濾器里面完成資源的清除