I work on spring boot and angular I made an authentication based on spring security I tested with postman and everything works fine, but when I use angular it does not work after debugging I noticed that request. getParameter("username") return null, the error is probably on the angular request, please can someone help me
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
// TODO Auto-generated method stub
String username = request.getParameter("username");
System.out.println(username);
String password = request.getParameter("password");
System.out.println(password);
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(username, password);
return authManager.authenticate(authToken);
}
//req angular
login(params:any) : Observable<any> {
let username=params.username as string
let password=params.password as string
const FORM_URL = {
headers:{
'Content-Type': 'application/json'
}
}
return this.http.post<any>(`${this.adresse+'/login'}`,{
'username':username,
'password':password
}, FORM_URL)}
}
According to the documentation:
Returns the value of a request parameter as a String, or null if the parameter does not exist.
Find out where this parameter should come from. Candidates are the client request, the container and the Sprint Boot framework. Once you know which side is supposed to set it, figure out why it does not.
One answer can be as simple as 'the user has not yet authenticated'. For a more focused response you'd have to share more details.
Related
My problem is that each POST requests sent with AJAX return a 415 error while using a remote tomcat hosted on a Debian 9 VM. The same code works fine for a local tomcat (same version, no difference in the config files).
Tested with different versions of tomcat (8.5 and 9). Same problem.
The considered Login page (see below) :
HTML
<div id="form">
<form id="loginEmp-Form">
<input type="email" id="login" name="login" placeholder="email" />
<input type="password" id="password" name="password" placeholder="password" />
<input type="submit" id="loginButton" value="Connection" />
</form>
<div id="errorMsg"></div>
</div>
JAVASCRIPT
$('#loginEmp-Form').submit(function(event) {
event.preventDefault();
var loginReceived = loginForm.login.value;
var passwordReceived = loginForm.password.value;
//I deliberately deleted the logic concerning the password hashing to make it simple
var Login = {
login: loginReceived,
password: passwordReceived
};
$.ajax({
headers : {'Content-Type' : 'application/json;charset=utf-8'},
type : 'POST',
url : 'login',
data : JSON.stringify(Login),
success : function(result) {
// DO SOMETHING
},
error : function(e) {
// DO SOMETHING
}
});
}
JAVA
Controller
public class LoginController {
private static final Logger LOGGER=LoggerFactory.getLogger(LoginController.class);
#RequestMapping(value = "/login", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public ModelAndView processLoginEmployee(#RequestBody Login loginReceived, HttpSession session) {
// Logic not detailed here because we're not even entering in the controller. Checked with the following traces.
LOGGER.info("Received request POST"); // nothing display
ModelAndView mav = null;
return mav;
}
}
Corresponding Bean
public class Login {
private String login;
private String password;
/*
* Constructors
*/
public Login(String login, String password) {
super();
this.login = login;
this.password= password;
}
public Login() {
}
/*
* Getters & setters
*/
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password= password;
}
}
As I said, when I'm deploying the application on a local Tomcat, everything works fine (front-end and back-end). I'm understanding that a 415 error refers to a problem of request format. But I can't really tell where is it coming from. So that's the reason why I'm asking for help! I've spent hours trying to search for the solution on the internet, but without success.
Other information:
GET Requests work fine, even while using remote Tomcat.
I'm working on my company internet network. Could any security element block post request ? Like a firewall ? I claim that no, because some colleagues developped and deployed their apps on remote tomcat and POST requests just worked perfectly. I asked for their help but they didn't find any solution.
What I've already tried --> Ajax call :
add/delete headers with 'content-type: application/json'
add/delete 'ContentType: application/json'
Java Controller :
add/delete 'consumes=MediaType.APPLICATION_JSON_VALUE'
Config of Tomcat :
checking if the size limit of requests is fine
checking if tomcat is opened to remote connection
I've tried with Postman to send the exact same request. Result: 415.
I'll appreciate a lot if someone finds the solution or at least has proposals. Thank you in advance for your time.
SOLUTION
Problem solved. 2 things :
First, in the pom.xl, only use this dependency and not the older one from org.codehaus.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
Maven is going to create automatically each needed jars while building. Also, another mistake was to use both dependencies simultaneously (from codehaus and fasterxml). The dependencies from fasterxml are the ones to be used now.
Then, modifications have been done in the controller:
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String processLoginEmployee(#RequestBody String json)
throws JsonParseException, JsonMappingException, IOException {
LOGGER.info("Received request POST");
ObjectMapper mapper = new ObjectMapper();
Login employee = mapper.readValue(json, Login.class);
System.out.println(employee.getLogin());
System.out.println(employee.getMdp());
return "success";
}
Attempting to get an object directly with #RequestParam doesn't work.
Fetching a json as a string and then parsing it with an ObjectMapper gets the job done.
Don't set Content-Type using headers option. Set the contentType option.
$.ajax({
contentType : 'application/json;charset=utf-8',
type : 'POST',
url : 'login',
data : JSON.stringify(Login),
success : function(result) {
// DO SOMETHING
},
error : function(e) {
// DO SOMETHING
}
});
contentType defaults to 'application/x-www-form-urlencoded; charset=UTF-8', and it overrides whatever you set in the headers option.
i am trying to introduce Angular into one of my old application. But not sure why this is not calling the Service. Below is the Code in the JS File. Earlier i got error in browser saying $http cannot be resolved . So i just passed it in the function.
var app = angular.module('ldlApp', []);
app.controller('ldlController', function($scope,$http) {
console.log(" Inside Controller **** ");
$scope.message = 'Hello from LDL Controller !!!! ';
$scope.BLT_LDL_DECESION_LOAN_DATA = [];
console.log(" Going to Hit the Service ***** ");
$http.get("/Services/ldl/getdetails")
.then(function(response) {
$scope.BLT_LDL_DECESION_LOAN_DATA = response.data;
console.log("BLT_LDL_DECESION_LOAN_DATA:
"+JSON.stringify($scope.BLT_LDL_DECESION_LOAN_DATA));
});
});
Below is the REST Controller in java File
#RestController
public class LoanDecesionController {
#RequestMapping(value = "/Services/ldl/getdetails", method = RequestMethod.GET)
#ResponseBody
#Transactional
public List<LinkedHashMap<String, Object>> getdetails() throws Exception {
System.out.println(" Inside Service **** ");
List<LinkedHashMap<String, Object>> dataMap = new ArrayList<LinkedHashMap<String, Object>>();
LinkedHashMap<String, Object> responsedataMap = new LinkedHashMap<>();
responsedataMap.put("SUCESS", "Called the service ");
dataMap.add(responsedataMap);
return dataMap;
}
}
In Browser i could see message like
Inside Controller ****
Going to Hit the Service *****
Below is something i am seeing in network tab .
Request URL: https://*****-*****-*****.com/Services/ldl/getdetails
Request Method: GET
Status Code: 302 Found
Remote Address: 10.***.***.49:553
Referrer Policy: no-referrer-when-downgrade
But i am not getting the sysouts in controller. So whether the problem is really with response or is it hitting the service.
when using #Transactional and #ResquestMaping spring boot don't auto-configure URL mappings. remove #Transactional from your method and try to manage transactions somewhere else in your code
It looks like my existing application is blocking the URL and not allowing to hit any other URLs . Thanks everyone for the help and giving insight to the problem.
Spring Rest Api :
#RequestMapping(value={"/save-userlist"}, method=RequestMethod.POST)
public ResponseEntity<?> saveUserList(#RequestBody UserListDTO userListDTO, #RequestBody List<User> users, #RequestParam Integer userId) {
ResponseEntity<?> response= null;
try{
logger.debug(userListDTO, );
logger.debug(users, );
return ResponseEntity.status(HttpStatus.OK).body("success");
}
catch(Exception e){
logger.debug(e.getMessage());
}
return response;
}
can anyone please suggest me how to pass multiple parameters to the Web-API from angular4
Please correct me if i'm wrong but you can only pass 1 request body. Your request body should be 1 object which contains all your variables in json format, this will automatically be serialized to i.e. the UserListDRO object.
See this example:
Method parameter user is marked with #RequestBody annotation. Thanks
to this annotation, Spring will try to bind the request body [which
can be JSON/XML/Other] to user object[ Means crating a new user object
with the details found in the request body like user name,age etc..],
based on Content-Type header in Http request.
I know this is addressed in this post but I am still having trouble setting a custom header using ES6 and am wondering if anyone has run into this issue? The problem is when is set the header using .set I only set the Access-Control-Request-Header to the label I want to set it and the value is lost. I want to set a custom field on the request header using superagent and not sure how.
Let's say I am running this in my app (client)
import ajax from 'superagent'
ajax.get(baseURL + "/query/")
.query({q: "SELECT Id FROM USER WHERE Id=" + id})
.set('X-Authorization', 'Oauth ' + token)
.set('Content-Type', 'application/json')
.end((error, response) => {
if(errro) { console.log(error); }
}
the header the get request makes contains:
Access-Control-Request-Headers:content-type, x-authorization
under Request Headers in the network tab of the browser debugger. I want to set the headers of the get so that under Request Headers in the network tab of the browser dubugger I see:
X-Authorization: some_token
Content-Type: application/json
Does anyone have any ideas on how I can set the Request Headers to have any field/value I want using ES6 and superagent?
thanks to all in advanced!
try adding the following code to your script, before get is called.
ajax._defaultHeaders = {};
function isObject(obj) { return Object(obj) === obj; };
ajax.set = (function (field, value) {
if (isObject(field)) {
for(var key in field) this.set(key, field[key]);
return this;
}
this._defaultHeaders[field] = value;
return this;
}).bind(ajax)
used to have similar problem with Spring Boot application and React JS on frontend.
When I dump headers that were attached to RQ I used to see only headers with pattern:
Access-Control-Request-Headers:content-type, x-authorization, etc...
however that was problem connected with my backend application, NOT with superagent on frontend.
I had to turn on cors() in WebSecurityConfig to look similar to this one:
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
.cors().and()
.authorizeRequests().antMatchers("/auth/login", "/auth/register").permitAll().
anyRequest().authenticated().and().
exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
so as you can see problem was with Spring configuration, not with Superagent,
Regards
R.
Context / As Is
I'm currently developing an single-page Angular JS application using SpringBoot with a Spring Security implementation. The application is for a 'Study Planner', where student can enter study leave from work in a Calendar. So far we have the student able to log in, and enter details in to the calendar client-side.
Aim / To Be
After being able to capture the events addition within our client-side
javascript, we are attempting to send this to back to our Server using an Ajax POST. Once sent to the server, the aim is then to save this within our database against the student so that it may be loaded next time the student views the calendar.
Issue
The issue we are having is around the Ajax POST method, caused I believe by the introduction of a csrf header to the POST in an attempt to get past Spring Security. Without the csrf header, I see the network traffic within Chrome and receive a 403 (unauthorised) error. With the csrf header introduced, there is no network traffic logged, the server is not hit and the ajax "error" function is hit.
Without CSRF Token
Student Logs in to their Calendar
Student add in an event
Alert triggered 'newEventData function'
Alert triggered 'we failed'
403 Error in Chrome
403 (Forbidden)
"Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-XSRF-TOKEN'."
With CSRF Token
(Adapted from spring docs to be in 'beforesend'. Same happens if I just add the function from the docs to the bottom og my js file)
Student Logs in to their Calendar
Student add in an event
Alert triggered 'newEventData function'
Alert triggered 'beforesend'
Alert triggered 'we failed'
Error thrown
SyntaxError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': '$(_csrf.headerName}' is not a valid HTTP header field name.
Areas for Assistance
How can I see more information around the Ajax error.
What could be causing no network traffic to be displayed at all, even though the ajax is firing and returning in to the error.
Any tips for configuration of the spring security to avoid this (apart from simply turning it off)
Other Relevant Information
We have extended the WebSecurityConfigAdapter
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
/**
* Set up url based security
*/
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests().antMatchers("/index.html", "/home.html",
"/login.html", "/", "/user","/login")
.permitAll().anyRequest().authenticated().and()
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class).csrf()
.csrfTokenRepository(csrfTokenRepository()).and().logout();
}
#Override
/**
* Set up a service to handle authentication requests
*/
public void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.userDetailsService(userDetailsService).passwordEncoder(new
// BCryptPasswordEncoder());
auth.userDetailsService(userDetailsService);
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
We have also extended 'OncePerRequestFilder' (Though none of these System Outs are printing on the Ajax POST)
public class CsrfHeaderFilter extends OncePerRequestFilter {
/**
* Overrides SpringSecurity filter
*/
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
System.out.println("In doFilterInternal");
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
System.out.println("csrf is null");
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie == null || token != null && !token.equals(cookie.getValue())) {
System.out.println("cookie null token not and token doesnt equal cookie");
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
System.out.println("doing filter chain");
filterChain.doFilter(request, response);
}
}
This is the method we are trying to hit. If I remove the csrf security from our HttpSecurity this is hit fine and prints out all the details.
#RequestMapping(method = RequestMethod.POST, value = "/Student/{studentusername}/universitydays")
public void newEvent(String id, String text, String start, String end, #PathVariable String studentusername) {
System.out.println(text);
System.out.println(studentusername);
System.out.println(id);
System.out.println(start);
System.out.println(new Date(Long.parseLong(start)));
System.out.println(new Date(Long.parseLong(end)));
}
Apologies for the super long post, this is something I've been looking at for a while but do not have a great understanding of web-app security so am struggling to fit the pieces together. Any guidance would be much appreciated.
Cheers,
Scott.
Update
After adding in the error parameters suggested by Paqman I now have the following information, but still not sure on how to approach.
SyntaxError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': '${_csrf.headerName}' is not a valid HTTP header field name.
When using my chrome console, these are coming back as text variables. I presume this is incorrect?
My code in within index.html header (other pages are loaded in as 'partials') is:
<head>
<title>Study Planner</title>
...css files...
<meta name="_csrf" content="${_csrf.token}" />
<meta name="_csrf_header" content="${_csrf.headerName}" />
</head>
Further Update
We managed to fix the literal string issue described above by using th:content rather than 'content', but this still gave us errors on the header/token being undefined. The actual resolution to our issue I have posted as an answer, but if someone has similar problems on retrieving the meta-data within html files in an Angular project I believe changing the 'content' to 'th:content' may assist.
See this related question
<head>
<title>Study Planner</title>
...css files...
<meta name="_csrf" th:content="${_csrf.token}" />
<meta name="_csrf_header" th:content="${_csrf.headerName}" />
</head>
After much confusion we finally resolved this issue. Though most documentation I found pointed to the csrf token being stored in the session, in our application we override the default SpringSecurity behaviour to store this within a cookie (taken from this SpringBoot Angular Guide).
public class CsrfHeaderFilter extends OncePerRequestFilter {
/**
* Overrides SpringSecurity filter
*/
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
System.out.println("csrf is null");
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie == null || token != null && !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
Because of this, we seemed to be 'barking up the wrong tree' when following when following the Spring Docs here.
Once realising this, we actually came across some Django docs which guided us to retrieving the token value from the cookie.
Solution
We added this function into our javascript to retreive the token:
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
Set a var to be the value returned by that function
var csrftoken = getCookie('XSRF-TOKEN');
And finally hard coded (not sure if we'll face problems with this later on) the HEADER, and used the variable created above for the token.
function newEventData(ev) {
$.ajax({
"url" : "/Student/" + loggedinusername
+ "/universitydays?id=" + ev.id + "&text="
+ ev.text + "&start="
+ Date.parse(ev.start_date) + "&end="
+ Date.parse(ev.end_date),
"method" : "POST",
beforeSend : function(xhr) {
xhr.setRequestHeader("X-XSRF-TOKEN", csrftoken);
},
"success" : function() {
},
"error" : function(jqXHR, textStatus, errorThrown) {
alert("error: " + errorThrown);
}
});
}
Hope this helps others if they come across a similar issue.