我昨天遇到一个问题,就是我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() { @Override public 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<>(); //将空值置为null SpringBeanUtil.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的过滤器,在过滤器里面完成资源的清除