Resolving CORS Issues in Spring Boot, Spring Security, and Angular: Basic Steps
Sofia Nayak
Aspiring Full-Stack Developer | Google India Scholar 2018 | Udacity Nanodegree Graduate
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.
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.
Execute The Same With the Talend API Tester
Configure Standard UserId and Password :
CORS Error: Unlocking Cross-Origin Resource Sharing
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.]
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 :
Reference :
[1] Go Java Full Stack with Spring Boot and Angular with in28minutes
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 !!! ??