I have two different java projects and I need them to interact with each other. The first one named RESTfulWebServer is a dynamic web project which contains the basic HTTP GET PUT POST requests in a java class named UserServices. It just prints one line statements right now on console(output window in netbeans), so nothing complex.
The other project named ClientProject is also a dynamic web project which contains a simple jsp page containing javascript code. It is supposed to make call to the RESTfulWebServer project and print the output line on console(output window in netbeans) the same way the RESTfulWebServer project does when a simple GET request is made to it.
This is the part where the problem arises. When I run the ClientProject the javascript function is being called properly (as I checked by printing an alert message), but it is not making the DELETE HTTP request to the RESTfulWebServer as it is supposed to.
Both the codes are attached below:
RESTfulWebServer (UserServices.java)
package com.service.user;
import javax.ws.rs.*;
#Path("/user/service")
public class UserServices {
#GET
public void getUser()
{
System.out.println("Inside get user method");
}
#POST
public void updateUser()
{
System.out.println("Inside update user method");
}
#DELETE
public void deleteUser()
{
System.out.println("Inside DELETE user method");
}
}
ClientProject (clientfile.jsp)
<%#page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Update User</title>
<script>
function loadResponse()
{
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function()
{
if (xmlhttp.readyState === 4 && xmlhttp.status === 200)
{
document.getElementById("myDiv").innerHTML = xmlhttp.responseText;
}
};
xmlhttp.open("DELETE","http://localhost:8080/app/user/service",true);
xmlhttp.send();
}
</script>
</head>
<body>
<div id="myDiv">The response text would come here</div>
<button type ="button" onclick="loadResponse()">submit</button>
</body>
</html>
The clientfile is supposed to print on console "Inside DELETE user method" but nothing is showed on console after "Build successful" message
What am I doing wrong? Also I am making use of TomCat server and doing this in NetBeans and I have to work on this IDE solely for some reason (kindly do not suggest to move to any other IDE and check it) if there is any mistake in my code or anything else pleaseee mention it?
First of all starting with requesting URL http://localhost:8080/app/user/service
when it falls in class level annotation - there are three methods so which one to pick so, needed to provide method level annotations as well for better approach.
#Path("/user/service")
public class UserServices {
#Path("/getUser")
#GET
public void getUser() { System.out.println("Inside GET method"); }
#Path("/updateUser")
#POST
public void updateUser() { System.out.println("Inside UPDATE method"); }
#Path("/deleteUser")
#DELETE
public void deleteUser() { System.out.println("Inside DELETE method"); }
}
Now coming towards something important which is necessary to make project(RestWebServer) accept requests from another project(Client Projects/app) which is hosted on separate domain, i.e. CORS (Cross Origin Resource Sharing)
CORS, in a nutshell, is a security check implemented like when an application requests for resources from or make server calls to another domain, these requests get blocked by browsers. Moreover you are using XMLHttpRequest which forces same-origin policy i.e. request should generate from same domain where resources are residing so in order to make requests allow cross domain accessing we implement CORS Filter logic on server side to allow methods (P,G,P,D) to get executed.
So add a class like this in your WebService Project in package where UserServices class is:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class CORSFilter implements Filter {
public CORSFilter() { }
public void init(FilterConfig fConfig) throws ServletException { }
public void destroy() { }
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
((HttpServletResponse)response).addHeader("Access-Control-Allow-Origin", "*");
((HttpServletResponse)response).addHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
chain.doFilter(request, response);
}
}
Now time to use this Filter in web.xml
<web-app ....>
<filter>
<filter-name>CORSFilter</filter-name>
<filter-class><package name -must be complete>.CORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CORSFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
clientFile.jsp
Now call method like this from view page with just method annotation added
xmlhttp.open('DELETE','http://localhost:8080/app/user/service/deleteUser',true);
Last thing you will have to make two server instances of Tomcat to deploy, name them Service and Client Server respectively for your convenience while making them. Let the Service one have all default config but you will have to change all three Port number for Client Server to avoid binding error. For this simply double click on server (Client) see Ports heading and change ports.
All done, it should run now, tested as well. Hope this will help you and other thread readers as well.
Related
I need to implement a requirement, where I need to deliver javascript code securely. My Idea is,
I will make the path as /something.js and in the controller, I will check the authentication, if not authenticate I will deliver console.error("Auth Failed").
How I can achieve the above scenario.
this is the simplest way using common request handler. works 100 %
#GetMapping(value = "/hello.js")
public void resources( HttpServletRequest httpRequest,
HttpServletResponse httpResponse) throws Exception {
//Authentication goes here or using handler interceptor
httpResponse.setContentType("text/javascript");
httpResponse.setHeader("MIME-type", "text/javascript");
httpResponse.getWriter().write(" function alertMan(msg) { \n alert(\"message:\"+msg); \n } ");
}
This question already has answers here:
How should I use servlets and Ajax?
(7 answers)
Closed 2 years ago.
I am learning working with Servlets and JavaScript, by trying to build an application where a Java servlet and a JavaScript code can exchange data. I have built a simple WebPage with an Header and a button. Upon Clicking the button, the client should send an AJAX request to the server, and the Server should send some data as a response, that should alter the text on the Header accordingly. I am using jQuery to send and receive the data, and alter the text on the Header. Here is the HTML code :
<!DOCTYPE html>
<html lang="en">
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>$(function() { alert('hello') });</script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="Try.js"></script>
</head>
<body>
<h1 align = "center" id="header">Using Visual Studio</h1>
<button id="button">Click</button>
</body>
</html>
This is the Try.js JavaScript code with jQuery :
$(function(){
$("button").click(function(){
$.ajax({url: "localhost:8080/Ajax/Serv", success: function(result){
$("#header").html(result);
}});
});
});
And following is the servlet code:
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jdk.nashorn.internal.parser.JSONParser;
#WebServlet("/Serv")
public class Serv extends HttpServlet {
private static final long serialVersionUID = 1L;
public Serv() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html; charset=UTF-8");
response.getWriter().write("Success Data!!!");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
The webpage Header should display "Success Data!!!" on Clicking the button "Click". However that is not happening.
I am using TomCat 7.0.39 on port 8080 for the server.
It appears you are loading the html page from your file system (e.g. file:///C:/Ajax/serv.html) and not the localhost (e.g. http://localhost:8080/Ajax/serv.html). That's why you're getting the Cross Origin Request Blocked error when attempting the Ajax call, this is a Cross-site Scripting (XSS) security vulnerability that your browser is preventing.
You should move your html (js, etc) file under a new Tomcat webapps folder, such as webapps/Ajax) and load them using http://localhost:8080/Ajax/serv.html. This will prevent the XSS problem.
Also adding an error handler to the Ajax call will help determine the problem.
$("button").click(function(){
$.ajax({url: "localhost:8080/Ajax/Serv",
success: function(result){
$("#header").html(result);
},
error: function(xhr, status, ex) {
alert("state=" + xhr.state() + ", status=" + status);
}
});
One common reason for the HTML/JSP page not being able to hit the servlet is when servlet has not been deployed. A simple way to test this scenario is to create a dynamic web project with an HTML/JSP page and a servlet in eclipse ▸ Right-click the HTML/JSP page ▸ Run As ▸ Run On Server
It will serve the HTML/JSP page from the server. However, you try to hit the servlet in the browser, it will show 404 error because the servlet has not been deployed.
Now, stop the server ▸ Right-click the servlet ▸ Run As ▸ Run On Server
You will see that the servlet is accessible this time. If a button (or other control), which has this servlet as the action, is clicked, the response from the server can be seen.
I have shown the screenshot with the structure of my web project. I tried your code and I was able to get the expected result without any issue.
I want to use JS to load another JS file from a server but i want to do it conditionally example if the userId sent by the request is in my database send back the file.
So i thought of creating an interceptor.
Is there a better way to do it because an interceptor would be an overkill?
<mvc:interceptors>
<mvc:interceptor>
<bean class="com.mycomp.webservice.UserInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
public class UserInterceptor extends HandlerInterceptorAdapter {
#Autowired
UserService userService;
#Override
#Transactional
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
if(!userService.userPresent(request)){
return false;
}
else{
return true;
}
}
Edit: So i figured out that the question isn't that clear. the file i want to upload is part of the static resources so i don' want to just load it to the client, i want to cache it as well.
So i leave you with a link.
Edit 2: So this is what i ended up doing. under my mvc-resources config i created an interceptor to handle request for protected static resources.
<mvc:resources mapping="/static/**" location="/static/" />
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/static/protected/**" />
<bean class="com.mycompany.interceptor.ProtectedResourcesInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
public class ProtectedResourcesInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if("your condition is true") {
System.out.println("Access Granted to protected resources");
return true;
}
return false;
}
}
The interceptor will handle requests to /static/protected and if conditions are right it will serve it. If anyone has a cleaner solution please share it.
I am using a WCF (.svc) WebService and it's working perfectly - I can call its methods without problems from Postman, PHP, etc. But, when I try to call it from JavaScript/jQuery using AJAX, there is an obvious problem - I do it from other domain than WS, so it won't let me do it.
It is all about POST methods. But there is a problem even when my page is sending firstly an OPTIONS method:
OPTIONS 'WS ADDRESS' 405 (Method Not Allowed)
XMLHttpRequest cannot load 'WS ADDRESS' Response to preflight request doesn't
pass access control check: No 'Access-Control-Allow-Origin' header is
present on the requested resource. Origin 'MY ADDRESS' is therefore not allowed
access. The response had HTTP status code 405.
There are detailed responses:
Okay, I read about cross-domain and WS's Web.config contains all of it that is necessary (?)
Please let me know what am I doing wrong that I cannot reach my WS from JavaScript, even though it seems to me configured well. But still, it seems not to send these special headers in response... Thanks in advance.
XML HttpRequest allows data to be transferred from a web server to the browser. The browsers, however, prevents CORS by default.
GET request is not going to change anything on the server side (nothing is created/updated/deleted) - it's only going to return some data.
POST/PUT/DELETE requests, however, will process the request and change something on the server side and issue a response to the browser. If the response doesn't have a proper Access-Control-Allow-Origin header, the browser will block the response. But that doesn't matter because by the time the response is issued, the server has already processed the request and made changes, perhaps to a database.
In order to prevent POST/PUT/DELETE request to be processed on server side, browsers will send a preflight request.
A prefligth request is an http request with the method OPTIONS. So before sending the POST request, the browser will send an OPTIONS request with an additional header called Access-Control-Request-Method with the value of POST.
The 405 (Method Not Allowed) error indicates the the server is not configured to accept OPTIONS request.
You can solve this issue either by using a wildcard for your web invoke method similar to this:
[OperationContract]
[WebInvoke(Method = "*", UriTemplate = "/Path", ResponseFormat = WebMessageFormat.Json)]
Or by adding an additional [OperationContract] to your [ServiceContract] that handles OPTIONS request similar to this:
[OperationContract(Name = "OptionsMyFunction")]
[WebInvoke(Method = "OPTIONS", UriTemplate = "/Path", ResponseFormat = WebMessageFormat.Json)]
You can implement .NET IDispatchMessageInspector for this work.
Create a class implementing IDispatchMessageInspector
Create a class implementing Attribute,IEndpointBehavior,IOperationBehavior
Allow only OPTIONS in your class implementing IDispatchMessageInspector
The code will look like this
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
using System.Collections.Generic;
using System.Net;
namespace WapstyPrintService
{
public class MessageInspector : IDispatchMessageInspector
{
private ServiceEndpoint _serviceEndpoint;
Dictionary<string, string> requiredHeaders;
public MessageInspector(ServiceEndpoint serviceEndpoint)
{
_serviceEndpoint = serviceEndpoint;
requiredHeaders = new Dictionary<string, string>();
requiredHeaders.Add("Access-Control-Allow-Origin", "*");
requiredHeaders.Add("Access-Control-Request-Method", "POST,GET,PUT,DELETE,OPTIONS");
requiredHeaders.Add("Access-Control-Allow-Headers", "X-Requested-With,Content-Type");
}
/// <summary>
/// Called when an inbound message been received
/// </summary>
/// <param name="request">The request message.</param>
/// <param name="channel">The incoming channel.</param>
/// <param name="instanceContext">The current service instance.</param>
/// <returns>
/// The object used to correlate stateMsg.
/// This object is passed back in the method.
/// </returns>
public object AfterReceiveRequest(ref Message request,
IClientChannel channel,
InstanceContext instanceContext)
{
var httpRequest = (HttpRequestMessageProperty)request
.Properties[HttpRequestMessageProperty.Name];
return new
{
origin = httpRequest.Headers["Origin"],
handlePreflight = httpRequest.Method.Equals("OPTIONS",
StringComparison.InvariantCultureIgnoreCase)
};
}
/// <summary>
/// Called after the operation has returned but before the reply message
/// is sent.
/// </summary>
/// <param name="reply">The reply message. This value is null if the
/// operation is one way.</param>
/// <param name="correlationState">The correlation object returned from
/// the method.</param>
public void BeforeSendReply(ref Message reply, object correlationState)
{
var state = (dynamic)correlationState;
if (state.handlePreflight)
{
reply = Message.CreateMessage(MessageVersion.None, "PreflightReturn");
var httpResponse = new HttpResponseMessageProperty();
reply.Properties.Add(HttpResponseMessageProperty.Name, httpResponse);
httpResponse.SuppressEntityBody = true;
httpResponse.StatusCode = HttpStatusCode.OK;
}
var httpHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
foreach (var item in requiredHeaders)
{
httpHeader.Headers.Add(item.Key, item.Value);
}
}
}
}
and
using System;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
namespace WapstyPrintService
{
public class BehaviorAttribute : Attribute, IEndpointBehavior,
IOperationBehavior
{
public void Validate(ServiceEndpoint endpoint) { }
public void AddBindingParameters(ServiceEndpoint endpoint,
BindingParameterCollection bindingParameters)
{ }
/// <summary>
/// This service modify or extend the service across an endpoint.
/// </summary>
/// <param name="endpoint">The endpoint that exposes the contract.</param>
/// <param name="endpointDispatcher">The endpoint dispatcher to be
/// modified or extended.</param>
public void ApplyDispatchBehavior(ServiceEndpoint endpoint,
EndpointDispatcher endpointDispatcher)
{
// add inspector which detects cross origin requests
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(
new MessageInspector(endpoint));
}
public void ApplyClientBehavior(ServiceEndpoint endpoint,
ClientRuntime clientRuntime)
{ }
public void Validate(OperationDescription operationDescription) { }
public void ApplyDispatchBehavior(OperationDescription operationDescription,
DispatchOperation dispatchOperation)
{ }
public void ApplyClientBehavior(OperationDescription operationDescription,
ClientOperation clientOperation)
{ }
public void AddBindingParameters(OperationDescription operationDescription,
BindingParameterCollection bindingParameters)
{ }
}
}
Then add your message inspector to service endpoint behavior
ServiceHost host = new ServiceHost(typeof(myService), _baseAddress);
foreach (ServiceEndpoint EP in host.Description.Endpoints)
EP.Behaviors.Add(new BehaviorAttribute());
I have some url secured with spring (configured through xml). It works. However when I try to hit that endpoint with an ajax request I get a 302 (found) response. This redirects my ajax call to the login page (so I GET the html). However I'd like to get a 401 (unauthorized) response with the url of the login page available to the client application, so I can redirect the user there with javascript. This question seems to be the closest to what I want, but there's no example and it suggests changing the controller again. Is there no configuration in spring-security that will give me a 401 and a url (or some other sensible error message and the url of the login page)?
You can extend LoginUrlAuthenticationEntryPoint. Here is my one:
package hu.progos.springutils;
// imports omitted
public class AjaxAwareLoginUrlAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {
public void commence(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException authException) throws IOException, ServletException {
if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");
} else {
super.commence(request, response, authException);
}
}
}
Then configure spring to use your implementation:
<beans:bean id="authEntryPoint" class="hu.progos.springutils.AjaxAwareLoginUrlAuthenticationEntryPoint" scope="singleton>
<beans:property name="loginFormUrl" value="/login.html" />
</beans:bean>
<http entry-point-ref="authEntryPoint">
<!-- your settings here -->
</http>
There are a million ways to do this of course. But the short solution to your problem is this configuration snippet:
<bean id="customAuthEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/your-custom-login" />
</bean>
I also take a step further and turn off the security auto-config so I can map the above entry point like so:
<security:http auto-config="false" entry-point-ref="customAuthEntryPoint">
...
...
</security:http>
I also override a bunch of spring security classes to get the security model to do exactly what I want. It's a slippery slope, but it's nice having the control once it works the way you want it to.