Have a working Struts2 portlet environment where I cannot make Struts fileUpload work, in portlet mode.
I can see that
A file is uploaded to the server, created in temp space.
A multipart request is present on the original request, with the filename
and file contents, where it enters Struts classes.
I can trigger the maximum size response, the default value set in Struts.
The FileUploadInterceptor is triggered but in the Jakarta
MultipartRequest there is no file. Somewhere between the original
request and the Jakarta wrapper the file is lost.
I have tried the same approach in a stripped down Spring boot 2 application, the same problem occurs as in the main project.
Followed the code at
https://struts.apache.org/core-developers/file-upload.html
for the JSP and action classes. The portlet is created, the flow works, but no file in the action.
Standard Sping Boot 2 with extra in pom:
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>2.5.20</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
Struts.xml
<package name="uploadtest" extends="struts-portlet-default" namespace="/uploadtest">
<action name="uploadtestPrepare" class="bouvet.no.fileuploadtest.action.FileUploadSubmitAction">
<result name="success">/WEB-INF/struts2/test/fileUpload.jsp</result>
</action>
<action name="uploadTestSubmit" class="bouvet.no.fileuploadtest.action.FileUploadSubmitAction">
<result name="success">/WEB-INF/struts2/test/fileUpload.jsp</result>
</action>
</package>
and the form
<s:form action="uploadTestSubmit" method="post" enctype="multipart/form-data">
FileName: <s:property value="%{filename}"/>
<s:file name="upload" label="File" />
<s:submit/>
</s:form>
a setter
public void setUploadFileName(String filename) {
this.filename = filename;
}
Is this a bug in portlet mode or am I missing a key dependency, a component, version? Alternative method?
In the image, a breakpoint in the JSR168 dispatcher, the file is present. First code to run after the server.
breakpoint in JSR168 dispatcher
The solution in this case was to step back a step and look at what was present in the request.
The CMS the portlet is running in is in fact taking care of the upload before the upload reach the portlet environment. The jakarta wrapper I found was created by the CMS but it is outside of the portlet scope.
Solution:
public String intercept(ActionInvocation invocation) throws Exception {
final ActionContext context = invocation.getInvocationContext();
Object action = invocation.getAction();
try {
if (action instanceof FileUploadAware) {
HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST);
// if (request instanceof MultipartRequestWrapper) {
File file = (File) request.getAttribute("upload");
((FileUploadAware) action).setFile(file);
// }
}
}catch (Exception e){
LOG.error("Exception? {}", e);
}finally {
return invocation.invoke();
}
Not a final solution but the concept, an interceptor that finds an upload attribute on the request and puts it on a FileUploadAware action. The attribute is a local temp file the CMS has intercepted and created.
This works for me, a special solution for this CMS or a general for portlets? Do not know.
Related
I have a lot of issue with CWE 117 in Veracode with my Java project.
I tried to fix it without code by using log configuration.
I add the dependency
<!-- https://mvnrepository.com/artifact/org.owasp/security-logging-logback -->
<dependency>
<groupId>org.owasp</groupId>
<artifactId>security-logging-logback</artifactId>
<version>1.1.7</version>
</dependency>
I add the conversion
<!-- Define the CRLFConverter -->
<conversionRule conversionWord="crlf" converterClass="org.owasp.security.logging.mask.CRLFConverter" />
I add the pattern in my layout
%crlf(%.-500msg)
I made the change in the logback.xml file in the resource folder.
I run another scan but the issues are still there.
Any idea why this is not fixing the CWE?
Thanks,
Nicolas
When upgrading my application to use Spring Boot version 2.1.8.RELEASE + struts2-convention-plugin with Struts2-core version 2.5.20 the actions are not being mapped correctly and I am getting the error
com.opensymphony.xwork2.config.ConfigurationException: There is no
Action mapped for namespace [/] and action name [home] associated with
context path [].
If I decalre the actions in struts.xml they work perfectly.
Below is my current configuration, why are they not mapping?
I have tried many diffrent configs and nothing seems to work, the StrutsPrepareAndExecuteFilter is firing but not actions are found, as if Spring has not scanned them. Could this be a dependency version issue?
application.yaml
server:
port: 8080
servlet:
context-path: /
Struts2.xml
<struts>
<constant name="struts.devMode" value="false" />
<constant name="struts.convention.action.packages" value="com.myactions.action" />
<constant name="struts.convention.action.includeJars" value=".*?/myjar.*?jar(!/)?,.*?/myjar*?jar(!/)?" />
<constant name="struts.objectFactory" value="spring" />
<constant name="struts.objectFactory.spring.autoWire" value="name" />
<constant name="struts.multipart.maxSize" value="100000000" />
<constant name="struts.convention.default.parent.package" value="struts-default"/>
## THIS WORKS
<!-- <package name="home" extends="struts-default">-->
<!-- <action name="actionHome" class="com.myactions.action.HomeController" method="actionHome">-->
<!-- <result name="success">home.jsp</result>-->
<!-- </action>-->
<!-- </package>-->
</struts>
Controller
#Namespace("/")
public class HomeController extends BaseController {
#Action("home")
public String actionHome() throws Exception {
return SUCCESS;
}
}
Main
#SpringBootApplication
#ServletComponentScan
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder springApplicationBuilder) {
return springApplicationBuilder.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Struts2 Filter
#WebFilter("/*")
public class Struts2Filter extends StrutsPrepareAndExecuteFilter {
}
UPDATE
After a couple days of trials and errors, here are a few clues.
Spring Boot 2.1.8 with Struts 2.5.20 configured with struts.xml works.
Your projet POM requires at least the following dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.8</version>
</dependency>
...
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>${struts2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>2.5.20</version>
</dependency>
I would also recommend adding the Config Browser Plugin to easily list the available actions:
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-config-browser-plugin</artifactId>
<version>2.5.20</version>
</dependency>
It works when you package :
a fat JAR with an embedded Tomcat server, which is the default behavior
a WAR in an external Tomcat Server
To get rid of struts.xml and configure your actions only through Java classes, the Convention plugin is required:
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-convention-plugin</artifactId>
<version>2.5.20</version>
</dependency>
Notes :
the constant defined in struts.xml can also be migrated to the struts.properties file (default values listed here)
struts.xml takes precedence over the Convention plugin and struts.properties
When struts.xml is removed, the Convention plugin takes over and Action classes are mapped using the name of packages and classes. The default conventions (action-to-URL mappings, result path...) can also be overridden thanks to a set of annotations.
Still, as mentioned by the OP, configuration through annotations does NOT work out of the box with an embedded Tomcat (it does with an external Tomcat, though).
Best I could do so far, is make it work using an embedded Jetty instead of Tomcat, and adding the recommended configuration to struts.properties:
struts.convention.exclude.parentClassLoader=false
struts.convention.action.fileProtocols=jar,code-source
The starting log still shows errors, but I'm able to access the configured actions with no more XML.
UPDATE
After the OP's update, I digged in a bit more. It turns out the Convention Plugin works fine with Tomcat embedded and no XML needed, by doing the following:
Adding the following line to the struts.properties
struts.convention.exclude.parentClassLoader=false
Upgrading asm modules asm, asm-commons and asm-tree to release 6.2 or later to prevent errors similar to
ERROR org.apache.struts2.convention.DefaultClassFinder.<init>:95 - Unable to read class [...]
Having stepped through spring2-convention-plugin the class PackageBasedActionConfigBuilder has a method buildUrlSet which gets all the urls to classes that need to be scanned for Actions this was removing the urls to my classes and hence nothing was being scanned.
There is a condition excludeParentClassLoader which needs to be set to false (line 415).
Solution
Set below constant's to -
<constant name="struts.convention.exclude.parentClassLoader" value="false" />
<constant name="struts.convention.action.fileProtocols" value="jar,code-source" />
This question already has answers here:
File Upload to Server Directory Using Spring MVC
(2 answers)
Closed 8 years ago.
I want to upload file and do process by using Spring MVC3. i tried with
#RequestMapping(value = "uploadAction.do")
public ModelAndView upload(
#RequestParam("file") CommonsMultiPartFile file
)
{
System.out.println(file);
ModelAndView view = new ModelAndView();
return view;
}
but it is not working and i have confused with #RequestParam and #ModelAttribute so please help me
The upload functionality depends on several factors factors. As by the docs, following are the things you must ensure
Make a POST request. File upload should be a POST request
#RequestMapping(value = "uploadAction.do", method=RequestMethod.POST)
Enable Spring multipart handling by adding a multipart resolver to the web application’s context
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- one of the properties available; the maximum file size in bytes -->
<property name="maxUploadSize" value="100000"/>
</bean>
Ensure commons-fileupload.jar is on your classpath, if you're using maven the following should cover you
<!-- File Upload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.0.1</version>
</dependency>
with all this in place, your mapping should work well, providing that your form is OK, here is an example
<form id="fileuploadForm" action="/uploadAction.do" method="POST" enctype="multipart/form-data" class="cleanform">
<input id="file" type="file" name="file" />
<p><button type="submit">Upload</button></p>
</form>
note also that its always better to program against an interface, by changing the argument type to
public ModelAndView upload(#RequestParam("file") MultipartFile file) {
you'll delegate injecting the implementation to the framework. The benefit is that you can write file upload test using spring mvc test framework, in which case the framework will insert the mocked implementation for the MultipartFile interface
Something like this:
#RequestMapping(value="/upload", method=RequestMethod.POST)
public String upload( #RequestParam("upload")
MultipartFile multipartFile
...
){
....
}
and in the form enctype is required:
<form id="command" name="command" method="POST" action="/upload" enctype="multipart/form-data">
....
<input id="upload" type="file" name="photo">
....
</form>
I am trying to render JSP in a Spring 3.2 using annotation driven configuration, but the JSP renders as a string and is not evaluated.
I am using the maven jetty plugin to run the webapp in development. So it seems as if everything should "just work".
The dependencies I am including to use JSP are
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
The bean to configure JSP is
#Configuration
public class WebAppConfiguration {
#Bean
public InternalResourceViewResolver internalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/views/");
resolver.setSuffix(".jsp");
return resolver;
}
}
The controller is pretty straight forward
#Controller
public class RootController {
#RequestMapping(value = "/login")
public String login() {
return "login";
}
and the JSP is also pretty easy
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%# page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head></head>
<body>
<%= "Hello World" %>
${ "Hello World" }
<form name="auth" action="<c:url value='j_spring_security_check' />" method="POST">
<label>Username: <input type="text" name="j_username"></label>
<label>Password: <input type="password" name="j_password"></label>
<input type="submit" value="Submit"/>
</form>
</body>
</html>
As you can see from the image the JSP is not being evaluated. Is there anything I need to do to tell JSP to be evaluated when rendered.
Edit 1
So just for a little extra information I used the Resthub archetype resthub-mongodb-backbonejs-archetype to bootstrap this project, which uses a WebAppInitializer rather than the older web.xml, and it uses new annotation driven beans rather than the xml beans.
EDIT 2
I have been smashing my head on this for all to long so I put the project on github https://github.com/austinbv/calendar/. Since I do not know what is important and what is not.
Thanks for the help
#austinbv Please use the SPRING LINK to check the setup. (As #Rohit has pointed you above - the missing piece)
I had the same problem when using spring boot. Adding these dependencies to the project pom.xml resolved the issue:
<dependency>
<groupId>tomcat</groupId>
<artifactId>jasper-compiler</artifactId>
<version>5.5.23</version>
</dependency>
<dependency>
<groupId>tomcat</groupId>
<artifactId>jasper-runtime</artifactId>
<version>5.5.23</version>
</dependency>
<dependency>
<groupId>tomcat</groupId>
<artifactId>jasper-compiler-jdt</artifactId>
<version>5.5.23</version>
</dependency>
The above given issue fixed for me after making following change in the "web.xml"
The spring servlet needs to be the default servlet. ie mapped to / and not /*.
Ref link: https://code-examples.net/en/q/b49ce1
You need to specify the appropriate view class
public InternalResourceViewResolver internalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setViewClass(org.springframework.web.servlet.view.JstlView.class);
resolver.setPrefix("/views/");
resolver.setSuffix(".jsp");
return resolver;
}
I do not know how much actual will be my answer, but I had exactly the same issue (Spring + boot + maven + tomcat).
I solved it by removing the scope-provided from tomcat.embed dependence. So, my dependence now looks like this:
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
Because JSP does not obey MVC pattern :P
I have several REST services defined that are currently returning JSON formatted objects as service response bodies. I'm trying to make this service also accept XML as a new requirement though it does not accept this.
I'm following the spring-mvc-showcase sample project and have setup my pom.xml dependencies almost identically, my service definitions likewise are very simple.
#Controller
#RequestMapping(value = "api/sales/*")
public class SalesController {
#RequestMapping(value = "/countries", method = RequestMethod.GET)
#ResponseBody
public List<NamedEntity> getCountries() {
NamedEntity has the appropriate #XmlRootElement annotation.
Could somebody explain the most basic requirements that I would need to get XML as a ResponseBody that the spring-mvc-showcase sample project is using.
EDIT: Added spring MVC sample.
The sample from the spring-mvc-showcase is as follows:
package org.springframework.samples.mvc.messageconverters;
#Controller
#RequestMapping("messageconverters/*")
public class MessageConvertersController {
#RequestMapping(value="/xml", method=RequestMethod.GET)
public #ResponseBody JavaBean writeXml() {
return new JavaBean("bar", "fruit");
Check the request header, client needs to have "application/xml" in the header, rather than "application/json "
Having said this make sure you have registered appropriate message converter for your object. If you are using Java 6 then Spring will auto detect JAXB in your classpath or else you can manually add the converter.
Add #Produces("application/xml") to getCountries()
You need to send "application/xml", not "application/application+xml". Also consider using:
#RequestMapping(value = "/countries", method = RequestMethod.GET, produces={"application/json", "application/xml"})
This ensures your method responds to those media types only and rejects others with 406 HTTP status code.
try this dispatcher servlet config.
<mvc:annotation-driven
content-negotiation-manager="contentManager" />
<bean id="contentManager"
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="false" />
<property name="ignoreAcceptHeader" value="false" />
<property name="defaultContentType" value="application/json" />
<property name="useJaf" value="false" />
</bean>
and some dependency
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.4.3</version>
</dependency>