尝试使用托管Tomcat服务器中的Spring MVC Controller 处理Mailchimp Webhook请求时遇到问题(在我自己的开发环境中,一切都运行良好)。
我只是添加一个"秘密" webhook URL的参数,如下所示:
http://doamin/webhook.html?secret=password
Mailchimp向webapp发出POST请求,但是他们说你应该添加一个"秘密"因此,出于安全原因,URL(GET参数)的键。
然后我在进入业务逻辑之前检查该参数,通过......
@RequestMapping(method=RequestMethod.POST)
public ModelAndView postProcess(WebRequest request){
if (request.getParameter("secret").equals("password"){
//business logic
}
}
这在我自己的本地tomcat中没问题。从Mailchimp请求中正确获取参数,并运行所有业务逻辑。
然后我将我的WAR上传到生产环境,我可以看到业务逻辑没有运行。
经过很多有趣的调试......我发现getParameter(" secret")将我返回null。
你觉得它可能与tomcat conf有关吗?我的本地tomcat是版本7.0.67。
我托管的tomcat是版本7.0.62
我的web.xml看起来像:
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>Servlet 3.0 Web Application</display-name>
<display-name>Spring Web MVC Application</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<!-- Load up all spring xml files as part of the loading of the webapp -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/security-config.xml;/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
</context-param>
<!-- This filter is used by Spring Security to intercept all URL patterns -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<error-page>
<error-code>403</error-code>
<location>/Forbidden.html</location>
</error-page>
<error-page>
<exception-type>org.springframework.web.util.NestedServletException</exception-type>
<location>/JDBCException.html</location>
</error-page>
<!-- Session expiration max time (in minutes) -->
<session-config>
<session-timeout>30</session-timeout>
</session-config>
</web-app>
我的托管server.xml看起来像:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE server-xml [
<!ENTITY jelastic-ssl SYSTEM "jelastic-ssl.xml">
<!ENTITY jelastic-ha SYSTEM "jelastic-ha.xml">
]>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Note: A "Server" is not itself a "Container", so you may not
define subcomponents such as "Valves" at this level.
Documentation at /docs/config/server.html
-->
<Server port="8005" shutdown="SHUTDOWN">
<!-- Security listener. Documentation at /docs/config/listeners.html
<Listener className="org.apache.catalina.security.SecurityListener" />
-->
<!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
<Listener className="org.apache.catalina.core.JasperListener" />
<!-- Prevent memory leaks due to use of particular java/javax APIs-->
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<!-- Global JNDI resources
Documentation at /docs/jndi-resources-howto.html
-->
<GlobalNamingResources>
<!-- Editable user database that can also be used by
UserDatabaseRealm to authenticate users
-->
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<!-- A "Service" is a collection of one or more "Connectors" that share
a single "Container" Note: A "Service" is not itself a "Container",
so you may not define subcomponents such as "Valves" at this level.
Documentation at /docs/config/service.html
-->
<Service name="Catalina">
<!--The connectors can use a shared executor, you can define one or more named thread pools-->
<!--
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="150" minSpareThreads="4"/>
-->
<!-- A "Connector" represents an endpoint by which requests are received
and responses are returned. Documentation at :
Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)
Java AJP Connector: /docs/config/ajp.html
APR (HTTP/AJP) Connector: /docs/apr.html
Define a non-SSL HTTP/1.1 Connector on port 8080
-->
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="443" />
<!--############## SSL Connector for _domain_name ### PROTO_Dl231aIDsW4 ##########-->
&jelastic-ssl;
<!--############## SSL Connector for _domain_name ### PROTO_Dl541aINsMx ##########-->
<!-- A "Connector" using the shared thread pool-->
<!--
<Connector executor="tomcatThreadPool"
port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
-->
<!-- Define a SSL HTTP/1.1 Connector on port 8443
This connector uses the JSSE configuration, when using APR, the
connector should be using the OpenSSL style configuration
described in the APR documentation -->
<!--
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" />
-->
<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<!-- An Engine represents the entry point (within Catalina) that processes
every request. The Engine implementation for Tomcat stand alone
analyzes the HTTP headers included with the request, and passes them
on to the appropriate Host (virtual host).
Documentation at /docs/config/engine.html -->
<!-- You should set jvmRoute to support load-balancing via AJP ie :
<Engine name="Catalina" defaultHost="localhost" jvmRoute="${jvmRid}">
-->
<Engine name="Catalina" defaultHost="localhost" jvmRoute="${jvmRid}">
<!--Jelastic HA clusteting is enabled here so please do not remove this line untill you reasly know what you are doing -->
&jelastic-ha;
<!-- Use the LockOutRealm to prevent attempts to guess user passwords
via a brute-force attack -->
<Realm className="org.apache.catalina.realm.LockOutRealm">
<!-- This Realm uses the UserDatabase configured in the global JNDI
resources under the key "UserDatabase". Any edits
that are performed against this UserDatabase are immediately
available for use by the Realm. -->
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<!-- SingleSignOn valve, share authentication between web applications
Documentation at: /docs/config/valve.html -->
<!--
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
-->
<!-- Access log processes all example.
Documentation at: /docs/config/valve.html
Note: The pattern used is equivalent to using pattern="common" -->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log." suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
编辑: 更多测试:
我已经评论了我要求密钥的代码。那么,当然,运行业务逻辑。但是在那个业务逻辑中,我也要求POST参数,这真是一个惊喜!!!!我每次尝试访问我的WebRequest参数时都会收到NullPointerException。
如果我重新启动tomcat,第一次运行代码,它会起作用,但是,如果我重复调用,则会出现NullPointerException。
我有问题的代码架构是这样的:
1)由MVC控制器调用的线程进程,因为我需要在不到15秒的时间内回复Mailchimp,而在后台我启动一个线程来运行业务逻辑:
package es.edm.util;
import java.util.Date;
import org.springframework.web.context.request.WebRequest;
import es.edm.services.MailingListService;
public class MailingListRequestProcessor implements Runnable {
private Thread t;
private WebRequest request;
private MailingListService mailing;
@Override
public void run() {
System.out.println(new Date() + ": Request status afet calling the thread to start, but before calling business logic" + request);
mailing.processRequest(request);
}
public void start (WebRequest request, MailingListService mailing) {
if (t == null){
System.out.println(new Date() + ": Request status before calling the thread to start" + request);
this.request = request;
this.mailing = mailing;
t = new Thread (this, "MailchimpRequest");
t.start ();
}
}
}
2)控制器,创建线程并回答Mailchimp请求:
package es.edm.controllers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;
import es.edm.services.MailingListService;
import es.edm.util.MailingListRequestProcessor;
@Controller
@RequestMapping(path="/Webhook")
public class MailingServiceIntegrationController_MailchimpImpl {
@Autowired
MailingListService mailing;
@RequestMapping(method=RequestMethod.GET)
public ModelAndView getProcess(WebRequest request){
return new ModelAndView("/web/MailchimpAnswer.jsp");
}
@RequestMapping(method=RequestMethod.POST)
public ModelAndView postProcess(WebRequest request){
MailingListRequestProcessor processor = new MailingListRequestProcessor();
processor.start(request, mailing);
return new ModelAndView("/web/MailchimpAnswer.jsp");
}
}
3)用于实现业务逻辑的Service类:
@Override
public void processRequest(WebRequest request) {
//To recover, once solved the problem with Jelastic and getParameters();
//if (request.getParameter("secret")!= null){
//if (request.getParameter("secret").equals(conf.getMailingListSecretPassword())){
switch (request.getParameter("type")){
case "subscribe": processSubscribe(request); break;
case "unsubscribe": processUnsubscribe(request); break;
case "profile": processProfile(request); break;
case "upemail": processEmailChange(request); break;
case "cleaned": processCleanedEmail(request); break;
case "campaign": processCampaign(request); break;
}
//}
//}
}
catalina输出是:
Exception in thread "MailchimpRequest" java.lang.NullPointerException
at es.edm.services.Impl.MailingListService_Mailchimp_Impl.processRequest(MailingListService_Mailchimp_Impl.java:37)
at es.edm.util.MailingListRequestProcessor.run(MailingListRequestProcessor.java:15)
at java.lang.Thread.run(Thread.java:745)
MailingListService_Mailchimp_Impl.java:37就是这样:
switch (request.getParameter("type")){
request.getParameter(&#34; type&#34;)为null ...而在我的本地tomcat中正确填写。
谢谢!
答案 0 :(得分:0)
当您使用Jelastic时,请尝试使用Jelastic支持。我猜他们可以帮助你。
答案 1 :(得分:0)
经过长时间的研究,我最终找到了问题:
<!-- This should be removed to improve security!!!! -->
<csrf disabled="true"/>
使用Mailchimp时无法使用CSRF保护,因为它需要传递&#34;秘密&#34;参数作为GET参数,但CSRF可以将其阻止。