I am currently working on an application in JAVA SPRING and ANGULAR. I am a novice and I am trying to progress in these two languages.
I am currently finishing the entire security part but there is one last point that is blocking me. When I log in to my application, authentication fails even though the credentials are correct. I retrieve the information correctly but when sending it to the back-end it blocks.
The problem comes from a constant which apparently is not in the right format but I can't figure out how to change that. I first tried putting it in JSON format but that obviously wasn't the right thing to do.
I share the code with you below:
In the SPRING backend:
Here is my security configuration
import java.util.Arrays;
import java.util.Collections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.header.writers.StaticHeadersWriter;
import com.tracker.EsportTracker.service.impl.AppUserDetailsService;
import com.tracker.EsportTracker.service.impl.UserService;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
UserService userService;
@Autowired
AuthenticationSuccessHandler authenticationSuccessHandler;
@Autowired
private AppUserDetailsService appUserDetailsService;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(appUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
http.csrf(AbstractHttpConfigurer::disable);
http.authorizeHttpRequests(
auth-> auth
.requestMatchers("/user/**", "/users/**","/tournaments/**","/tours/**").permitAll()
.requestMatchers("").hasRole("ADMIN")
.requestMatchers("").hasRole("USER")
.requestMatchers("/login/user").authenticated()
.anyRequest().authenticated()
)
.userDetailsService(appUserDetailsService)
.formLogin(
formLogin -> formLogin
.loginPage("/login/user")
.loginProcessingUrl("/auth")
.defaultSuccessUrl("/")
.successHandler(authenticationSuccessHandler)
.permitAll()
)
.logout(
logout -> logout
.logoutUrl("/signout").permitAll()
)
.exceptionHandling(
exceptionHandling -> exceptionHandling
.accessDeniedPage("/accesRefuse")
);
return http.build();
}
@Bean
public FilterRegistrationBean simpleCorsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.setAllowedOrigins(Arrays.asList("http://localhost:4200","http://localhost:4200/"));
config.setAllowedMethods(Collections.singletonList("*"));
config.setAllowedHeaders(Collections.singletonList("*"));
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
}
Then the AuthenticationSuccessHandlerImp class which is implemented in the latter
import java.io.IOException;
import java.util.Collection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import com.tracker.EsportTracker.entities.User;
import com.tracker.EsportTracker.service.impl.UserService;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
@Component
public class AuthenticationSuccessHandlerImp implements AuthenticationSuccessHandler{
@Autowired
UserService userService;
@Autowired
HttpSession session;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_OK);
boolean admin = false;
String username = authentication.getName();
User personne=userService.findByUsername(username);
session.setAttribute("User",personne);
}
}
Finally the controller which manages the connection
import java.security.Principal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.tracker.EsportTracker.entities.User;
import com.tracker.EsportTracker.service.interfaces.IUserService;
@RestController
@RequestMapping(value = "/login")
public class LoginController {
@Autowired
IUserService userService;
@CrossOrigin
@RequestMapping(value="/user")
public ResponseEntity<User> login(Principal principal) {
System.out.println(principal);
if (principal != null) {
User user = userService.findByUsername(principal.getName());
return ResponseEntity.ok(user);
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
}
}
In the ANGULAR FRONT:
Here is my typed text for the login component
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { AppService } from '../services/app.service';
import { UserService } from '../services/user.service';
import { User } from '../models/user';
import { SharedService } from '../services/shared.service';
import { UserComponent } from '../user/user.component';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent {
user = '';
credentials = {username:'', password:''}
constructor(private appService:AppService,private httplClient:HttpClient,private router:Router,private userService:UserService, private sharedService: SharedService) { }
login() {
console.log("username=" + this.credentials.username + " password=" + this.credentials.password);
this.appService.authenticate(this.credentials, () => {
});
this.userService.findOneByUsername(this.credentials.username).subscribe(
(data: User) => {
var user: User = data;
localStorage.setItem("UserId", user.idUser.toString());
localStorage.setItem("Username", user.username.toString());
this.userService.getUserTournaments(user.idUser);
this.sharedService.changeUsername(user.username);
this.router.navigateByUrl("/users");
},
(error) => {
console.error("Une erreur s'est produite lors de la récupération des données de l'utilisateur :", error);
}
);
return false;
}
}
Then the service that connects to the back
import { Injectable } from '@angular/core';
import { environment } from 'src/environments/environment.ts';
@Injectable({
providedIn: 'root'
})
export class AppService {
authenticated = false;
responseAll: any;
isAdmin = false;
str: any;
constructor(private httpClient: HttpClient) { }
authenticate(credentials: any, callback: any) {
const headers = new HttpHeaders({
Authorization: 'Basic ' + btoa(credentials.username + ':' + credentials.password)
});
const authEndpoint = environment.apiBaseUrl + 'login/user';
this.httpClient.get(authEndpoint, { headers:headers }).subscribe(
(response) => {
this.authenticated = true;
this.responseAll = response;
this.isAdmin = this.checkIfAdmin(response);
this.str = 'Set your value here based on the response';
if (callback) {
callback();
}
},
(error) => {
console.error('Authentication error:', error);
this.authenticated = false;
this.isAdmin = false;
this.responseAll = null;
this.str = null;
}
);
}
private checkIfAdmin(response: any): boolean {
return false;
}
}
The error that appears is:
Honestly, I looked in the Angular documentation but I don't know what to do anymore.
HttpErrorResponse
error
:
null
headers
:
HttpHeaders
lazyInit
:
() => {…}
length
:
0
name
:
""
arguments
:
(...)
caller
:
(...)
[[FunctionLocation]]
:
http.mjs:64
[[Prototype]]
:
ƒ ()
[[Scopes]]
:
Scopes[4]
lazyUpdate
:
null
normalizedNames
:
Map(0) {size: 0}
[[Prototype]]
:
Object
message
:
"Http failure response for http://localhost:7070/login/user: 401 OK"
name
:
"HttpErrorResponse"
ok
:
false
status
:
401
statusText
:
"OK"
url
:
"http://localhost:7070/login/user"
[[Prototype]]
:
HttpResponseBase
2023-12-06T11:58:17.036+01:00[0;39m [32mDEBUG[0;39m [35m19728[0;39m [2m---[0;39m [2m[nio-7070-exec-3][0;39m [36mo.s.security.web.FilterChainProxy [0;39m [2m:[0;39m Securing GET /login/user
[2m2023-12-06T11:58:17.036+01:00[0;39m [32mDEBUG[0;39m [35m19728[0;39m [2m---[0;39m [2m[nio-7070-exec-2][0;39m [36mo.s.security.web.FilterChainProxy [0;39m [2m:[0;39m Securing GET /users/secu/GotaKev
[2m2023-12-06T11:58:17.042+01:00[0;39m [32mDEBUG[0;39m [35m19728[0;39m [2m---[0;39m [2m[nio-7070-exec-3][0;39m [36mo.s.security.web.FilterChainProxy [0;39m [2m:[0;39m Secured GET /login/user
[2m2023-12-06T11:58:17.050+01:00[0;39m [32mDEBUG[0;39m [35m19728[0;39m [2m---[0;39m [2m[nio-7070-exec-2][0;39m [36mo.s.security.web.FilterChainProxy [0;39m [2m:[0;39m Secured GET /users/secu/GotaKev
[2m2023-12-06T11:58:17.058+01:00[0;39m [32mDEBUG[0;39m [35m19728[0;39m [2m---[0;39m [2m[nio-7070-exec-3][0;39m [36mo.s.s.w.a.AnonymousAuthenticationFilter [0;39m [2m:[0;39m Set SecurityContextHolder to anonymous SecurityContext
null
je suis la méthode findByUsername de la classe serviceGotaKev
[2m2023-12-06T11:58:17.163+01:00[0;39m [32mDEBUG[0;39m [35m19728[0;39m [2m---[0;39m [2m[nio-7070-exec-2][0;39m [36mo.s.s.w.a.AnonymousAuthenticationFilter [0;39m [2m:[0;39m Set SecurityContextHolder to anonymous SecurityContext
[2m2023-12-06T11:58:17.173+01:00[0;39m [32mDEBUG[0;39m [35m19728[0;39m [2m---[0;39m [2m[nio-7070-exec-5][0;39m [36mo.s.security.web.FilterChainProxy [0;39m [2m:[0;39m Securing GET /users
[2m2023-12-06T11:58:17.173+01:00[0;39m [32mDEBUG[0;39m [35m19728[0;39m [2m---[0;39m [2m[nio-7070-exec-4][0;39m [36mo.s.security.web.FilterChainProxy [0;39m [2m:[0;39m Securing GET /tournaments
[2m2023-12-06T11:58:17.173+01:00[0;39m [32mDEBUG[0;39m [35m19728[0;39m [2m---[0;39m [2m[nio-7070-exec-5][0;39m [36mo.s.security.web.FilterChainProxy [0;39m [2m:[0;39m Secured GET /users
[2m2023-12-06T11:58:17.175+01:00[0;39m [32mDEBUG[0;39m [35m19728[0;39m [2m---[0;39m [2m[nio-7070-exec-4][0;39m [36mo.s.security.web.FilterChainProxy [0;39m [2m:[0;39m Secured GET /tournaments
[2m2023-12-06T11:58:17.175+01:00[0;39m [32mDEBUG[0;39m [35m19728[0;39m [2m---[0;39m [2m[nio-7070-exec-6][0;39m [36mo.s.security.web.FilterChainProxy [0;39m [2m:[0;39m Securing GET /users/secu/GotaKev
[2m2023-12-06T11:58:17.175+01:00[0;39m [32mDEBUG[0;39m [35m19728[0;39m [2m---[0;39m [2m[nio-7070-exec-6][0;39m [36mo.s.security.web.FilterChainProxy [0;39m [2m:[0;39m Secured GET /users/secu/GotaKev
je suis la méthode findByUsername de la classe serviceGotaKev
[2m2023-12-06T11:58:17.185+01:00[0;39m [32mDEBUG[0;39m [35m19728[0;39m [2m---[0;39m [2m[nio-7070-exec-6][0;39m [36mo.s.s.w.a.AnonymousAuthenticationFilter [0;39m [2m:[0;39m Set SecurityContextHolder to anonymous SecurityContext
[2m2023-12-06T11:58:17.187+01:00[0;39m [32mDEBUG[0;39m [35m19728[0;39m [2m---[0;39m [2m[nio-7070-exec-7][0;39m [36mo.s.security.web.FilterChainProxy [0;39m [2m:[0;39m Securing GET /users/1/tournaments
[2m2023-12-06T11:58:17.188+01:00[0;39m [32mDEBUG[0;39m [35m19728[0;39m [2m---[0;39m [2m[nio-7070-exec-7][0;39m [36mo.s.security.web.FilterChainProxy [0;39m [2m:[0;39m Secured GET /users/1/tournaments
[2m2023-12-06T11:58:17.199+01:00[0;39m [32mDEBUG[0;39m [35m19728[0;39m [2m---[0;39m [2m[nio-7070-exec-7][0;39m [36mo.s.s.w.a.AnonymousAuthenticationFilter [0;39m [2m:[0;39m Set SecurityContextHolder to anonymous SecurityContext
[2m2023-12-06T11:58:17.203+01:00[0;39m [32mDEBUG[0;39m [35m19728[0;39m [2m---[0;39m [2m[nio-7070-exec-5][0;39m [36mo.s.s.w.a.AnonymousAuthenticationFilter [0;39m [2m:[0;39m Set SecurityContextHolder to anonymous SecurityContext
[2m2023-12-06T11:58:17.209+01:00[0;39m [32mDEBUG[0;39m [35m19728[0;39m [2m---[0;39m [2m[nio-7070-exec-4][0;39m [36mo.s.s.w.a.AnonymousAuthenticationFilter [0;39m [2m:[0;39m Set SecurityContextHolder to anonymous SecurityContext
Thanking you in advance for the help
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));can be removed, read the CORS chapter in the spring security official docs on the webpage, shows you exactly how to configure CORS, you also dont need ` @CrossOrigin` annotation . You use either a bean or the annotation, not both. We cant run your code, so its impossible for us without logs they will tell you the exact reason for the 401