Resolving CORS Issues in Spring Boot, Spring Security, and Angular: Basic Steps

Resolving CORS Issues in Spring Boot, Spring Security, and Angular: Basic Steps

Setting up Spring Security

Check The Version In pom.xml: Spring Boot and Java 17

 <parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.2.5</version>
		<relativePath/> 
 </parent> 
<!------------------------------------------------------------------------------------------->
<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
               <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>17</java.version>
</properties>
	        

Add in pom.xml

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>
		        

After that: Update Project By going to Maven


After that, restart the application, and Open the browser. Navigate to the previously created URL. You will be prompted to log in.

Form Based Authentication : Enable Spring Security

Setting Default Username to 'user' and Handling Spring Boot Application's Dynamically Generated Password given below[yours will be different while running it.Use that one.]

This is known as Formed Based Authentication And is enabled by default by Spring Security.

JSHON Format


Execute The Same With the Talend API Tester

API TESTER : Add The Extention in Your Browser .Then Start To Test.


Response

Configure Standard UserId and Password :


In


Formed Based Authentication



CORS Error: Unlocking Cross-Origin Resource Sharing

CORS Policy
Access to XMLHttpRequest at 'https://localhost:8080/login' (redirected from 'https://localhost:8080/hello-world/path-variable/in28minutes') from origin 'https://localhost:4200' has been blocked by CORS policy:

Message-1: Response to preflight request doesn't pass access control check
Message-2: No 'Access-Control-Allow-Origin' header is present on the requested resource.

        

Let's try to understand:

CORS stands for Cross-Origin Resource Sharing.

The error you're encountering is due to the CORS (Cross-Origin Resource Sharing) policy restrictions enforced by the browser. It occurs when your Angular application running on https://localhost:4200 tries to make a request to your backend server running on https://localhost:8080, and the server doesn't include the necessary CORS headers to allow the request.

It's a security feature implemented by web browsers to restrict web applications running on one origin (domain, protocol, and port) from making requests to another origin.


Frontend Part: Angular

Basic Service Files [that I used] :[Optional]

src\app\service\http\http-intercepter-basic-auth.service.ts

import { HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { BasicAuthenticationService } from '../basic-authentication.service';

@Injectable({
  providedIn: 'root'
})
export class HttpIntercepterBasicAuthService implements HttpInterceptor {

  constructor(
    private basicAuthenticationService: BasicAuthenticationService
  ) { }

  intercept(request: HttpRequest<any>, next: HttpHandler) {
    
    let basicAuthHeaderString = this.basicAuthenticationService.getAuthenticatedToken();
    let username = this.basicAuthenticationService.getAuthenticatedUser();


    if (basicAuthHeaderString && username) {
      request = request.clone({
        setHeaders: {
          Authorization: basicAuthHeaderString
        }
      })
    }
    return next.handle(request);
  }
}
        
Then, Configure HttpInterceptor as Provider in App Module
 providers: [
     provideClientHydration(),
    { provide: HTTP_INTERCEPTORS, useClass: HttpIntercepterBasicAuthService, multi: true }
]        
For Dynamic Authentication: src\app\service\basic-authentication.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { map } from 'rxjs/operators';
import { API_URL } from '../app.constants';

export const TOKEN = 'token'
export const AUTHENTICATED_USER = 'authenticatedUser'

@Injectable({
  providedIn: 'root'
})
export class BasicAuthenticationService {

  constructor(private http: HttpClient) { }

  executeAuthenticationService(username: string, password: string) {

    let basicAuthHeaderString = 'Basic ' + window.btoa(`${username}:${password}`);

    let headers = new HttpHeaders({ Authorization: basicAuthHeaderString })

    return this.http.get<AuthenticationBean>(`${API_URL}/basicauth`,
    { headers }).pipe(
      map(
        data => {
          sessionStorage.setItem(AUTHENTICATED_USER, username);
          sessionStorage.setItem(TOKEN ,  basicAuthHeaderString );
          return data;
        }
      )
    );
  }

  getAuthenticatedUser(): string | null{
    return sessionStorage.getItem(AUTHENTICATED_USER)
  }

  getAuthenticatedToken(): string | null {
    if (this.getAuthenticatedUser()) {
      return sessionStorage.getItem(TOKEN );
    } else {
      return null;
    }
  }

  isUserLoggedIn(): boolean {
    let user = sessionStorage.getItem(AUTHENTICATED_USER)
    return !(user === null)
  }

  logout():void {
    sessionStorage.removeItem(AUTHENTICATED_USER)
    sessionStorage.removeItem(TOKEN )
  }
}

export class AuthenticationBean {
  constructor(public message: string) { }
}
        

?WelcomeMessage >> Hello World : [Optional]

src\app\service\data\welcome-data.service.ts
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { API_URL } from '../../app.constants';

//Create a class; to represent the structure if the response from backend service
export class HelloWorldBean {
  constructor(
    public message: string,
    public error: any
  ) { }
}

@Injectable({
  providedIn: 'root'
})
export class WelcomeDataService {

  constructor(
    private http: HttpClient  //inject httpclient
  ) { }

  executeHelloWorldBeanService() {
    //then ,Invoke the helloworldbean service
    return this.http.get<HelloWorldBean>(`${API_URL}/hello-world-bean`);
    // console.log("Execute Hello World Bean Service");
  }
   //https://localhost:8080/hello-world/path-variable/in28minutes

  executeHelloWorldServiceWithPathVariable(name : string) {


    return this.http.get<HelloWorldBean>
     (`${API_URL}/hello-world/path-variable/${name}`);
   
  }

}
        
src\app\welcome\welcome.component.ts
import { WelcomeDataService, HelloWorldBean } from '../service/data/welcome-data.service';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
//import { HttpErrorResponse } from '@angular/common/http';

@Component({
  selector: 'app-welcome',
  templateUrl: './welcome.component.html',
  styleUrl: './welcome.component.css'
})
export class WelcomeComponent implements OnInit {

  name = '';
  welcomeMessageFromService : string | undefined;
  errorMessage: string | undefined;

  //ActivatedRoute
  constructor(
    private route: ActivatedRoute,
    private service: WelcomeDataService
  ) { }

  ngOnInit(): void {
    this.name = this.route.snapshot.params['name']
  }

  getWelcomeMessage() {
    this.service.executeHelloWorldBeanService().subscribe(
      response => {
        this.handleSuccessfulResponse(response);
      },
      error => {
        this.handleErrorResponse(error);
      }
    );
  }

  getWelcomeMessageWithParameter(){
    this.service.executeHelloWorldServiceWithPathVariable(this.name).subscribe(
      response => {
        this.handleSuccessfulResponse(response);
      },
      error => {
        this.handleErrorResponse(error);
      }
    );
  }

  handleSuccessfulResponse(response: HelloWorldBean) {
    this.welcomeMessageFromService = response.message; //Assigning the entire response object
    this.errorMessage = undefined; //Clear any previous error message
  }

  handleErrorResponse(error :any){
    this.errorMessage = error.message;
    this.welcomeMessageFromService = undefined; //Clear any previous welcome message
  }
}
        
?src\app\welcome\welcome.component.html
<h1>Welcome!!!</h1>
<div class="container">
    <p>Welcome {{name}}. You can manage your todos <a routerLink="/todos">here.</a></p>
</div>

<div class="container">
    Click here to get a customized welcome message.
    <button (click)="getWelcomeMessageWithParameter()" class="btn btn-success">Get Welcome Message</button>
</div>

<div class="container" *ngIf="welcomeMessageFromService">
    <h2>Your Customized Welcome Message</h2>
    {{ welcomeMessageFromService }}
</div>

<div class="container" *ngIf="errorMessage">
    <h2>Error Message</h2>
    {{ errorMessage }}
</div>        

Backend Part : Springboot | Spring Security

Resolved CORS Issue [Imp.]
File Structure That I Implemented
Package : com.sofi.rest.webservices.restfulwebservices.basic.auth
package com.sofi.rest.webservices.restfulwebservices.basic.auth;
import org.springframework.http.HttpStatus;
public class AuthenticationBean {
	
	private String message;
	public AuthenticationBean(String message) {
		super();
		this.message = message;
	}
	public String getMessage() {
		return message;
	}
	public void setMessage(String message) {
		this.message = message;
	}
	@Override
	public String toString() {
		return "HelloWorldBean [message=" + message + "]";
	}
	public static Object status(HttpStatus internalServerError) {
		// TODO Auto-generated method stub
		return null;
	}
	
}        
package com.sofi.rest.webservices.restfulwebservices.basic.auth;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@CrossOrigin(origins = "https://localhost:4200")
@RestController
public class BasicAuthenticationController {
  
   @GetMapping("/basicauth")
   public AuthenticationBean helloWorldBean() {
       return new AuthenticationBean("You are authenticated!");	
   }
}
        
package com.sofi.rest.webservices.restfulwebservices.basic.auth;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SpringSecurityConfigurationBasicAuth {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeHttpRequests(auth ->
                        auth
                            .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                            .anyRequest().authenticated()
                )
                .httpBasic(Customizer.withDefaults())
                .sessionManagement(session ->
                        session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                )
                .csrf().disable()
                .build();
    }
}
        
For Global Configuration: com.sofi.rest.webservices.restfulwebservices.basic.auth.config
package com.sofi.rest.webservices.restfulwebservices.basic.auth.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("https://localhost:4200")
                .allowedMethods(HttpMethod.GET.name(),
                        HttpMethod.POST.name(),
                        HttpMethod.PUT.name(),
                        HttpMethod.DELETE.name())
                .allowedHeaders(HttpHeaders.CONTENT_TYPE,
                        HttpHeaders.AUTHORIZATION);
    }
}

        

For Welcome Component :com.sofi.rest.webservices.restfulwebservices.helloworld
package com.sofi.rest.webservices.restfulwebservices.helloworld;
public class HelloWorldBean {
	
	private String message;
	public HelloWorldBean(String message) {
		super();
		this.message = message;
	}
	public String getMessage() {
		return message;
	}
	public void setMessage(String message) {
		this.message = message;
	}
	@Override
	public String toString() {
		return "HelloWorldBean [message=" + message + "]";
	}
}

        
package com.sofi.rest.webservices.restfulwebservices.helloworld;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
@CrossOrigin(origins = "https://localhost:4200")
public class HelloWorldController {
   @GetMapping("/hello-world")
   public String helloWorld() {
       return "Hello World";
   }
   @GetMapping("/hello-world-bean")
   public HelloWorldBean helloWorldBean() {
       return new HelloWorldBean("Hello World - Welcome to Coding Universe");
   }
   @GetMapping("/hello-world/path-variable/{name}")
   public HelloWorldBean helloWorldPathVariable(@PathVariable String name) {
       return new HelloWorldBean(String.format("Hello World, %s", name));
   }
}
        

Output :

By clicking Get Welcome Message : I get customized message


Reference :

[1] Go Java Full Stack with Spring Boot and Angular with in28minutes

Sofia Nayak

Aspiring Full-Stack Developer | Google India Scholar 2018 | Udacity Nanodegree Graduate

10 个月

After three days of troubleshooting, I finally discovered a silly mistake that was causing the CORS challenge. With that resolved, the problem is finally solved. Happy coding !!! ??

回复

要查看或添加评论,请登录

Sofia Nayak的更多文章

社区洞察

其他会员也浏览了