I am trying to implement login authentication in spring boot ,I have used Spring security and used a react form in the frontend to authenticate , I am using axios to call my /login
which works well, the form opens and authenticates perfectly but the problem lies here is that after login is successful i am unable to access other endpoints declared in my spring controller
This is my Spring security config
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth->auth
.requestMatchers("/","/login/**","/register")
.permitAll()
.anyRequest()
.authenticated())
.logout(logout->logout
.logoutUrl("/logout")
.logoutSuccessUrl("/")
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true))
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(session->session.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))
.build();
}
My login controller
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody RegisterUser registerUser) {
try {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(registerUser.getEmail(), registerUser.getPassword())
);
if (authentication.isAuthenticated()) {
return ResponseEntity.ok().body("Login successful");
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Login failed");
}
} catch (AuthenticationException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Login failed");
}
}
This is my react form
axios.defaults.baseURL="http://localhost:8080"
axios.defaults.withCredentials=true;
const Login=()=>{
const [email, setEmail]=React.useState('');
const [password, setpassword]=React.useState('');
const [error, setError] = React.useState('');
const navigate =useNavigate();
const handlesubmit= async (e)=>{
e.preventDefault();
try {
const response = await axios.post("/login" ,{
email,
password,
});
if(response.status===200){
console.error(email,password)
navigate('/');
}
else {
navigate('/logs')
}
}
catch (e){
console.error(e)
setError("login failed")
}
}
Tried using login->login.form("/login")
and loginprocesssurl("/login")
in security config yet doesnt work, I have also set my proxy to the port where spring is running yet i have no results
What i want now is after the login is successful ( it is ) i want to access the other endpoints which only authenticated users can access
With the custom endpoint, there is one slight detail missing: SecurityContext
is not populated during the login request/response. Without storing it, further requests still will be anonymous despite successful authentication.
Preferred way is having login logic in the SecurityFilterChain
:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(...)
.formLogin(it -> it
.loginPage("/") // mapped to index.html, letting react handle it
.loginProcessingUrl("/login")
.successHandler((request, response, authentication) -> {
response.setStatus(HttpStatus.OK.value());
response.getWriter().print("Login successful");
})
.failureHandler((request, response, exception) -> {
response.sendError(HttpStatus.UNAUTHORIZED.value(), "Login failed");
})
)
.logout(...)
...
.build();
}
Why's that? It adds additional UsernamePasswordAuthenticationFilter
, which contains the authentication logic of your @PostMapping("/login")
endpoint + it stores the SecurityContext
in ThreadLocal
. Later it is saved by SecurityContextRepository
in the other filter down the chain. (Note, it requires explicit save since 6 version)
If you really want to control the signing in flow in your endpoint (maybe for some side effects), save the context there:
...
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody RegisterUser registerUser) {
try {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(registerUser.getEmail(), registerUser.getPassword())
);
if (authentication.isAuthenticated()) {
// storing context in ThreadLocal, later will be saved in SecurityContextPersistenceFilter
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
return ResponseEntity.ok().body("Login successful");
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Login failed");
}
} catch (AuthenticationException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Login failed");
}
}
...