Ajax status 0 when calling the servlet - ajax

Hi I'm trying to just get a simple string returned from the servlet using Ajax, but nothing was ever returned 'cause the status is always 0 while readystate is 4.
Here's the .js code
function validate(choice) {
//var url = "http://localhost:8080/examples/validate.do?id=" + escape(choice);
var url = "../../validate.do"
if(window.XMLHttpRequest) {
req = new XMLHttpRequest();
}else if(window.ActiveXObject) {
req = new ActiveXObject("MSXML2.XMLHTTP.3.0");
}
alert("IM IN VALIDATE() with " + choice);
req.open("GET", url, true);
req.onreadystatechange = callback;
req.send(null);
return false;
}
function callback() {
if(req.readyState == 4 ) {
if(req.status == 200){
var check = req.responseText;
alert(check);
}
else
alert(req.status);
}
}
and Java code
package model;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class DoAjaxServlet extends HttpServlet {
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
response.setContentType("text/html");
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Cache-Control", "no-store");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
String resultStr = "JUST RETURNING THIS STRING";
out.write(resultStr);
} finally {
out.close();
}
}
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
#Override
public String getServletInfo() {
return "Short description";
}
}
I'm running this on Tomcat 7 using Chrome, and accessed the html file from localhost:8080 not instead of running local, so a lot of solutions floating around won't work.
Going to
http://localhost:8080/examples/validate.do
in Chrome it prints the string just fine, so I think I didn't write the url wrong. The .js file are at somewhere like
http://localhost:8080/examples/jsp/HTE/my.js
I also tried using "http://localhost:8080/examples/validate.do" directly as url in .js and adding the setHeader("Access-Control-Allow-Origin", "*") to Java file but nothing changes.
After searching around in the posts I'm running of ideas on this one... Would you kindly tell me where this might go wrong?

Related

set content range in spring

I have many resting api in my spring boot application.
#GetMapping("lc/all")
List<LC> getAll()
{
return lcRepository.findAll();
}
Mainly they are sending list.
Now for some reason, I have to receive the length of the response. Changing for each and every method would be tedious.
How can I set Content-Range for each method automatically.
I have modified the CORS:
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/**")
.allowedOrigins("http://localhost:5000")
.exposedHeaders("Content-Range");
}
};
}
First you need a filter that writes only the requested range part as a response when range header is present.
package com.example.contentrange;
import static java.nio.charset.StandardCharsets.UTF_8;
import static javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT;
import static javax.servlet.http.HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE;
import java.io.IOException;
import java.util.Arrays;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpRange;
import org.springframework.web.util.ContentCachingResponseWrapper;
#WebFilter("/*")
public class AddResponseHeaderFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
var rangeHeader = httpServletRequest.getHeader("Range");
// if there is no range request in the header than do the "normal" filtering
if (rangeHeader == null) {
chain.doFilter(request, response);
return;
}
HttpRange range = HttpRange.parseRanges(rangeHeader).get(0);
ContentCachingResponseWrapper responseWrapper =
new ContentCachingResponseWrapper((HttpServletResponse) response);
try {
chain.doFilter(request, responseWrapper);
} finally {
byte[] copy = responseWrapper.getContentAsByteArray();
int size = responseWrapper.getContentSize();
int lower = (int) range.getRangeStart(size);
int upper = (int) range.getRangeEnd(size);
if (lower <= size) {
responseWrapper.setStatus(SC_PARTIAL_CONTENT);
byte[] subArray = Arrays.copyOfRange(copy, lower, upper + 1);
String newContent = new String(subArray, UTF_8);
responseWrapper.reset();
responseWrapper.setHeader(
"Content-Range", String.format("bytes %d-%d/%d", lower, upper, size));
responseWrapper.setContentLength(newContent.length());
responseWrapper.getWriter().write(newContent);
responseWrapper.getWriter().flush();
responseWrapper.flushBuffer();
responseWrapper.copyBodyToResponse();
} else {
responseWrapper.setStatus(SC_REQUESTED_RANGE_NOT_SATISFIABLE);
}
}
}
}
Second you need to add org.springframework.boot.web.servlet.ServletComponentScan annotation to your application class.
#ServletComponentScan
#SpringBootApplication
public class ContentRangeApplication {
public static void main(String[] args) {
SpringApplication.run(ContentRangeApplication.class, args);
}
}
For more info on Range: https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests

Spring security Session Timeout handling for Ajax calls redirect to login not working

There are a lot of questions like this one similar to my question but not working. I am following this blog to redirect ajax request to login page when session timeout but in my case it is not working. here is the code
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.security.web.util.ThrowableCauseExtractor;
import org.springframework.web.filter.GenericFilterBean;
public class AjaxTimeoutRedirectFilter extends GenericFilterBean{
private static final Logger logger = LoggerFactory.getLogger(AjaxTimeoutRedirectFilter.class);
private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
private int customSessionExpiredErrorCode = 901;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
try
{
chain.doFilter(request, response);
logger.debug("Chain processed normally");
}
catch (IOException ex)
{
throw ex;
}
catch (Exception ex)
{
Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
RuntimeException ase = (AuthenticationException) throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
if (ase == null)
{
ase = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
}
if (ase != null)
{
if (ase instanceof AuthenticationException)
{
throw ase;
}
else if (ase instanceof AccessDeniedException)
{
if (authenticationTrustResolver.isAnonymous(SecurityContextHolder.getContext().getAuthentication()))
{
HttpServletRequest httpReq = (HttpServletRequest) request;
logger.info("User session expired or not logged in yet");
String ajaxHeader = ((HttpServletRequest) request).getHeader("X-Requested-With");
if ("XMLHttpRequest".equals(ajaxHeader))
{
logger.info("Ajax call detected, send {} error code", this.customSessionExpiredErrorCode);
((HttpServletResponse)response).sendRedirect("/home/login");
return;
}else
{
((HttpServletResponse)response).sendRedirect("/home/login");
logger.info("Redirect to login page");
return;
}
}
else
{
this.redirectStrategy.sendRedirect((HttpServletRequest) request, (HttpServletResponse) response,"/home/login");
return;
}
}
}
}
}
private static final class DefaultThrowableAnalyzer extends ThrowableAnalyzer
{
/**
* #see org.springframework.security.web.util.ThrowableAnalyzer#initExtractorMap()
*/
protected void initExtractorMap()
{
super.initExtractorMap();
registerExtractor(ServletException.class, new ThrowableCauseExtractor()
{
public Throwable extractCause(Throwable throwable)
{
ThrowableAnalyzer.verifyThrowableHierarchy(throwable, ServletException.class);
return ((ServletException) throwable).getRootCause();
}
});
}
}
public void setCustomSessionExpiredErrorCode(int customSessionExpiredErrorCode)
{
this.customSessionExpiredErrorCode = customSessionExpiredErrorCode;
}
}
I have added this <security:custom-filter ref="ajaxTimeoutRedirectFilter" after="EXCEPTION_TRANSLATION_FILTER"/> and the ajaxTimeoutRedirectFilter bean in the xml configuration file but not working. When i debug it goes to redirect code but the redirect is not redirecting to login.
As ajax call response status after redirect will be 200 instead of 302. There is no option left to identify redirection from status.
Instead of changing status code by implementing your own filter (order before ExceptionTranslationFilter), breaking filter chain by re-throwing exception.
Simple way is
1. Add this hidden div in login page.
<div style="display:none">LOGIN_PAGE_IDENTIFIER</div>
And in your each JSP page.(or, If you have any config.js which you include in every jsp page, add below code there)
<script type="text/javascript">
$(document).ajaxComplete(function (event, xhr, settings) {
if(xhr.responseText.indexOf("LOGIN_PAGE_IDENTIFIER") != -1)
window.location.reload();
});
</script>
PS:
About your concern regarding your AjaxTimeoutRedirectFilter
If you are receiving 901 status in ajax response then
$(document).ajaxComplete(function (event, xhr, settings) {
if(xhr.status == 901)
window.location.reload();
});
adding this to your every JSP page should solve your problem.

Spring - Redirect to a link upon successfully logging in, after a failed Ajax request

I have a website that requires some HTML to be rendered inside an element asynchronously upon an user action. If the user's session expires things get tricky, but it can be solved by creating a custom AuthenticationEntryPoint class like this SO question and this SO question suggest.
My problem comes once the user logs back in because the user gets redirected to the last URL that was requested, which happens to be the Ajax request, therefore my user gets redirected to a fragment of an HTML, instead of the last page it browsed.
I was able to solve this by removing a session attribute on the custom AuthenticationEntryPoint:
if (ajaxOrAsync) {
request.getSession().removeAttribute("SPRING_SECURITY_SAVED_REQUEST");
}
Here comes my question's problem.
While the previous code solves my issue, it has the side effect of redirecting the user to the home page instead of the last page it browsed (as there is no saved request). It wouldn't be much of a problem, but it makes the website inconsistent because if the last request was an asynchronous request, it gets redirected home but if it was a normal request it gets redirected to the last page browsed. =(
I managed to code this to handle that scenario:
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.PortResolver;
import org.springframework.security.web.PortResolverImpl;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.savedrequest.DefaultSavedRequest;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
import static org.apache.commons.lang.StringUtils.isBlank;
public class CustomAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {
... // Some not so relevant code
#Override
public void commence(final HttpServletRequest request,
final HttpServletResponse response,
final AuthenticationException authException) throws IOException, ServletException {
... // some code to determine if the request is an ajax request or an async one
if (ajaxOrAsync) {
useRefererAsSavedRequest(request);
response.sendError(SC_UNAUTHORIZED);
} else {
super.commence(request, response, authException);
}
}
private void useRefererAsSavedRequest(final HttpServletRequest request) {
request.getSession().removeAttribute(SAVED_REQUEST_SESSION_ATTRIBUTE);
final URL refererUrl = getRefererUrl(request);
if (refererUrl != null) {
final HttpServletRequestWrapper newRequest = new CustomHttpServletRequest(request, refererUrl);
final PortResolver portResolver = new PortResolverImpl();
final DefaultSavedRequest newSpringSecuritySavedRequest = new DefaultSavedRequest(newRequest, portResolver);
request.getSession().setAttribute(SAVED_REQUEST_SESSION_ATTRIBUTE, newSpringSecuritySavedRequest);
}
}
private URL getRefererUrl(final HttpServletRequest request) {
final String referer = request.getHeader("referer");
if (isBlank(referer)) {
return null;
}
try {
return new URL(referer);
} catch (final MalformedURLException exception) {
return null;
}
}
private class CustomHttpServletRequest extends HttpServletRequestWrapper {
private URL url;
public CustomHttpServletRequest(final HttpServletRequest request, final URL url) {
super(request);
this.url = url;
}
#Override
public String getRequestURI() {
return url.getPath();
}
#Override
public StringBuffer getRequestURL() {
return new StringBuffer(url.toString());
}
#Override
public String getServletPath() {
return url.getPath();
}
}
}
The previous code solves my issue, but it is a very hacky approach to solve my redirection problem (I cloned and overwrote the original request... +shudders+).
So my question is, Is there any other way to rewrite the link that Spring uses to redirect the user after a successful login (given the conditions I'm working with)?
I've looked at Spring's AuthenticationSuccessHandler, but I haven't found a way of communicating the referer url to it in case of a failed Ajax request.
I've found an acceptable solution to my problem thanks to an idea that came up when reading the docs and later on browsing this other SO answer. In short, I would have to create my own custom ExceptionTranslationFilter, and override the sendStartAuthentication to not to save the request cache.
If one takes a look at the ExceptionTranslationFilter code, it looks this (for Finchley SR1):
protected void sendStartAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain,
AuthenticationException reason) throws ServletException, IOException {
SecurityContextHolder.getContext().setAuthentication(null);
requestCache.saveRequest(request, response); // <--- Look at me
logger.debug("Calling Authentication entry point.");
authenticationEntryPoint.commence(request, response, reason);
}
So, to not save data from Ajax requests I should implement an CustomExceptionTranslationFilter that acts like this:
#Override
protected void sendStartAuthentication(final HttpServletRequest request,
final HttpServletResponse response,
final FilterChain chain,
final AuthenticationException authenticationException) throws ServletException, IOException {
... // some code to determine if the request is an ajax request or an async one
if (isAjaxOrAsyncRequest) {
SecurityContextHolder.getContext().setAuthentication(null);
authenticationEntryPoint.commence(request, response, authenticationException);
} else {
super.sendStartAuthentication(request, response, chain, authenticationException);
}
}
This makes the CustomAuthenticationEntryPoint logic much simpler:
#Override
public void commence(final HttpServletRequest request,
final HttpServletResponse response,
final AuthenticationException authException) throws IOException, ServletException {
... // some code to determine if the request is an ajax request or an async one, again
if (isAjaxOrAsyncRequest) {
response.sendError(SC_UNAUTHORIZED);
} else {
super.commence(request, response, authException);
}
}
And my CustomWebSecurityConfigurerAdapter should be configured like this:
#Override
protected void configure(final HttpSecurity http) throws Exception {
final CustomAuthenticationEntryPoint customAuthenticationEntryPoint =
new CustomAuthenticationEntryPoint("/login-path");
final CustomExceptionTranslationFilter customExceptionTranslationFilter =
new CustomExceptionTranslationFilter(customAuthenticationEntryPoint);
http.addFilterAfter(customExceptionTranslationFilter, ExceptionTranslationFilter.class)
....
.permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.exceptionHandling()
.authenticationEntryPoint(customAuthenticationEntryPoint)
....;
}

How to read httpServletResponse in the interceptor?

I have a spring boot application. And now I need to read request and response in interceptor.I use a HttpServletRequestWrapper replace the request in DispatcherServlet
#Component("dispatcherServlet")
public class FofDisPatcherServlet extends DispatcherServlet {
#Override
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
MultiReadHttpServletRequest requestWrapper = null;
try {
requestWrapper = new MultiReadHttpServletRequest(request);
super.doDispatch(requestWrapper, response);
} catch (Exception e) {
super.doDispatch(request,response);
}
}
}
And in my interceptor , I can read the request body. But when I want to read the response body, it doesn't works.when I replace the response in the CustomerDispatcherServlet I got nothing response.I have tried ContentCachingResponseWrapper , but I got the payload with "".
It's a old question.and I have search some questions but didn't find a suitable solution.
I know I can solve the problem with AOP.But I want to know how can I do it in the interceptor?
here is my interceptor code
public class CustomerInterceptor extends HandlerInterceptorAdapter{
#Override
public void postHandle(...){
MultiReadHttpServletRequest req = (MultiReadHttpServletRequest) request;
ContentCachingResponseWrapper res = new ContentCachingResponseWrapper(response);
Byte[] body = res. getContentAsByteArray();
...
}
}
the body I got is [].
After few days .I find the answer.In the CustomerDispatcherServlet I should add responseWrapper.copyBodyToResponse()
the CustomerDIspatcherServlet like this:
#Component("dispatcherServlet")
public class FofDisPatcherServlet extends DispatcherServlet {
#Override
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
MultiReadHttpServletRequest requestWrapper = null;
try {
requestWrapper = new MultiReadHttpServletRequest(request);
if (!(response instanceof ContentCachingResponseWrapper)) {
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
super.doDispatch(requestWrapper, responseWrapper);
responseWrapper.copyBodyToResponse();
}else {
super.doDispatch(requestWrapper, response);
}
} catch (Exception e) {
super.doDispatch(request, response);
}
}
}
Try this:
#Component("dispatcherServlet")
public class FofDisPatcherServlet extends DispatcherServlet {
#Override
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
MultiReadHttpServletRequest requestWrapper = null;
try {
requestWrapper = new MultiReadHttpServletRequest(request);
super.doDispatch(requestWrapper, new ContentCachingResponseWrapper(request));
} catch (Exception e) {
super.doDispatch(request,response);
}
}
}
.
public class CustomerInterceptor extends HandlerInterceptorAdapter{
#Override
public void postHandle(..., HttpServletResponse response){
if (response instanceof ContentCachingResponseWrapper) {
Byte[] body = ((ContentCachingResponseWrapper)response). getContentAsByteArray();
}
...
}
}
The error is in your code
public class CustomerInterceptor extends HandlerInterceptorAdapter{
#Override
public void postHandle((HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView){
MultiReadHttpServletRequest req = (MultiReadHttpServletRequest) request;
ContentCachingResponseWrapper res = new ContentCachingResponseWrapper(response);
Byte[] body = res. getContentAsByteArray();
...
}
}
You are passing request in ContentCachingResponseWrapper.
See this question very similar problem .

AJAX response conflict

I have a problem with my ajax redirection on response.
The redirection works perfectly, but when, later, I have to return a Boolean with response, it returns the redirection.
Here is the code. The concerned lines have comments :
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Worker extends HttpServlet {
private static final long serialVersionUID = 1L;
private static String firstName = "";
private static String lastName = "";
private static boolean doAnimWheel = false;
private static String portion;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// begin recovering form
Worker.firstName = request.getParameter("firstName");
Worker.lastName = request.getParameter("lastName");
response.sendRedirect("launch.html"); // TODO find why it blocks response
// end recovering form
String param = request.getParameter("srcId");
if(param != null) {
if(param.equals("launch")) {
Worker.doAnimWheel = new Boolean(request.getParameter("doAnimWheel")).booleanValue();
return;
}
else if(param.equals("wheel")) {
response.setContentType("text/plain");
PrintWriter out = response.getWriter();
out.print(Worker.doAnimWheel); // Here I have to return my Boolean, but it return launch.html
out.flush();
out.close();
return;
}
else if(param.equals("result")) {
Worker.portion = request.getParameter("portion");
Worker.doAnimWheel = new Boolean(request.getParameter("doAnimWheel")).booleanValue();
return;
}
}
}
}
I think the problem is that you always send the redirect at the beginning of your method
response.sendRedirect("launch.html"); // TODO find why it blocks response
// end recovering form
Java documentation for the sendRedirect method HttpServletResponse states: "After using this method, the response should be considered to be committed and should not be written to."
What you try to return later is evidently ignored.
You may want to move the sendRedirect calling to the branches of code that actually need to perform the redirect, like this:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// begin recovering form
Worker.firstName = request.getParameter("firstName");
Worker.lastName = request.getParameter("lastName");
String param = request.getParameter("srcId");
if(param.equals("launch")) {
Worker.doAnimWheel = new Boolean(request.getParameter("doAnimWheel")).booleanValue();
response.sendRedirect("launch.html");
return;
}
else if(param.equals("wheel")) {
response.setContentType("text/plain");
PrintWriter out = response.getWriter();
out.print(Worker.doAnimWheel); // Here I have to return my Boolean, but it return launch.html
out.flush();
out.close();
return;
}
else if(param.equals("result")) {
Worker.portion = request.getParameter("portion");
Worker.doAnimWheel = new Boolean(request.getParameter("doAnimWheel")).booleanValue();
response.sendRedirect("launch.html");
return;
}
}
}
}

Resources