RestTemplate get请求偶尔会报空指针

使用RestTemplate调用公司用户服务接口,发送get请求,偶尔会报空指针,非必现。用户服务框架为:nodejs+mongodb,请求参数为 mongodb 聚合查询语句。
代码如下:

public HttpEntity createHttpEntity(String body){
        // 解决响应数据可能出现的中文乱码问题
        List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters();
        // 移除原来的转换器
        converterList.remove(1);
        // 设置字符编码为utf-8
        HttpMessageConverter<?> converter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
        // 添加新的转换器
        converterList.add(1,converter);
        // 选择性设置请求头信息
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("Ocp-Apim-Subscription-Key", "xxxxxxxxxxxxx");
        // Get请求,创建HttpEntity时,请求体传入null即可
        String httpBody = body;
        HttpEntity<String> httpEntity = new HttpEntity<String>(httpBody, httpHeaders);
        return httpEntity;
    }

public MDSUser userInfo(String username) {
        log.info("用户名:"+username);
        MDSUser mdsUser = null;
        HttpEntity httpEntity = createHttpEntity(null);
        String url = EMPLOYEES_BASE + "agg={param}";
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("param", "[{$match:{$or:[{CommonName:/^"+username+"$/i},{sAMAccountName:/^"+username+"$/i},{userPrincipalName:/^"+username+"$/i}]}},{$project:{Clones:0,Sum:0}},{$limit:1}]");
        log.info("url:"+url);
        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
        URI uri = builder.build().encode().toUri();
        ResponseEntity<JSONArray> response = restTemplate.exchange(url, HttpMethod.GET, httpEntity, JSONArray.class, paramMap);
        JSONArray list = response.getBody();
        if(list.size() > 0){
            mdsUser = JSON.parseObject(JSON.toJSONString(list.get(0)), new TypeReference<MDSUser>(){});
        }
        return mdsUser;
    }

报错信息如下:

2021-10-22 13:07:52.349 [http-nio-8593-exec-1] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet]:175 - Servlet.service() for servlet [dispatcherServlet] in context with path [/purchase-to-pay] threw exception [org.apache.shiro.authc.AuthenticationException: Token失效,请重新登录] with root cause
java.lang.NullPointerException: null
    at org.springframework.web.client.RestTemplate$AcceptHeaderRequestCallback.canReadResponse(RestTemplate.java:862)
    at org.springframework.web.client.RestTemplate$AcceptHeaderRequestCallback.lambda$doWithRequest$0(RestTemplate.java:847)
    at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:174)
    at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
    at org.springframework.web.client.RestTemplate$AcceptHeaderRequestCallback.doWithRequest(RestTemplate.java:851)
    at org.springframework.web.client.RestTemplate$HttpEntityRequestCallback.doWithRequest(RestTemplate.java:911)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:733)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:710)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:598)
    at com.epiroc.purchasetopay.service.impl.UserClientServiceImpl.getUserInfo(UserClientServiceImpl.java:135)
    at com.epiroc.purchasetopay.shiro.authc.ShiroRealm.checkUserTokenIsEffect(ShiroRealm.java:146)
    at com.epiroc.purchasetopay.shiro.authc.ShiroRealm.doGetAuthenticationInfo(ShiroRealm.java:119)
    at org.apache.shiro.realm.AuthenticatingRealm.getAuthenticationInfo(AuthenticatingRealm.java:571)
    at org.apache.shiro.authc.pam.ModularRealmAuthenticator.doSingleRealmAuthentication(ModularRealmAuthenticator.java:180)
    at org.apache.shiro.authc.pam.ModularRealmAuthenticator.doAuthenticate(ModularRealmAuthenticator.java:267)
    at org.apache.shiro.authc.AbstractAuthenticator.authenticate(AbstractAuthenticator.java:198)
    at org.apache.shiro.mgt.AuthenticatingSecurityManager.authenticate(AuthenticatingSecurityManager.java:106)
    at org.apache.shiro.mgt.DefaultSecurityManager.login(DefaultSecurityManager.java:274)
    at org.apache.shiro.subject.support.DelegatingSubject.login(DelegatingSubject.java:260)
    at com.epiroc.purchasetopay.shiro.authc.aop.JwtFilter.executeLogin(JwtFilter.java:53)
    at com.epiroc.purchasetopay.shiro.authc.aop.JwtFilter.isAccessAllowed(JwtFilter.java:39)
    at org.apache.shiro.web.filter.AccessControlFilter.onPreHandle(AccessControlFilter.java:162)
    at org.apache.shiro.web.filter.PathMatchingFilter.isFilterChainContinued(PathMatchingFilter.java:203)
    at org.apache.shiro.web.filter.PathMatchingFilter.preHandle(PathMatchingFilter.java:178)
    at com.epiroc.purchasetopay.shiro.authc.aop.JwtFilter.preHandle(JwtFilter.java:73)
    at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:131)
    at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
    at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66)
    at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449)
    at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365)
    at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)
    at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)
    at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:387)
    at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362)
    at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:124)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter.doFilterInternal(HttpTraceFilter.java:90)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:117)
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:106)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1415)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

求解答

这是读取响应时没有反馈的空指针。

看看你公司的服务是不是偶尔没有返回。

Token失效,请重新登录

2021-10-22 13:07:52.349 [http-nio-8593-exec-1] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet]:175 - Servlet.service() for servlet [dispatcherServlet] in context with path [/purchase-to-pay] threw exception [org.apache.shiro.authc.AuthenticationException: Token失效,请重新登录] with root cause
你看看这个报错信息,是你的token 失效,请求失败

你找找token失效的原因吧

查mongo时候报的空指针么?打个断点看看是网络还是校验的数据有问题把

ResponseEntity<JSONArray> response = restTemplate.exchange(url, HttpMethod.GET, httpEntity, JSONArray.class, paramMap);
    JSONArray list = response.getBody();

初步判断可能是这里的问题,这里发起请求的时候如果出错,服务端没正常返回,response 可能是nil,这个时候调 response.getBody()就会出现空指针,可以在这里加一层异常判断。
当然最好提供整个调用链的完整代码,这样根据异常中出现的行数也能定位到问题


@Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
        String token = (String) auth.getCredentials();
        if (token == null) {
            log.info("————————身份认证失败——————————IP地址:  "+ oConvertUtils.getIpAddrByRequest(SpringContextUtils.getHttpServletRequest()));
            throw new AuthenticationException("token为空!");
        }
        // 校验token有效性
        MDSUser loginUser = null;
        try {
            loginUser = this.checkUserTokenIsEffect(token);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new SimpleAuthenticationInfo(loginUser, token, getName());
    }

    /**
     * 校验token的有效性
     *
     * @param token
     */
    public MDSUser checkUserTokenIsEffect(String token) throws AuthenticationException, IOException {
        // 解密获得username,用于和数据库进行对比
        String username = JwtUtil.getUsername(token);
        if (username == null) {
            throw new AuthenticationException("token非法无效!");
        }

        // 查询用户信息
        log.info("———校验token是否有效————checkUserTokenIsEffect——————— "+ token);

        MDSUser loginUser = userClientService.getUserInfo(username);

        if (loginUser == null) {
            throw new AuthenticationException("用户不存在!");
        }
        // 判断用户状态
        if (CommonConstant.DEL_INACTIVE_FLAG.toString().equals(loginUser.getEmpStatusDesc())) {
            throw new AuthenticationException("该用户已注销!");
        }
        // 校验token是否超时失效 & 或者账号密码是否错误
        if (!jwtTokenRefresh(token, username, CommonConstant.VIRTUAL_PASSWORD)) {
            throw new AuthenticationException("Token失效,请重新登录!");
        }

        // 查询系统中的 unit
        List<String> unitList = sysMapper.selectSysUnit();
        loginUser.setSysUnitList(unitList);
        Set<String> unitSet = new HashSet<>();
        for(String temp:unitList){
            if(CommonConstant.COUNTRY_HONG_KONG.equals(temp) || CommonConstant.COUNTRY_TAI_WAN.equals(temp)){
                unitSet.add("HKK");
            }
            unitSet.add(temp);
        }
        List<String> realUnitList = new ArrayList<>(unitSet);
        loginUser.setUnitList(realUnitList);
        // 查询当前用户的 unit_id
        String unitId = sysMapper.getUnitId(loginUser.getUnit());
        loginUser.setUnitId(unitId);
        // 获取用户 role
        RolePojo role = sysMapper.selectRoleCodeByUserId(loginUser.getGuid());
        // 查询不到的都视为普通用户,CNN特殊处理
        String roleCode = "";
        String roleId = "";
        if(role == null){
            roleCode = "2";
            roleId = sysMapper.selectRoleIdByRoleCode(roleCode);
        } else {
            roleCode = role.getRoleCode();
            roleId = role.getRoleId();
        }
        if("HKK".equals(loginUser.getUnit())){
            String realUnit = "";
            if(CommonConstant.HKK_HK_TIMEZONE.equals(loginUser.getTimezone())){
                realUnit = CommonConstant.COUNTRY_HONG_KONG;
            } else if(CommonConstant.HKK_TW_TIMEZONE.equals(loginUser.getTimezone())){
                realUnit = CommonConstant.COUNTRY_TAI_WAN;
            }
            loginUser.setUnit(realUnit);
        }

        loginUser.setRoleId(roleId);
        loginUser.setRoleCode(roleCode);
        return loginUser;
    }

// 校验token是否超时失效 & 或者账号密码是否错误
if (!jwtTokenRefresh(token, username, CommonConstant.VIRTUAL_PASSWORD)) {
throw new AuthenticationException("Token失效,请重新登录!");
应该就是上面的第45行代码这里抛出的异常,在 throw之前打个log看看

你贴的错误信息里报的是空指针异常,发生的条件是:

如果a = b.c.d报空指针,则b或者c可能为null

结合错误信息

at com.epiroc.purchasetopay.service.impl.UserClientServiceImpl.getUserInfo(UserClientServiceImpl.java:135)

你应该看UserClientServiceImpl.java的135行是否存在b.c.d这种代码,并看一下b或者c有没有可能在某些情况下为null

UserClientServiceImpl.java:135 这个代码沾出来看看,这种空指针异常打个断点,就知道那个是null了。

代码的135行获取用户信息空指针,可以进去 getUserInfo()方法打个断点看看为什么
at com.epiroc.purchasetopay.service.impl.UserClientServiceImpl.getUserInfo(UserClientServiceImpl.java:135)