I have a form and in a HTML form inside which I have file upload button.
File is going to be uploaded using AJAX request.
On file upload I am checking extension and file size.
I am uploading file into /home/xyz/upload/username/username_timestamp
But since I am using AJAX request, I cannot(and don't want to) use the CAPTCHA and there is a risk of attacker flooding with files. How can I deal with this issue?
PS: I am using Spring-MVC(not spring security) and Struts2 in my projects, so framework specific solution will be icing on a cake.
Protect your action to be called out of expected by using Strut's token mechanism and your expected logic of file uploads inside your parent action (e.g. max uploads per minutes) ; something like below:
index.jsp
<form action="upload" method="post" enctype="multipart/form-data">
<label for="myFile">Upload your file</label>
<input type="file" name="myFile" />
<s:if test="uploadsPerMinutes < 10">
<s:token name="tknUpload" /> <!-- *** conditional generate token *** -->
</s:if>
<sj:submit value="Submit Form" targets="myAjaxTarget"/>
</form>
<div id="myAjaxTarget">
</div>
struts.xml
<action name="upload" class="com.upload.FileUpload">
<interceptor-ref name="tokenSession/>
<interceptor-ref name="basicStack"/>
<result name="success" type="stream">...</result>
</action>
<action name="uploadParent" class="com.upload.FileUploadParent">
<result name="success">index.jsp</result>
</action>
FileUploadParent.java
public class FileUploadParent extends ActionSupport{
...
public static int uploadsPerMinutes = 0;
private static DateTime lastUploadTime;
public String execute()
{
...
synchronized(uploadsPerMinutes){
if(currentTime - lastUploadTime > 1min) uploadsPerMinutes=0;
else uploadsPerMinutes++;
lastUploadTime = currentTime;
}
return SUCCESS;
}
public String getUploadsPerMinutes()
{
return uploadsPerMinutes;
}
}
With these, client have to get a token from server for each file upload. These are behind the scene and do not disturb your normal users.
Related
It is necessary that the file is downloaded either staying on the current page, or a new one opens. Tell me how to do it. Here is what has already been done.
public FileResult DownloadFile(int id)
{
FilesDB filesDB = _context.FilesDB.Find(id);
filesDB.DownloadCount++;
_context.SaveChanges();
string downloadName= filesDB.FileDisplayName +
Path.GetExtension(filesDB.FilePath);
return File(filesDB.FilePath, filesDB.FileType, downloadName);
}
You can download both way. Just use HTML form.
Here I'm using WebService:
<form id="downloadForm" action="MyService.asmx/DownloadFile" method="post" target="_self" >
<input type="hidden" name="id" id="txtID" />
</form>
JS:
function downloadFile() {
$('#txtID').val(id);
$('#downloadForm').submit();
}
Just call downloadFile() and this will download staying on current page.
If you want to open in new one just change target="_self" to target="_blank"
I am bit new to Java web programming
I have two buttons(Test connection and Execute) in an html form and the form action is a servlet. How can I differentiate the action in the servlet based on which button is clicked.
Thanks
In the HTML:
<input type='submit' name='submitButton' value='Test connection' />
<input type='submit' name='submitButton' value='execute' />
In the servlet:
String clickedButtonValue = request.getParameter("submitButton");
if("Test connection".equals(clickedButtonValue))
{
...
}
else if("execute".equals(clickedButtonValue))
{
...
}
else ...
The reason this works: With both submit buttons sharing the same name attribute, the browser will only send the value of the one that was clicked.
I am uploading a picture using form-data taking the picture from the Client's PC to Spring MVC Server.
Update Page:
<!DOCTYPE html >
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Spring MVC - Upload File</title>
</head>
<body>
<form id="form1" method="post" action="/upload" enctype="multipart/form-data" accept-charset="utf-8">
<!-- File input -->
<input name="file" id="file" type="file" /><br/>
<input type="submit" value="Upload" />
</form>
</body>
</html>
And this is the Spring Controller
#RequestMapping(value = "upload", method = RequestMethod.POST)
public #ResponseBody
String provaUpdate(MultipartHttpServletRequest request,Principal p,HttpServletResponse response)throws IOException {
String result="";
LocalFileManager mLocalFileManager = LocalFileManager.get();
Iterator<String> iterator = request.getFileNames();
while(iterator.hasNext())
{
System.out.println("iterator.next()="+iterator.next());
}
System.out.println("request.getFileMap().isEmpty()??"+request.getFileMap().isEmpty());
// mLocalFileManager.saveLocalData(g,g.getPicturesCount(), request.getFile("new").getInputStream());
return result;
}
So, when i run that, it just prints:
request.getFileMap().isEmpty()??true
Then, no Files seems to be uploaded, but if I get the request.getInputStream() I can write a TXT File showing that:
------WebKitFormBoundaryWG8vA5PuTFFxPBqK
Content-Disposition: form-data; name="file"; filename="1.jpg"
Content-Type: image/jpeg
�� JFIF ��ICC_PROFILE 蠠 mntrRGB XYZ ٠ $ acsp �� ӭ )B9
desc D ybXYZ bTRC Ԡ dmdd ࠠ ɧXYZ
h gTRC Ԡ lumi
| meas
//(Symbols) Long ETC, so the picture is sent !!
------WebKitFormBoundaryWG8vA5PuTFFxPBqK--
So, seems that the picture is sent correctly but the MultipartHttpServletRequest is not able to get the File.
Which is my mistake?
I think,you may be missing below entry in your spring configuration xml file.
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- below properties can be configured as per your need -->
<property name="maxUploadSize" value="5000000" />
<property name="maxInMemorySize" value="5000000" />
</bean>
The mistake was that i am working with Thymeleaf, so the form is different.
It is solved changing that:
1- Create a class that contains a MultipartFile, like this:
public class Images {
MultipartFile image;
public MultipartFile getImage() {
return image;
}
public void setImage(MultipartFile image) {
this.image = image;
}
}
2- The correct form code for a Spring Server working with Thymeleaf:
<form id="myform" action="#" th:action="#{/upload}" th:object="${Images}" method="POST" modelAttribute="Images" enctype="multipart/form-data">
<input type="file" th:field="${Images.image}" name="file"/>
<input type="submit" value="Upload"/>
</form>
And finally the correct Controller method:
#RequestMapping(value = "upload", method = RequestMethod.POST)
public String addVocabularyValadate( #ModelAttribute("Images") Images images,BindingResult bindingResult,
Model model) throws IOException
{
System.out.println("inputstream Nombre!"+images.getImage().getOriginalFilename());
if(bindingResult.hasFieldErrors() == true)
return "error";
else
return "upload OK!";
}
I'm using Spring (3.1), Spring MVC (3.1) and Spring Security (3.0) in combination and I've put together a single JSP page that has two forms on it; One is a form to login and the other is a form to register (i.e. create a new user).
For the register form, I use the Spring form tags, backed up by a controller to handle the request but for the login form I don't bother with the Spring form tags as I don't believe they're needed. There is also no controller that I need to write to handle the form submission as Spring Security takes care of authenticating so long as the request is submitted to j_spring_security_check.
The register form is working fine but the login form is a problem. It seems that when I click the submit button on the login form, the registration form is also submitted, or at least Spring thinks I'm trying to submit that form. Here is the JSP:
<form id="loginform" method="POST" action="<c:url value='j_spring_security_check'/>">
<label for="existing_email">Email:</label>
<input name="j_username" id="existing_email" type="text" value="${SPRING_SECURITY_LAST_USERNAME}" />
<label for="existing_password">Password:</label>
<input name="j_password" id="existing_password" type="password" />
<input id="login-form-submit" type="submit" value="Sign in" />
</form>
<form:form id="registrationform" modelAttribute="user" method="POST" action="register">
<form:label path="username" for="email">Email:</form:label>
<form:input path="username" name="username" id="email" type="text" />
<form:errors path="username" cssClass="formError" />
<form:label path="password" for="password">Password:</form:label>
<form:input path="password" name="password" id="password" type="password" />
<form:errors path="password" cssClass="formError" />
<input id="registration-form-submit" type="submit" value="Sign up" />
</form:form>
Notice that form tags for the input of type submit are not present and this seems to be a normal thing to do in the examples I've seen. Adding form tags to the submit button I guess doesn't make sense as it doesn't map to anything on the target object (user in this case).
When I click the "Sign in" button I get the following exception:
SEVERE: Servlet.service() for servlet [appServlet] in context with path [/project1] threw exception [An exception occurred processing JSP page /WEB-INF/views/registration.jsp at line 29
28: <form:form id="registrationform" modelAttribute="user" method="POST" action="register">
29: <form:label path="username" for="username">Username:</form:label>
30: <form:input path="username" name="username" id="username" type="text" />
31: <form:errors path="username" cssClass="formError" />
32:
Stacktrace:] with root cause
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'user' available as request attribute
at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:141)
This I recognise from cases where you forget to include the modelAttribute attribute in form:form, but of course I don't want to submit this form to my controller.
I have a feeling there is a mistake I'm making or a simple solution. Can anyone recommend a way around this or perhaps a different approach?
Here is the controller method that handles requests to register in case that's needed:
#RequestMapping(value = "**/register", method = RequestMethod.POST)
public String registerUser(#ModelAttribute("user") #Validated User user, BindingResult errors, ModelMap model) {
if (errors.hasErrors()) {
return "registration";
}
// Other stuff then...
return "profile"
}
If you are using "user" modelAttribute in form tag then a non-null request attribute must be present with name "user".
One way to add that in request attribute is what you did in your answer above. Other ways are:
(1) Add in ModelMap:
#RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
public String loginFailed(ModelMap model) {
model.addAttribute("user", new User());
model.addAttribute("error", "true");
return "registration";
}
(2) Add in request scope (Using WebRequest or HttpServletRequest):
#RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
public String loginFailed(ModelMap model, WebRequest webRequest) {
webRequest.setAttribute("user", new User(), WebRequest.SCOPE_REQUEST);
model.addAttribute("error", "true");
return "registration";
}
(3) Use #ModelAttribute on method:
#ModelAttribute("user")
public User user() {
return new User();
}
Please also see Using #ModelAttribute on a method and Using #ModelAttribute on a method argument
Also note that you don't have to use type attribute. See form:input and form:password.
I think the problem is specifically when a login fails and the same page is served up, albeit on a different URL path and so through a different controller method. Therefore my original suspicion that the issue is that both forms are submitted may be something of a red herring, though I still don't fully understand what's going on and that may yet have something to do with it. In any case, this is what corrected the problem for me:
I had a controller method that originally looked like this:
#RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
public String loginFailed(ModelMap model) {
model.addAttribute("error", "true");
return "registration";
}
In the Spring Security context I specify /loginfailed as the path to go to by default if a login attempt fails. This is where it seems the user object is needed so if I alter the signature as follows it all works:
#RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
public String loginFailed(#ModelAttribute("user") User user, BindingResult errors, ModelMap model) {
model.addAttribute("error", "true");
return "registration";
}
Any comments/clarification welcome from others.
Request.Files doesn't work when I deploy to the server but it works when I test locally. What do I need to configure to make this work when deployed?
Some example of your code would be nice, but this post helped me once:
A Back To Basics Case Study: Implementing HTTP File Upload with ASP.NET MVC including Tests and Mocks
This post from Phil Haack can be useful for you:
<form action="" method="post" enctype="multipart/form-data">
<label for="file">Filename:</label>
<input type="file" name="file" id="file" />
<input type="submit" />
</form>
[HttpPost]
public ActionResult Index(HttpPostedFileBase file) {
if (file.ContentLength > 0) {
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
file.SaveAs(path);
}
return RedirectToAction("Index");
}
Note that here you do not examine the Request.Files collection. Also example for the multiple file upload can be found there (main idea that you should use the IEnumerable<HttpPostedFileBase> in similar manner).