Spring websocket with VueJs - blocked by CORS - spring

I am trying to find a solution on how to connect VueJs and Spring via WebSocket.
WebSocket conf (Spring)
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gs-guide-websocket")
.setAllowedOrigins("http://localhost:8080")
.withSockJS();
}
I have tried few options with .setAllowedOrigins(""), like "*", "http://localhost:8080" or don't use it.
I have CORS conf in my project also.
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:8080"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("authorization", "content-type", "x-auth-token"));
configuration.setExposedHeaders(Arrays.asList("x-auth-token"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
In my CORS conf I have tried many options, but without result :(
And the last thing is the client-side connection (VueJs):
connect() {
this.socket = new SockJS("http://localhost:8081/gs-guide-websocket");
this.stompClient = Stomp.over(this.socket);
this.stompClient.connect(
{},
frame => {
this.connected = true;
console.log(frame);
this.stompClient.subscribe("/topic/chat/1", tick => {
console.log(tick);
this.received_messages.push(JSON.parse(tick.body).content);
});
},
error => {
console.log(error);
this.connected = false;
}
);
}
And on everything I have tried, I am receiving this error:
Access to XMLHttpRequest at 'http://localhost:8081/gs-guide-websocket/info?t=1583167396687' from origin 'http://localhost:8080' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
Is there any solution that I can use to make it work?
FYI:
Spring: localhost:8081
VueJs: localhost:8080
Thanks for your help all :)

I was so close. Everything here is right (not for the production of course).
The last thing that was missing in CORS conf was to set Allow credentials to true.
I hope this post will help you :)

Related

Why does Spring Security return 403 in Postman or on the browser?

I don't understand. What is it about this code that prevents me from POSTing from the login page /user/login by returning the 403 error status code?
I have disabled the CSRF and I always get 403 response both from the browser and Postman.
I am given the impression JWTVerifier is always called when I POST to /login
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().cors().configurationSource(request -> {
var cors = new CorsConfiguration();
cors.setAllowCredentials(true);
cors.setAllowedOrigins(Collections.singletonList("http://localhost:4200"));
cors.setAllowedHeaders(Arrays.asList("Origin", "Access-Control-Allow-Origin", "Content-Type", "Accept",
"Jwt-Token", "Authorization", "Origin", "Accept", "X-Requested-With",
"Access-Control-Request-Method", "Access-Control-Request-Headers"));
// exposed to the front-end
cors.setExposedHeaders(Arrays.asList("Origin", "Content-Type", "Accept", "Jwt-Token", "Authorization",
"Access-Control-Allow-Origin", "Access-Control-Allow-Credentials"));
cors.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
// /** -> all routes in the back-end app, the configuration will be applied
return cors;
})
// we're not using sessions, not keeping track of currently logging in users
// users prove by using a token -> stateless
.and().sessionManagement().sessionCreationPolicy(STATELESS) //
// open for everyone <- no authentication
.and().authorizeRequests().antMatchers(PUBLIC_URLS).permitAll() //
// anything else - reqs. authentication
.anyRequest().authenticated().and() //
.exceptionHandling().accessDeniedHandler(jwtAccessDeniedHandler)
.authenticationEntryPoint(jwtAuthenticationEntryPoint).and()
.addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class);
}
PUBLIC_URLS is a String[] constant with the value { "/user/login", "/user/register", "user/resetpassword/**", "/user/image/**" };

Angular OAuth2 integration

I am working on a springboot + angular project. And I am implementing oauth for the same.
My Spring boot Oauth services(oauth/token) works fine as expected and was tested in postman successfully. But as am trying to integrate that url with angular, i am continuously facing some trouble. I am always getting 401 unauthorized.
My Angular code is as below
login(userName:any,password:any){
console.log("logged in");
let params = new URLSearchParams();
 params.set('username','1522856566577');   
  params.set('password','sens!tiveP#ss');    
params.set('grant_type','password');
params.set('client_id','clientIdPassword');
params.set('client_secret','secret');
params.set('scope','read');
let headers = new HttpHeaders().
append('Content-type','application/x-www form-urlencoded;
charset=utf- 8').
append('Authorization','Basic '
+btoa("clientIdPassword:secret"));
let options = {
headers:headers
};
  this.http.post('/api/oauth/token',params.toString(), options).subscribe(data => console.log(data),err => console.log('Invalid Credentials')); }}
When you are sending headers from angular, Api needs to allow origins of requests. Add this code in Spring Boot controller above the class like below. After that Api will be allowed to get headers.
#CrossOrigin(origins = "*", maxAge = 3600)
#RestController
public class AuthenticationController {
//services...
}
You are setting client id and secret twice in headers and url search params.try the following code to generate tokens in the service
getToken()
{
const httpOptions = {
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic ' + btoa('your_client_id:your_client_pass')
})
};
var body = "grant_type=client_credentials";
return this.http.post('your_url',body,
httpOptions)
.pipe(map(res=>res.json()));
}

Empty HTTP response headers in browser but filled in POSTMAN

I've created a Spring Boot back end with a React front end.
When I'm sending a HTTP request via browser to my backend, I receive a
response with empty headers.
Browser response with empty headers
When I'm sending a HTTP request via the POSTMAN tool, I got filled headers!
POSTMAN response with fullfilled headers
That doesn't make sense to me.
Code: https://github.com/The-Taskmanager/SelfServiceWebwizard
Back end Mapping
#PostMapping("/signin")
public ResponseEntity<?> authenticateUser(#Valid #RequestBody LoginRequest loginRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getUsernameOrEmail(),
loginRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = tokenProvider.generateToken(authentication);
HttpHeaders headers = new HttpHeaders();
headers.add("Mustermann", "Max");
headers.add("Content-Type", "application/json");
headers.add("Authorization", new JwtAuthenticationResponse(jwt).getAccessToken());
return ResponseEntity.ok().headers(headers).body(new JwtAuthenticationResponse(jwt));
}
Front end request
login(username: string, password: string) {
fetch('http://localhost:8080/api/auth/signin', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
"usernameOrEmail": username,
"password": password
})
}).then(response => {
console.log(response);
console.log(response.text());
console.log(response.headers);
})
.catch(error => console.error(error));
}
UPDATE1:
Added this method but headers in browser still empty:
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration().applyPermitDefaultValues();
source.registerCorsConfiguration("/**", config);
return source;
}
UPDATE2:
When I request with React, Spring Boot shows me this ERROR:
ERROR 8876 --- [nio-8080-exec-4] c.s.jwt.JwtAuthenticationEntryPoint: Responding with unauthorized error. Message - full authentication is required to access this resource
Browser console:
When I request with Postman, Spring Boot shows no ERROR.
UPDATE3:
Request-header send by React:
{
host=[localhost:8080],
content-type=[application/json],
cache-control=[no-cache],
user-agent=[Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0],
accept-encoding=[gzip, deflate],
content-length=[58],
referer=[http://localhost:8080/],
origin=[http://localhost:8080],
dnt=[1],
connection=[keep-alive],
accept-language=[de,en-US;q=0.7,en;q=0.3],
accept=[*/*]
}
Request-header send by POSTMAN from POSTMAN console:
host:"localhost:8080"
content-type:"application/json"
cache-control:"no-cache"
user-agent:"PostmanRuntime/7.1.5"
accept-encoding:"gzip, deflate"
content-length:68
postman-token:"b00fd7a8-bd34-4a32-8681-990b04012e3b"
cookie:"JSESSIONID=2CAEC9280432DD13AABA53B73B7874AC"
accept:"*/*"
Solved by myself.
Changed code in front end's fetch method
console.log(response.headers)
// changed to:
console.log(response.headers.get('Authorization')
So the headers were there all the time but I couldn't manage to log them in browser console with React.
Nevertheless there is the Spring Boot (full authentication) and Browser (401) error in UPDATE2. Don't know where this is coming from.

Printing remote pdf in Sencha ExtJs from Spring-Boot app doesn´t work

I have an ExtJS app that shows a PDF from a spring boot app using a REST service. So far this works fine but when I have updated the spring-boot version from 1.3 -> 1.4, the code doesn´t work fine and shows me a blank PDF as response.
This is my code:
ExtJS - Sencha
Ext.Ajax.request({
url: MyApp.Application.Globals.baseUrl + url,
params: params,
method: 'POST',
async: false,
headers:{
'Authorization': Utils.getAuthorization()
},
scope : this,
// ON SUCCESS
success: function(response) {
window.open('data:application/pdf,' + escape(response.responseText));
},
// ON FAILURE
failure: function(err) {
console.log(err);
}
});
Server Code (Spring-Boot)
String report = reportService.executeReport(....);
response.setContentType("application/pdf");
try {
response.getWriter().write(report);
} catch (IOException e) {
ELogger.error(this, CoreConstants.LOGGER_CATEGORY, "error creating pdf", e);
}
POM
<spring-boot.version>1.4.4.RELEASE</spring-boot.version> with 1.3.3 it works fine
If anyone can help me, I will be grateful.
Regards!
I have found the workaround using ResponseEntity as a response:
#RequestMapping(value = "/example", produces = "application/pdf")
public ResponseEntity<InputStreamResource> exampleMethod(){
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("application/pdf"));
headers.add("Access-Control-Allow-Origin", "*");
headers.add("Access-Control-Allow-Methods", "GET, POST, PUT");
headers.add("Access-Control-Allow-Headers", "Content-Type");
headers.add("Content-Disposition", "filename=sysparamspdf");
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
headers.setContentLength(report.getBytes(StandardCharsets.UTF_8).length);
ResponseEntity<InputStreamResource> response = new ResponseEntity<InputStreamResource>(
new InputStreamResource(stream), headers, HttpStatus.OK);
return response;
}

Enabling WebAPI CORS for Angular 2 authentification

I've seen a few answers on stackoverflow and I'm lost.
I have webapi 2 + standalone angular 2
webapi project is from template. the only thing i've changed is that i added CORS
and following line to IdentityConfig.cs > ApplicationUserManager Create()
context.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "http://localhost:3000" });
here I've all standard from template:
[Authorize]
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
On the client side I have function to get access token, that works properly:
authenticate(loginInfo: Login): boolean {
let headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
this.http.post(this.baseUrl + 'Token', 'grant_type=password&username=alice2#example.com&password=Password2!',
{
headers: headers
})
.subscribe(
data => this.saveAuthToken(<AccessToken>(data.json())),
err => this.handleError(err),
() => console.log('authentication Complete')
);
return true;
}
And get function, that works ok without authentication (commented code) :
get(url: string) {
var jwt = sessionStorage.getItem(this.idTokenName);
var authHeader = new Headers();
if (jwt) {
authHeader.append('Authorization', 'Bearer ' + jwt);
}
return this.http.get(this.apiUrl + url, {
headers: authHeader
})
.map(res => res.json())
.catch(this.handleError);
//return this.http.get(this.apiUrl + url)
// .map(res => res.json())
// .catch(this.handleError);
}
But when i try to add Authorization header server returns:
XMLHttpRequest cannot load http://localhost:3868/api/values. Response for preflight has invalid HTTP status code 405
How to allow user to authenticate through Angular properly?
Install-Package Microsoft.Owin.Cors
Add to App_Start > Startup.Auth.cs > ConfigureAuth(IAppBuilder app)
app.UseCors(CorsOptions.AllowAll);
Only one line. That's all.
You could explicitly add the needed headers and methods:
context.Response.Headers.Add(
"Access-Control-Allow-Headers",
new[] { "Content-Type, Authorization" }
);
context.Response.Headers.Add(
"Access-Control-Allow-Methods",
new[] { "GET, POST, OPTIONS" }
);
I had to add the following to the globalasax.cs:
protected void Application_BeginRequest()
{
var req = HttpContext.Current.Request;
var res = HttpContext.Current.Response;
var val = res.Headers.GetValues("Access-Control-Allow-Origin");
if (val == null)
{
if (!req.Url.ToString().ToLower().Contains("token") || (req.Url.ToString().ToLower().Contains("token") && req.HttpMethod == "OPTIONS"))
{
res.AppendHeader("Access-Control-Allow-Origin", "http://localhost:4200");
}
}
if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
{
res.AppendHeader("Access-Control-Allow-Credentials", "true");
res.AppendHeader("Access-Control-Allow-Headers", "Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name");
res.AppendHeader("Access-Control-Allow-Methods", "POST,GET,PUT,PATCH,DELETE,OPTIONS");
res.StatusCode = 200;
res.End();
}
}
When talking to webapi angular and using a http post that either contains non-standard body contents (i.e json) or authentication then a pre-flight request is set that basically says 'am i okay to send the actual request'. Now there are several ways around this that essentially involve short cuts - use IE (if the server is on the same machine as IE ignores the port when deciding what the same machine is) or open CORS up to permit all (which is dangerous as the granting permission to an authenticated user opens your system up to all manner of hacks).
Anyway the solution we used was to add a method to the Globals.asax.cs on the server
protected void Application_BeginRequest()
{
if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
{
var origin = HttpContext.Current.Request.Headers["Origin"];
Response.Headers.Add("Access-Control-Allow-Origin", origin);
Response.Headers.Add("Access-Control-Allow-Headers", "content-type, withcredentials, Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
Response.Headers.Add("Access-Control-Allow-Credentials", "true");
Response.Headers.Add("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS, POST, PUT, DELETE");
Response.Flush();
}
}
Now the above is checking for the pre-flight very specifically and if it finds it it adds permissions to send the next request. On your system you may need to tweek the Allow_Headers request (easiest way is to use your browser f12 to look at what headers your pre-flight request is actually sending out.
Note that the above just deals with the pre-flight CORS will still apply for the actual http POST which will need correctly handling. For this we added the server we wanted to allow in to settings and then added the System.Web.Http.Cors to the WebApiConfig Register method as follows
var cors = new EnableCorsAttribute(Properties.Settings.Default.CORSOriginPermittedSite, "*", "GET, HEAD, OPTIONS, POST, PUT, DELETE");
cors.SupportsCredentials = true;
config.EnableCors(cors);
This avoids hard coding the site which a production system really wants to avoid.
Anyway hopefully that will help.

Resources