Implement Antibot Captcha with Java Spring MVC using Thymeleaf and JQuery AJAX - ajax

I'm trying to implement Captcha with my Spring MVC application. Although there are some examples of how to do that, I couldn't find any where the form is submitted with JQuery AJAX and the template engine is Thymeleaf.
My main source of information is this.
I've added the dependencies and the servlet, but I'm struggling with the template part (where I'm using Thymeleaf and JQuery AJAX to send my form to the controller) and to do the validation in the controller.
I don't necessarily want to use Captcha as my antibot framework, so if you have any ideas using other framework, I would be more than glad to hear them.

Ok, so I ended up using Cage Captcha generator. It can be integrated with Maven and it's fairly easy to be implemented with a Spring MVC application with JQuery AJAX.
/**
* Generates captcha as image and returns the image path
* stores the captcha code in the http session
* and deletes older, unused captcha images.
*/
#RequestMapping(value = "/captcha/generate", method = RequestMethod.GET, produces="application/json")
#ResponseBody
public ResponseEntity<CaptchaRequestData> generateCaptcha(HttpSession session) {
String captchaImageUploadDirectory = environment.getProperty("captcha_image_folder");
String captchaWebAlias = environment.getProperty("captcha_web_alias");
//Creating dir or making new one if it doesn't exist
File file = new File(captchaImageUploadDirectory);
if (!file.exists()) {
try {
file.mkdirs();
} catch(Exception e){}
}
String timeSuffix = DBUtils.getDateTimeAsString();
String fileName = CAPTCHA_IMAGE_PREFIX + timeSuffix + "." + CAPTCHA_IMAGE_EXTENSION;
String fullFilename = captchaImageUploadDirectory + fileName;
//Generating the captcha code and setting max length to 4 symbols
Cage currGcage = new YCage();
String captchaToken = currGcage.getTokenGenerator().next();
if (captchaToken.length() > CAPTCHA_CODE_MAX_LENGTH) {
captchaToken = captchaToken.substring(0, CAPTCHA_CODE_MAX_LENGTH).toUpperCase();
}
//Setting the captcha token in http session
session.setAttribute("captchaToken", captchaToken);
try {
OutputStream os = new FileOutputStream(fullFilename, false);
currGcage.draw(captchaToken, os);
os.flush();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
deleteFilesOlderThan(captchaImageUploadDirectory, CAPTCHA_IMAGE_LIFE_MILLISECONDS, CAPTCHA_IMAGE_EXTENSION);
CaptchaRequestData data = new CaptchaRequestData(captchaWebAlias + fileName);
return new ResponseEntity<>(data, HttpStatus.OK);
}
Then when I'm creating the object I check if the given code equals the one, stored in the session:
if (!httpSession.getAttribute("captchaToken").equals(bindingData.getCaptchaCode())) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
Finally if the provided captcha is incorrect I generate a new one.

Related

Spring Controller does nothing

Greeting my dear fellows
I would appreciate some help since I've been 2 days googling to find out why my code is not working. My webapp is Spring running in a Weblogic Server under Eclipse. Btw, apologies for my spelling (I am not native English speaker)
Straight from my webapp, the following controller works flawless
#RequestMapping(value = "/sendFile")
public ModelAndView vistaEnvioFicheros() throws myCustomException {
ModelAndView model = null;
try {
getLog().debug("Setting model for sending a file");
model = new ModelAndView("/content/sendFileWeb");
} catch (Exception ex) {
getLog().error("Shxx happens: ", ex);
throw new myCustomException(ex.getMessage(), ex);
}
return model;
}
This controller loads a jsp file with a file browser button and a file upload button which works great too.
So when I click on the upload button the following controller triggers:
#RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
public Object subirFichero(#RequestParam(value = "file") MultipartFile file) throws myCustomException {
ModelAndView model = null;
if (file.isEmpty()){
try {
getLog().debug("File is empty");
model = new ModelAndView("/content/errorWeb");
} catch (Exception ex) {
getLog().error("Shxx happens again ", ex);
throw new myCustomException(ex.getMessage(), ex);
}
return model;
}
...
}
Problem is: when I upload empty file, errorWeb jsp file should be shown in my web browser, however nothing happens. If I debbug the controlles I can see the code runs propperly until return model sentence nonetheless errorWeb jsp file is not loaded
Could anyone give me a hint about why first controller loads it jsp view but second controller doesn't. Also I don't get a single error message in any console or whatever
Thank you very much
Ok, I got it solved! This was just a newbie thing (I am a newbie web developper).
The problem was in the client side, where my webapp was sending the request thay triggers the second controller, not by an standard href o after a submit (or whatever), but by a custom javascript function.
This custom javascript function firstly validates the file and then uploads it to the server. After that client side code stops listening the response from the server. That's why the new ModelAndView is ignored.
Thank you for your time to all of you who read the post

What is the callback URL after calling repeat.vsp when using Form Integration?

I'm trying to do repeat payments with Form Integration in Sagepay (now Opayo).
From an earlier problem posted on here, I get that the securitykey is needed but is not returned in the Form call, so an additional call needs to be made to the getTransactionDetails command.
I have the securitykey and can now make a call to https://test.sagepay.com/gateway/service/repeat.vsp to initiate the repeat payment. However, the documentation does not say where the response to that call goes. I assume therefore, that it would go to the NotificationURL that is set up with a payment when using the Server or Direct integrations. Since I'm using Form, this is not set.
The question is, is there any way of capturing the response to the https://test.sagepay.com/gateway/service/repeat.vsp call if the initial payment was created using Form integration?
I suppose the second question is, has anybody successfully made repeat payments work with Sagepay Form integration?
Not sure if this helps you and we didn't do repeat payments; but we are looking at releasing deferred payments and I think it is a similar approach.
How do you make the call to 'https://test.sagepay.com/gateway/service/repeat.vsp'?
Could you use a 'HttpWebRequest' to make the call then capture the direct response in 'HttpWebResponse'?
EG:
private static void DeferredSharedApiCall(Dictionary<string, string> data, string type, string url)
{
string postData = string.Join("&", data.Select(x => $"{x.Key}={HttpUtility.UrlEncode(x.Value)}"));
HttpWebRequest request = WebRequest.CreateHttp(url);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
using (TextWriter tw = new StreamWriter(request.GetRequestStream()))
{
tw.Write(postData);
}
HttpWebResponse response = null;
try
{
response = request.GetResponse() as HttpWebResponse;
}
catch (WebException ex)
{
//log.Error($"{type} Error, data: {postData}", ex);
}
catch (Exception ex)
{
//log.Error($"{type} Error, data: {postData}", ex);
}
if (response != null)
{
using (TextReader tr = new StreamReader(response.GetResponseStream()))
{
string result = tr.ReadToEnd();
//log.Info($"{type} Response: {Environment.NewLine}{result}");
}
}
}

loading a pdf in-browser from a file in the server file system?

How can I get a pdf located in a file in a server's directory structure to load in a browser for users of a Spring MVC application?
I have googled this and found postings about how to generate PDFs, but their answers do not work in this situation. For example, this other posting is not relevant because res.setContentType("application/pdf"); in my code below does not solve the problem. Also, this other posting describes how to do it from a database but does not show full working controller code. Other postings had similar problems that caused them not to work in this case.
I need to simply serve up a file (not from a database) and have it been viewable by a user in their browser. The best I have come up with is the code below, which asks the user to download the PDF or to view it in a separate application outside the browser. What specific changes can I make to the specific code below so that the user automatically sees the PDF content inside their browser when they click on the link instead of being prompted to download it?
#RequestMapping(value = "/test-pdf")
public void generatePdf(HttpServletRequest req,HttpServletResponse res){
res.setContentType("application/pdf");
res.setHeader("Content-Disposition", "attachment;filename=report.pdf");
ServletOutputStream outStream=null;
try {
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(new File("/path/to", "nameOfThe.pdf")));
/*ServletOutputStream*/ outStream = res.getOutputStream();
//to make it easier to change to 8 or 16 KBs
int FILE_CHUNK_SIZE = 1024 * 4;
byte[] chunk = new byte[FILE_CHUNK_SIZE];
int bytesRead = 0;
while ((bytesRead = bis.read(chunk)) != -1) {outStream.write(chunk, 0, bytesRead);}
bis.close();
outStream.flush();
outStream.close();
}
catch (Exception e) {e.printStackTrace();}
}
Change
res.setHeader("Content-Disposition", "attachment;filename=report.pdf");
To
res.setHeader("Content-Disposition", "inline;filename=report.pdf");
You should also set the Content Length
FileCopyUtils is handy:
#Controller
public class FileController {
#RequestMapping("/report")
void getFile(HttpServletResponse response) throws IOException {
String fileName = "report.pdf";
String path = "/path/to/" + fileName;
File file = new File(path);
FileInputStream inputStream = new FileInputStream(file);
response.setContentType("application/pdf");
response.setContentLength((int) file.length());
response.setHeader("Content-Disposition", "inline;filename=\"" + fileName + "\"");
FileCopyUtils.copy(inputStream, response.getOutputStream());
}
}

calling a spring controller from xhrPost doesnot redirect

I am calling a spring controller method from dojo xhrPost and I need to redirect to a new html from the controller method. But controller returns back to the dojo in javascript instead of moving to a new page.
my javascript:
var xhrArgs={
url:"/tradeIn/submitTradeIn",
postData:dojo.toJson(tradeInDetails),
handleAs:"text",
headers:{"Content-Type":"application/json"}
/*load:function(data){
console.log(data);
label=data;
if(data =="fail"){
location.reload(true);
window.location="/Fail";
}
else{
window.location="/success";
}
}*/
}
var deferred=dojo.xhrPost(xhrArgs);
my spring controller:
#RequestMapping(value="/tradeIn/submitTradeIn", method = {RequestMethod.POST})
public String submitTradeIn(#RequestBody TradeInDetails tradeDetails) throws UnsupportedEncodingException{
List<byte[]> labelList=new ArrayList<byte[]>();
ShippingInfo shippingInfo=new ShippingInfo();
shippingInfo.setAddress1(tradeDetails.getCustomerDetails().get(0).getAddress1());
shippingInfo.setAddress2(tradeDetails.getCustomerDetails().get(0).getAddress1());
shippingInfo.setCity(tradeDetails.getCustomerDetails().get(0).getCity());
shippingInfo.setCompany(tradeDetails.getCustomerDetails().get(0).getCompany());
shippingInfo.setDayPhone(tradeDetails.getCustomerDetails().get(0).getDayPhone());
shippingInfo.setEmail(tradeDetails.getCustomerDetails().get(0).getEmail());
shippingInfo.setEvePhone(tradeDetails.getCustomerDetails().get(0).getEvePhone());
shippingInfo.setFirstName(tradeDetails.getCustomerDetails().get(0).getFirstName());
shippingInfo.setLastName(tradeDetails.getCustomerDetails().get(0).getLastName());
shippingInfo.setState(tradeDetails.getCustomerDetails().get(0).getState());
shippingInfo.setZip(tradeDetails.getCustomerDetails().get(0).getZip());
shippingInfo.setCountry(tradeDetails.getCustomerDetails().get(0).getCountry());
List<ReturnRequestLabel> label;
List<TradeInClubs> tradeInList1= new ArrayList<TradeInClubs>();
for(ClubDetails cl: tradeDetails.getClubDetails()){
TradeInClubs tradeInclubs1=new TradeInClubs();
tradeInclubs1.setClubMaterial(cl.getShaftType());
tradeInclubs1.setClubType(cl.getClubType());
tradeInclubs1.setManufacturer(cl.getClubManufacturer());
tradeInclubs1.setModel(cl.getClubModel());
tradeInclubs1.setTradeInValue(cl.getTradeInPrice());
tradeInList1.add(tradeInclubs1);
}
try{
ReturnFedexLabel returnFedexLabel = new ReturnFedexLabel();
label=returnFedexLabel.fetchFedexLabel(tradeInList1, shippingInfo);
byte[] labelImageData;
String fedexLabelNumber=null;
for(ReturnRequestLabel rl: label){
labelImageData=rl.fedexReturnLabel.imageData;
labelList.add(labelImageData);
fedexLabelNumber=rl.trackingNumber;
}
File f=new File("label.jpg");
BufferedImage img=ImageIO.read(new ByteArrayInputStream(labelList.get(0)));
ImageIO.write(img,"JPEG",f);
int id=tradeInDao.insertQuery(shippingInfo,tradeInList1,fedexLabelNumber);
byte[] pdfData=fedexLabelToPdf.printFedexLabel(labelList);
emailTradeIn.emailTradeInDetails(pdfData,tradeDetails.getCustomerDetails().get(0).getEmail(),tradeInList1,id);
System.out.println("here");
} catch (Exception e){
logger.error(e.getMessage());
return "fail";
}
return "success";//Base64.encodeBase64String(labelList.get(0));
}
it is not moving to success page. it just stays in the current page
Check and see what deferred is, it is probably "success".
From http://dojotoolkit.org/reference-guide/1.7/quickstart/ajax.html:
dojo.xhrPost xhrPost will create an Ajax request using the HTTP POST
method and is usually used to submit data to a service. It returns
data to a callback. The callback is defined as a member of the object
used to create the request (the property-bag), or by using the
dojo.Deferred.then() method.
For complete details and examples, see the dojo.xhrPost documentation.
if you need to change the page from the controller it shouldn't be an ajax request. If you can change it from the client side then you can continue with the ajax request and respond accordingly.

Uploading more than one image

Dear All,
Working on Spring MVC. I want to upload more than one images from the client. How to achieve it. I know how to handle the multipart form data for single image. But now I am expecting some data with some images from the client.
Any help or url that will help me.
Thanks,
Op
Image is also a file. Whether you would be storing it in database / in file system but it is still a file.
In spring MVC, you could do as shown in the below link:
http://viralpatel.net/blogs/spring-mvc-multiple-file-upload-example/
Here are the code i tried and it is working fine at my end.
//Handle multiple images
#RequestMapping(method = RequestMethod.POST, value="upload", consumes=MediaType.MULTIPART_FORM_DATA_VALUE,
produces=MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody JSONResponse uploadImages(HttpServletRequest req)
throws Exception {
try{
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) req;
Set set = multipartRequest.getFileMap().entrySet();
Iterator i = set.iterator();
while(i.hasNext()) {
Map.Entry me = (Map.Entry)i.next();
String fileName = (String)me.getKey()+"_"+System.currentTimeMillis();
MultipartFile multipartFile = (MultipartFile)me.getValue();
System.out.println("Original fileName - " + multipartFile.getOriginalFilename());
System.out.println("fileName - " + fileName);
saveImage(fileName, multipartFile);
}
}
catch(Exception e){
e.printStackTrace();
}
return new JSONResponse();
}

Resources