使用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)