Objective - I am creating my own captcha wherein I have a client from where I will do a REST
call at the Java backend. In the backend, I have a program that will generate a random string and then it will be converted to an Image which then will be returned to the Client side for rendering so that User can enter the same characters as in the image and Captcha can be validated.
Approach - I have a REST call which returns javax.ws.rs.core.Response
and in that, I am binding my CaptchaImage so that it can be rendered to the browser. The approach is somewhat similar to How to return a PNG image from Jersey REST service method to the browser
Code: ICaptchaServlet.java - Interface
import java.io.IOException;
import java.security.GeneralSecurityException;
import javax.servlet.http.*;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
public interface ICaptchaServlet{
public Response processRequest(@Context HttpServletRequest req) throws GeneralSecurityException, IOException;
@GET
@Path("/getChallenge")
public Response doGet(@Context HttpServletRequest req) throws GeneralSecurityException;
}
CaptchaServlet.java
import java.awt.Color;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
//Internal Rest Service annotation which it picks up fine
@Named("CaptchaServlet")
@Path("/reCaptchaImg")
@Produces("image/png")
public class CaptchaServlet implements ICaptchaServlet{
@Override
public Response processRequest(HttpServletRequest request) throws GeneralSecurityException, IOException {
System.out.println("shaan Inside processRequest");
int width = 150;
int height = 50;
//Random string generation happens
Random r = new Random();
int length = 6;
System.out.println("shaan Creating string parameter");
StringBuffer captchaStringBuffer = new StringBuffer();
for (int i = 0; i < length; i++) {
int baseCharNumber = ((r.nextInt((500-1)+1)+1) % 62);
int charNumber = 0;
if (baseCharNumber < 26) {
charNumber = 65 + baseCharNumber;
}
else if (baseCharNumber < 52){
charNumber = 97 + (baseCharNumber - 26);
}
else {
charNumber = 48 + (baseCharNumber - 52);
}
captchaStringBuffer.append((char)charNumber);
}
String captchaString = captchaStringBuffer.toString();
System.out.println("The random string generated is : "+captchaString);
char[] s3 = captchaString.toCharArray();
System.out.println("shaan1");
//Converting Captcha String to Image
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bufferedImage.createGraphics();
System.out.println("shaan2");
Font font = new Font("Georgia", Font.BOLD, 18);
g2d.setFont(font);
System.out.println("shaan3");
RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
System.out.println("shaan4");
rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHints(rh);
System.out.println("shaan5");
GradientPaint gp = new GradientPaint(0, 0, Color.red, 0, height / 2, Color.black, true);
g2d.setPaint(gp);
g2d.fillRect(0, 0, width, height);
g2d.setColor(new Color(255, 153, 0));
System.out.println("shaan6");
String captcha = String.copyValueOf(s3);
System.out.println("shaan7");
int x = 0;
int y = 0;
for (int i = 0; i < s3.length; i++) {
x += 10 + (Math.abs(r.nextInt()) % 15);
y = 20 + Math.abs(r.nextInt()) % 20;
g2d.drawChars(s3, i, 1, x, y);
}
g2d.dispose();
System.out.println("shaan8");
System.out.println("shaan9");
//Sending Image in output
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "png", baos);
byte[] imageData = baos.toByteArray();
System.out.println("shaan10" + captcha);
request.getSession().setAttribute("captcha", captcha);
System.out.println("shaan11");
baos.flush();
baos.close();
System.out.println("shaan12");
System.out.println("The imageData is :"+imageData);
// uncomment line below to send non-streamed
//return Response.ok(imageData).build();
// uncomment line below to send streamed
return Response.ok(new ByteArrayInputStream(imageData)).build();
}
@Override
public Response doGet(HttpServletRequest request) throws GeneralSecurityException {
System.out.println("shaan inside doGet");
Response response = null;
try {
response = processRequest(request);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return response;
}
}
Issue: I have put in System log statements to help trace the issue in a better way. When I execute this the Rest Service call does not return SUCCESS and goes to ERROR callback function. The image is not rendered. I have tried both the below approaches :
// uncomment line below to send non-streamed
//return Response.ok(imageData).build();
// uncomment line below to send streamed
// return Response.ok(new ByteArrayInputStream(imageData)).build();
Logs :
shaan inside doGet
shaan Inside processRequest
shaan Creating string parameter
The random string generated is : o06k5g
shaan1
shaan2
shaan3
shaan4
shaan5
shaan6
shaan7
shaan8
shaan9
shaan10o06k5g
shaan11
shaan12
The imageData is :[B@3c83339
23:35:49.192 [[ACTIVE] ExecuteThread: '10' for queue: 'weblogic.kernel.Default (self-tuning)'] DEBUG o.a.c.ws.policy.PolicyOutInterceptor - No binding operation info.
23:35:49.200 [[ACTIVE] ExecuteThread: '10' for queue: 'weblogic.kernel.Default (self-tuning)'] DEBUG o.a.c.j.i.JAXRSOutInterceptor - Response content type is: image/png
23:35:49.213 [[ACTIVE] ExecuteThread: '10' for queue: 'weblogic.kernel.Default (self-tuning)'] DEBUG o.a.cxf.ws.addressing.ContextUtils - retrieving MAPs from context property javax.xml.ws.addressing.context.inbound
23:35:49.213 [[ACTIVE] ExecuteThread: '10' for queue: 'weblogic.kernel.Default (self-tuning)'] DEBUG o.a.cxf.ws.addressing.ContextUtils - WS-Addressing - failed to retrieve Message Addressing Properties from context
23:35:49.219 [[ACTIVE] ExecuteThread: '10' for queue: 'weblogic.kernel.Default (self-tuning)'] DEBUG o.a.c.t.servlet.ServletController - Finished servicing http request on thread: Thread[[ACTIVE] ExecuteThread: '10' for queue: 'weblogic.kernel.Default (self-tuning)',5,Pooled Threads]