Spring boot #RestController fails with 403 error after adding authentication - spring

I am using the Spring – REST Controller tutorial which works just as advertized. I then added a JavaScript ajax snipped which sends a POST request that works too.
After that I added authentication to our ActiveDirectory server. The authentication itself works, but all POST request are now rejected with a 403 error at the web browser console. My controller method is never called.
Surprisingly all GET requests still work, both by modifying the ajax snippet to use the GET methods, or by manually entering the url in the web browser. Also standard html forms using POST requests work too (however they are using #Controller instead of #RestController annotations).
So it seems to me that the JavaScript ajax code is missing some essential headers, or maybe the Spring security configuration blocks such request intentionally. Please advise how to fix this problem.
Java code:
#RestController
public class Controller {
#PostMapping({"/rest/api/1/command"})
public void command(#RequestBody String s) {
System.out.println("we got: " + s);
}
...
}
JavaScript Code:
<script>
$.ajax({
async: false,
type: "POST",
url: "/rest/api/1/command",
contentType: "application/json",
dataType: "json",
data: { command: "run" },
success: function (data) {
return true;
}
});
</script>
Authentication Java code:
package com.example.demo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider;
#Configuration
public class ActiveDirectoryConfig extends WebSecurityConfigurerAdapter {
#Value("${activedirectory.domain}")
private String domain;
#Value("${activedirectory.url}")
private String url;
#Value("${activedirectory.rootdn}")
private String rootdn;
#Value("${activedirectory.searchfilter}")
private String searchfilter;
#Value("${admin.username:}")
private String adminusername;
#Value("${admin.password:}")
private String adminpassword;
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception
{
ActiveDirectoryLdapAuthenticationProvider adProvider =
new ActiveDirectoryLdapAuthenticationProvider(
domain, url, rootdn);
adProvider.setConvertSubErrorCodesToExceptions(true);
adProvider.setUseAuthenticationRequestCredentials(true);
if (searchfilter != null)
adProvider.setSearchFilter(searchfilter);
auth.authenticationProvider(adProvider);
auth.eraseCredentials(false);
}
}

After Marcus Hert da Coregio's comment I was able to fix the program by adding the CSRF token to the ajax call. The actual value of the CSRF token will be provided by the Thymeleaf template.
Code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta id="_csrf" name="_csrf" th:content="${_csrf.token}"/>
<meta id="_csrf_header" name="_csrf_header" th:content="${_csrf.headerName}"/>
<title>Title</title>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous">
</script>
</head>
<body>
<script>
var token = $('#_csrf').attr('content');
var header = $('#_csrf_header').attr('content');
$.ajax({
async: false,
type: "POST",
url: "/rest/api/1/command",
contentType: "application/json",
dataType: "json",
data: { command: "run" },
beforeSend: function(xhr) {
xhr.setRequestHeader(header, token);
},
success: function (data) {
return true;
}
});
</script>
<p>hello world</p>
</body>
</html>

Related

Template might not exist or might not be accessible by any of the configured Template Resolvers springboot

I have a microservice app built-in spring boot, the main module is working at 8080 port and the other module is working at 7070 port. At /clicker endpoint I'm trying to redirect to
the site which is located on this port but always when I'm using /clicker endpoint I can see an error like this. But when I try to get [http://192.168.254.115:7070] directly from the browser, everything is working fine so I think that's not because of the bad config in the application.properties.
Error resolving template [http://192.168.254.115:7070], template might not exist or might not be accessible by any of the configured Template Resolvers
This controller works at 8080 port.
#CrossOrigin(origins = "*")
public class MainController {
#RequestMapping("/")
public String index() {
return "redirect:/login";
}
#RequestMapping("/home")
public String home(){
return "home.html";
}
#CrossOrigin
#RequestMapping("/clicker")
public ModelAndView goToClicker(HttpServletRequest request) {
//http://localhost:7070/
//http://192.168.254.115:7070/
String url = "http://" + request.getServerName() + ":" + "7070";
return new ModelAndView(url);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Home</title>
<link rel="stylesheet" type="text/css" th:href="#{/styles/style.css}"/>
</head>
<body>
<div class = "main">
<div class = "container">
<button type="button">CLICKER</button>
</div>
</div>
</body>
</html>
And that's the controller which works at 7070 port.
#Controller
#CrossOrigin(origins = "*")
public class ClickerController {
#CrossOrigin
#RequestMapping("/")
public String index() {
return "clicker.html";
}
}

Spring Boot with basic authentication and cors

I'm working on learning spring boot, and I have some questions about basic authentication and cors.
I created two pages and ajax to backend side.
The first page ajax username and password to backend and the method is POST.
Besides, it used basic authentication.
If its successes, the first page will redirect to the second page.
The second page will ajax to backend after the second page was loaded.
It uses GET and it will get no data except HTTP.Status.
This is my ajax function in the first page.
function login () {
var username = document.getElementById("username").value;
var password = document.getElementById("password").value;
alert(btoa(username + ":" + password));
var settings = {
"async": true,
"crossDomain": true,
"url": "http://localhost:10000/login",
"method": "POST",
"headers": {
"content-type": "application/json",
"accept": "application/json",
"authorization": "Basic " + btoa(username + ":" + password),
"cache-control": "no-cache",
}
}
alert(settings);
$.ajax(settings).done(function (response) {
console.log(response);
localStorage.setItem("token", btoa(username + ":" + password));
window.location = "file:///home/cyl/SecurityTest/pages/getEmployeePage.html"
});
}
This is my ajax function in the second page.
function getData () {
alert(localStorage.getItem("token"));
var settings = {
"async": true,
"crossDomain": true,
"url": "http://localhost:10000/getAllEmployee",
"method": "GET",
"headers": {
"authorization": "Basic " + localStorage.getItem("token"),
"accept": "application/json",
"content-type": "application/json",
"cache-control": "no-cache"
}
}
$.ajax(settings).done(function (response, textStatus, xhr) {
console.log(response);
});
}
This is my RestController
#RestController
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class EmployeeController {
#CrossOrigin(origins="*", allowedHeaders = "*")
#PostMapping(path = "/login")
public ResponseEntity<String> login() {
return new ResponseEntity<String>(HttpStatus.ACCEPTED);
}
#CrossOrigin(origins="*", allowedHeaders = "*")
#GetMapping(path = "/getAllEmployee")
public ResponseEntity<String> getAllEmployee() {
//List<Employee> employeeList = this.employeeDAO.getAllEmployee();
return new ResponseEntity<String>(HttpStatus.OK);
}
}
CorsConfig
#Configuration
public class CorsConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST")
.allowCredentials(true);
}
}
But at the second page step, I got an error
"Access to XMLHttpRequest at 'http://localhost:10000/getAllEmployee' from origin 'null' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status."
I can't deal with this problem despite I search some relevant issues.
Except for this question, The way I store the authentication token in the client side is a right way?
If not, how can I do that?
Thanks!
you can use this Spring annotation if you run the same spring project on your local machine and the JS project with this tag will allow you to access the rest service
#CrossOrigin(origins = "*", maxAge = 3600)
public class controllerRest{}
regards!

Spring Boot: Ambiguous handler methods mapped for HTTP path 'http://localhost:8080/error'

Everytime I used ajax for posting, I'm getting this error but the passing of data works successfully. Some said that it is because of my return statement. I also don't have any mapping for /error. This is one of my work that causes this error.
AJAX:
$.ajax
({
type: 'post',
url: '/SaveDependent',
data:
{
dependent_id: 0,
reference_no: referenceNo,
dependent_name: dNameValue[i],
dependent_dob: dDobValue[i],
dependent_gender: dGenderValue[i],
dependent_occupation: dOccupationValue[i],
dependent_relationship: dRelationshipValue[i],
dependent_beneficiary: dBeneficiaryValue[i]
},
success: function (response)
{
alert("success");
},
});
CONTROLLER:
#RequestMapping(value= "/SaveDependent", method=RequestMethod.POST)
public String saveDependent(ClientApplicationDependent clientApplicationDependent) {
clientApplicationDependentService.saveOrUpdate(clientApplicationDependent);
return "success";
}
SERVICE:
public interface ClientApplicationDependentService {
public void saveOrUpdate(ClientApplicationDependent clientApplicationDependent);
}
SERVICE IMPL:
#Override
public void saveOrUpdate(ClientApplicationDependent clientApplicationDependent) {
clientApplicationDependentRepository.save(clientApplicationDependent);
}

Embedding Vaadin spring-boot application into HTML

I've already posted this question in the Vaadin Forum, unfortunately I did not get any response - maybe answer of the question lies somewhere between spring-boot and Vaadin.
currently I'm having a hard time embedding a Vaadin application into an HTML page.
What do I use:
Vaadin 7.6.6
vaadin-spring
spring-boot 1.3.5.RELEASE
To enable CORS in combination with spring-boot, I adapted Sami's Blog entry and created the following custom CORS servlet:
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import com.vaadin.spring.server.SpringVaadinServlet;
/**
* This custom {#link SpringVaadinServlet} enables CORS in combination with
* Spring.
*
* #author Christoph Guse
*
*/
public class CORSServlet extends SpringVaadinServlet {
/**
*
*/
private static final long serialVersionUID = -2482991123719720492L;
/**
* Override to handle the CORS requests.
*/
#Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Origin is needed for all CORS requests
String origin = request.getHeader("Origin");
if (origin != null && isAllowedRequestOrigin(origin)) {
// Handle a preflight "option" requests
if ("options".equalsIgnoreCase(request.getMethod())) {
response.addHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Allow", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS");
// allow the requested method
String method = request.getHeader("Access-Control-Request-Method");
response.addHeader("Access-Control-Allow-Methods", method);
// allow the requested headers
String headers = request.getHeader("Access-Control-Request-Headers");
response.addHeader("Access-Control-Allow-Headers", headers);
response.addHeader("Access-Control-Allow-Credentials", "true");
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
response.getWriter().flush();
return;
} // Handle UIDL post requests
else if ("post".equalsIgnoreCase(request.getMethod())) {
response.addHeader("Access-Control-Allow-Origin", origin);
response.addHeader("Access-Control-Allow-Credentials", "true");
super.service(request, response);
return;
}
}
// All the other requests nothing to do with CORS
super.service(request, response);
}
/**
* Check that the page Origin header is allowed.
*/
private boolean isAllowedRequestOrigin(String origin) {
// TODO: Remember to limit the origins.
return origin.matches(".*");
}
}
Additionally I found some documentation about spring-boot and CORS, so I added this Spring configuration:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import CORSServlet;
/**
* #author Christoph Guse
*
*/
#Configuration
public class AuthAppVaadinApplicationConfiguration {
#Bean
public WebMvcConfigurer corsConfigurer(){
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins(".*");
}
};
}
#Bean(name="vaadinServlet")
public CORSServlet corsServlet(){
return new CORSServlet();
}
}
My HTML looks like this:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible"
content="IE=9;chrome=1" />
<title>Embedding a Vaadin Application in HTML Page</title>
<!-- Set up the favicon from the Vaadin theme -->
<link rel="shortcut icon" type="image/vnd.microsoft.icon"
href="/VAADIN/themes/reindeer/favicon.ico" />
<link rel="icon" type="image/vnd.microsoft.icon"
href="/VAADIN/themes/reindeer/favicon.ico" />
</head>
<body>
<!-- Loads the Vaadin widget set, etc. -->
<script type="text/javascript"
src="http://vaadin.poc:8090/VAADIN/vaadinBootstrap.js?v=7.6.6"></script>
<h1>Embedding a Vaadin UI</h1>
<p>This is a static web page that contains an embedded Vaadin
application. It's here:</p>
<!-- So here comes the div element in which the Vaadin -->
<!-- application is embedded. -->
<div style="width: 100%; height: 75vh; border: 2px solid green;"
id="helloworld" class="v-app">
<!-- Optional placeholder for the loading indicator -->
<div class=" v-app-loading"></div>
<!-- Alternative fallback text -->
<noscript>You have to enable javascript in your browser to
use an application built with Vaadin.</noscript>
</div>
<script type="text/javascript">//<![CDATA[
if (!window.vaadin)
alert("Failed to load the bootstrap JavaScript: "+
"VAADIN/vaadinBootstrap.js");
/* The UI Configuration */
vaadin.initApplication("helloworld", {
"browserDetailsUrl": "http://vaadin.poc:8090/",
"serviceUrl": "http://vaadin.poc:8090/",
"theme": "valo",
"versionInfo": {"vaadinVersion": "7.6.6"},
"widgetset": "com.vaadin.DefaultWidgetSet",
"vaadinDir": "http://vaadin.poc:8090/VAADIN/",
"heartbeatInterval": 300,
"debug": true,
"standalone": false,
"authErrMsg": {
"message": "Take note of any unsaved data, "+
"and <u>click here<\/u> to continue.",
"caption": "Authentication problem"
},
"comErrMsg": {
"message": "Take note of any unsaved data, "+
"and <u>click here<\/u> to continue.",
"caption": "Communication problem"
},
"sessExpMsg": {
"message": "Take note of any unsaved data, "+
"and <u>click here<\/u> to continue.",
"caption": "Session Expired"
}
});//]] >
</script>
<p>Please view the page source to see how embedding works.</p>
</body>
</html>
My problem is the application is initally loaded, but several icons are missing and if I trigger an action in the appliction, i.e. open a dropbox, then the application is not able to do the connect to the spring-boot application.
Error messages look like this:
XMLHttpRequest cannot load http://vaadin.poc:8090/UIDL/?v-uiId=0. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 403.
Is there anybody out there who managed to embed a Vaadin spring-boot application into another HTML application?
Any hint is highly appreciated!
Christoph
fortunately someone in the Vaadin forum gave me the missing link. I forgot to add some JavaScript in the standalone HTML:
<script>
XMLHttpRequest.prototype._originalSend = XMLHttpRequest.prototype.send;
var sendWithCredentials = function(data) {
this.withCredentials = true;
this._originalSend(data);
};
XMLHttpRequest.prototype.send = sendWithCredentials;
</script>
That helped, but the fonts were not properly loaded by CORS problems, so I removed the custom Vaadin CORSServlet and added the filter based CORS support provided by spring-boot (as explained in this blog article).
My example now runs properly, the demo application is fully functional, fonts are loaded and used correctly.
Please have a look at https://github.com/flexguse/vaadin-html-embedding to get the fully working example.
Cheers,
Christoph

How to call #RequestMapping method of Controller having specified URL using AJAX

I'm very new to Spring and Portlet. I want to use jqgrid to show some list. I am trying to call a method in controller which is annoted with the #RequestMapping but the method is not being called
My Controller has following method
#Controller(value = "myController")
public class MyController {
#RequestMapping(value="/myURL",method=RequestMethod.GET)
public #ResponseBody MyDTO initItemSearchGrid(RenderResponse response, RenderRequest request){
MyDTO myDto=new MyDTO();
return myDto;
}
}
My JSP code using AJAX
var urlink="/myURL"; /* myURL is the exact String written in value Attribute of
resourceMapping in Controller*/
$.ajax({
url :urlink,
cache: false,
data:$('#myForm').formSerialize(),
dataType: "json",
type: "GET",
contentType: "application/json; charset=utf-8",
success: function(jsondata){
...
}
});
When above AJAX code is executing my method is not called.
You mention Portlets in your question. Working with Spring and portlets is a bit different from servlets.
So, assuming you have a portlet like this
#Controller
#RequestMapping("VIEW") // VIEW mapping (as opposed to EDIT)
public class MyPortlet {
#RenderMapping
public ModelAndView handleRenderView(RenderRequest request, RenderResponse response) {
ResourceURL resourceUrl = response.createResourceURL();
resourceUrl.setResourceID("myResource"); // this is the id used to reference a #ResourceMapping
ModelAndView ret = new ModelAndView("myPortlet");
ret.addObject("resourceUrl", resourceUrl.toString());
return ret;
}
#ResourceMapping("myResource")
public void handleMyResource(ResourceRequest request, ResourceResponse response) {
OutputStream out = response.getPortletOutputStream();
// write whatever to output
}
}
As you can see, the #ResourceMapping is identified by a resource ID. The url for the resource mapping can be created using the standard portlet API methods and classes createResourceURL() and javax.portlet.ResourceURL.
If you prefer to use the portlet taglibrary instead, you can also generate a resource URL using the <portlet:resourceRequest> tag.
Your view might look something like this
myPortlet.jsp
...
<script>
$.ajax({
url :${resourceUrl},
cache: false,
data:$('#myForm').formSerialize(),
dataType: "json",
type: "GET",
contentType: "application/json; charset=utf-8",
success: function(jsondata){
.........
.........
.........
}
});
</script>
...

Resources