使用java web修改AD域用户密码报错解决方案

本文详细记录了一次在企业微信项目中遇到的AD域连接异常问题及解决方案。问题表现为在本地应用无误,但Web访问时出现SSL握手失败的异常。通过在Tomcat启动时执行特定的AD域连接代码,成功解决了后续Web访问中的连接问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

报错如下
javax.naming.CommunicationException: simple bind failed: 服务器地址:636 [Root exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target]


javax.naming.CommunicationException: simple bind failed: 服务器地址:636 [Root exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target]
*****Start search *******'s distinguishedName
五月 13, 2019 9:50:17 上午 org.apache.catalina.core.StandardWrapperValve invoke
严重: Servlet.service() for servlet [springmvc] in context with path [/ITrepair] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
java.lang.NullPointerException
	at com.sw.service.PasswordServiceImpl.searchDistinguishedName(PasswordServiceImpl.java:80)
	at com.sw.service.PasswordServiceImpl.chpwd(PasswordServiceImpl.java:142)
	at com.sw.controller.PasswordController.changePwd(PasswordController.java:89)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:877)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	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:198)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:800)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1471)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Unknown Source)

网上看到有相同的错误但是找了好久没找到解决方法,都是在本地application不报错,web访问报错
在本地把方法写到main方法跑是没有问题的 正常连接 正常改密
项目是开发的企业微信项目
在调用企业微信接口(获得个人信息)后,填写新密码,点击修改密码(调用项目中的业务层的连接AD域方法)则报错。
但是如果直接浏览器地址访问控制层调用业务层的方法 则不报错,如果不重启tomcat,之后从微信端也可以正常连接AD域 搞不懂什么情况23333
然后我就在代码中加了启动tomcat执行连接AD域的方法 之后从微信端访问则可正常连接AD域不报错
解决问题代码如下:
web.xml代码:

<listener>
          <listener-class>com.sw.sendtest.LdapTest</listener-class>  
</listener>

启动tomcat执行方法:

public class LdapTest implements ServletContextListener {
		//开启tomcat执行方法  配合web.xml监听器食用
		@Override
		public void contextDestroyed(ServletContextEvent arg0) {
			// TODO Auto-generated method stub
		}

	@Override
	public void contextInitialized(ServletContextEvent arg0) {
		// TODO Auto-generated method stub
		 System.out.println("tomcat启动");
		 PasswordServiceImpl psi=new PasswordServiceImpl();
		 psi.initialLdap();
		 psi.closeLdap();
	}
}

连接/关闭AD域代码:

public boolean initialLdap() {
		Hashtable<String, String> env = new Hashtable<String, String>();
		System.setProperty("javax.net.ssl.trustStore", keystore);
		env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
		env.put(Context.SECURITY_PRINCIPAL, "域账号");
		env.put(Context.SECURITY_CREDENTIALS, "密码");
		env.put(Context.SECURITY_AUTHENTICATION, "simple");
		env.put(Context.SECURITY_PROTOCOL, "ssl");
		env.put(Context.PROVIDER_URL, "ldaps://服务器地址");
		try {
			System.out.println("*****开始初始化Ldap");
			ctx = new InitialLdapContext(env, null);
			System.out.println("*****初始化Ldap成功");
		} catch (NamingException e) {
			System.out.println("初始化Ldap出现问题" + e);
			return false;
		}
		return true;
}
public boolean closeLdap() {
		System.out.println("*****关闭 Ldap");
		try {
			ctx.close();
		} catch (NamingException e) {
			System.out.println("关闭Ldap出现问题  NamingException: " + e);
			return false;
		}
		return true;
	}

PS:只贴了执行方法的代码,没有把定义的变量内容贴出,请结合实际修改应用
然后就会每次启动tomcat先成功连接一次AD域,之后web端访问也可以正常连接
虽然不明白是什么原理 但是还是暂时解决了这个问题23333
如果大佬们有更好的解决办法,欢迎分享~

<think>好的,我现在需要帮助用户解决在使用Spring Boot封装第三方POST表单接口时遇到的问题。用户已经检查了输入参数正确,但接口仍然返回参数错误。我需要回顾之前的对话历史,看看之前给出的建议,然后思考可能用户没有正确应用那些建议,或者可能存在其他未被考虑的问题。 首先,用户之前询问了关于bodyValue传格式不正确的问题,我给出了详细的原因和解决方案,包括使用MultiValueMap、设置正确的Content-Type、处理编码问题等。现在用户的问题看起来类似,但可能是在实际应用中遇到了其他问题,或者之前提供的解决方案没有完全解决。 用户现在的情况是:已经确认输入参数正确,但第三方接口仍然返回参数错误。这可能意味着请求的格式或内容仍有问题,或者第三方接口有其他特定的要求未被满足。 我需要考虑以下几点: 1. **MultiValueMap的正确使用**:用户是否正确地使用了LinkedMultiValueMap来确保参数可以包含多个值,而不会覆盖之前的参数?比如,是否有重复的键需要添加而不是替换。 2. **Content-Type的设置**:是否在WebClient的请求中明确设置了application/x-www-form-urlencoded的Content-Type?如果漏掉这一步,服务器可能无法正确解析表单数据。 3. **参数的编码问题**:特别是当参数包含特殊字符(如空格、中文、符号)时,是否进行了正确的URL编码?手动编码或使用UriComponentsBuilder处理是否被正确应用。 4. **日志记录和调试**:用户是否启用了请求日志来查看实际发送的数据格式是否正确?比如,使用wiretap或拦截器来捕获请求头和请求体,确认数据是否符合预期。 5. **第三方接口的特殊要求**:是否第三方接口对表单参数的顺序有要求?比如,需要参数按特定顺序排列,或者需要某些特定的头信息(如User-Agent、Accept等)。 6. **多部分表单与普通表单的混淆**:如果接口需要的是multipart/form-data而不是x-www-form-urlencoded,用户是否错误地使用了普通的表单提交方式? 7. **SSL/TLS或代理问题**:是否因为SSL证书问题导致请求被拦截或修改?尤其是在测试环境或使用自签名证书时,可能需要禁用SSL验证。 8. **服务器端验证问题**:第三方接口可能对某些隐藏参数或时间戳、签名等有要求,用户是否遗漏了这些参数? 9. **重定向或Cookie处理**:如果接口有重定向或需要Cookie,WebClient默认是不自动处理的,是否需要配置相关策略? 10. **HTTP方法或URL错误**:确认POST请求的URL是否正确,是否存在拼写错误,或者是否需要使用HTTPS而不是HTTP。 接下来,我需要组织这些思考点,形成结构化的回答,帮助用户逐一排查问题。可能需要分步骤指导用户检查各个可能的问题点,并提供相应的代码示例和调试方法。 例如,首先检查MultiValueMap的使用是否正确,然后确认Content-Type设置,接着检查参数编码,然后查看日志输出,最后考虑第三方接口的特殊要求。同时,提醒用户使用工具如Postman对比请求,确保请求体的格式正确。 此外,用户可能在之前的解决方案中没有正确应用某些步骤,比如虽然设置了Content-Type,但可能由于其他配置导致覆盖了该设置,或者在构建MultiValueMap时遗漏了某些参数。需要强调详细检查每个步骤的重要性,并提供具体的代码示例来演示正确做法。 可能还需要考虑是否使用了正确的HTTP客户端,比如WebClient的配置是否正确,是否有过滤器或拦截器修改了请求,或者是否应该尝试使用RestTemplate来测试,以排除WebClient配置的问题。 总之,需要系统地引导用户检查所有可能的错误点,并提供明确的解决方案和验证步骤,确保用户能够逐步排查并解决问题。</think>在 Spring Boot 中使用 WebClient 封装第三方表单接口时,若参数已验证正确但接口仍报错,可能是 **请求构建细节** 或 **第三方接口特殊要求** 导致。以下是系统性排查指南: --- ### 一、基础代码模板验证 #### 1. 标准表单提交代码 ```java // ✅ 正确姿势:必须用 MultiValueMap 且显式声明 Content-Type MultiValueMap<String, String> formData = new LinkedMultiValueMap<>(); formData.add("username", "user123"); formData.add("password", "myp@ss"); String response = WebClient.create() .post() .uri("https://api.example.com/login") .contentType(MediaType.APPLICATION_FORM_URLENCODED) // 关键设置 .bodyValue(formData) .retrieve() .bodyToMono(String.class) .block(); ``` #### 2. 常见漏网之鱼 - **Content-Type 未设置或冲突** 若使用 `.bodyValue(普通Map)` 而非 `MultiValueMap`,Spring 可能自动序列化为 JSON,导致服务器无法解析。 - **参数顺序敏感** 部分接口要求参数按固定顺序排列(如签名计算场景),使用 `LinkedMultiValueMap` 保证顺序。 --- ### 二、高级调试技巧(关键步骤) #### 1. 捕获原始请求数据 在 `application.properties` 中开启详细网络日志: ```properties logging.level.org.springframework.web.reactive.function.client.ExchangeFunctions = DEBUG logging.level.reactor.netty.http.client = DEBUG ``` 控制台将输出实际发送的 **请求头和请求体**,例如: ```text POST /api HTTP/1.1 Content-Type: application/x-www-form-urlencoded Content-Length: 27 username=test&password=123 ``` #### 2. 对比工具验证 用 Postman 发送相同参数,抓取正常请求的原始数据,与代码生成的请求对比差异。重点关注: - 空格是否被编码为 `%20` 或 `+` - 中文字符是否转为 `%E4%B8%AD` - 参数分隔符是 `&` 还是 `;` #### 3. 编码强制验证 ```java // 手动编码单个参数 String encodedValue = URLEncoder.encode("含有空格的参数", StandardCharsets.UTF_8); formData.add("key", encodedValue); // 或使用 UriComponentsBuilder 自动编码 String queryString = UriComponentsBuilder.newInstance() .queryParam("key", "未编码值") .build() .encode() .getQuery(); ``` --- ### 三、特殊接口要求排查 #### 1. 隐藏参数需求 某些接口要求固定参数(如时间戳 `timestamp`、随机数 `nonce` 或签名 `sign`): ```java // 示例:添加签名参数 String timestamp = String.valueOf(System.currentTimeMillis()); formData.add("timestamp", timestamp); formData.add("sign", computeSign(timestamp)); ``` #### 2. 请求头校验 添加必要请求头: ```java .webClient.post() .header("User-Agent", "MyApp/1.0") // 部分接口校验 UA .header("X-Api-Key", "your-key") ``` #### 3. SSL/TLS 兼容性 若目标接口使用老旧 SSL 协议,需强制指定: ```java @Bean public WebClient webClient() { SslContext sslContext = SslContextBuilder.forClient() .protocols("TLSv1.2") // 根据目标服务器支持情况调整 .build(); HttpClient httpClient = HttpClient.create() .secure(ssl -> ssl.sslContext(sslContext)); return WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)) .build(); } ``` --- ### 四、错误对照速查表 | 现象 | 快速诊断方法 | 解决方案 | |-------------------------------|--------------------------------|--------------------------------| | 返回"invalid parameter" | 对比 Postman 请求体编码格式 | 检查空格/中文是否已正确 URL 编码 | | 返回"timestamp expired" | 检查服务器时间与本地时间差 | 同步时间或添加时间容差逻辑 | | 返回"signature mismatch" | 抓取原始参数字符串重新计算签名 | 确认签名算法与参数顺序与文档一致 | | 连接超时或重置 | 使用 `curl -v` 测试网络可达性 | 配置代理或联系运维开通防火墙白名单 | --- ### 五、终极验证方案 若仍无法解决,按以下步骤定位: 1. 用 Wireshark 抓取 **代码发送的请求** 和 **Postman 成功请求** 的原始 TCP 包 2. 对比两者在 HTTP 报文层的差异(特别是消息头的顺序和请求体编码) 3. 检查是否有多余的换行符、未修剪的空格等不可见字符 通过逐层排除,可精准定位到表单提交的隐藏问题。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值