I'm trying to write an application with separated Backend (written using Spring Boot, and Spring Security for login) and Frontend (ReactJS). Right now I'm struggling with accessing a secured endpoint after a successful login.
What I want to achieve: Make a GET to secured endpoint e.g. '/books/all'. If user not logged in, return 401. If 401 received on front-end, make a POST to '/login'. Then I want to have a successful login and to be able to make a successful GET to '/books/all'.
What doesn't work: The last part. I'm making a POST to '/login' and receive 200 GET. Then I make a second call to '/books/all' and receive GET 401. Also I no longer receive JSESSIONID cookie which worries me.
My question: how to fix this behavior? I believe it's connected to JSESSIONID (server doesn't send information about user being successfully logged in?).
On front-end I'm using axios.
axios.get('http://localhost:8080/rest/book/anna/all')
.then(response => {
console.log('response rebuild');
console.log(response);
if (response.status === 401 && response.request.responseURL === 'http://localhost:8080/login') {
axios.post('http://localhost:8080/login', 'username=c&password=d')
.then(response => {
console.log('response 2');
console.log(response);
})
.catch(error => {
console.log('error');
console.log(error);
})
}
})
.catch(error => {
console.log('error 2');
console.log(error);
axios.post('http://localhost:8080/login', 'username=c&password=d')
.then(response => {
console.log('response 2');
console.log(response);
axios.get('http://localhost:8080/rest/book/anna/all')
.then(response => {
console.log('response 3');
console.log(response);
})
.catch(error => {
console.log('error 3');
console.log(error);
})
})
.catch(error => {
console.log('error');
console.log(error);
})
});
Please mind that I'm aware that this code it's low quality; it's just temporary to check if redirection after login is working.
SecurityConfig.java
package com.shareabook.security;
import com.shareabook.repository.UsersRepository;
import com.shareabook.service.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@EnableJpaRepositories(basePackageClasses = UsersRepository.class)
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService userDetailsService;
@Autowired
private RESTAuthenticationEntryPoint restAuthenticationEntryPoint;
@Autowired
private RESTAuthenticationSuccessHandler restAuthenticationSuccessHandler;
@Autowired
private RESTAuthenticationFailureHandler restAuthenticationFailureHandler;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(getPasswordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers("**/anna/**").authenticated()
.anyRequest().permitAll();
http.exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint);
http.formLogin().successHandler(restAuthenticationSuccessHandler);
http.formLogin().failureHandler(restAuthenticationFailureHandler);
// .and()
// .formLogin().permitAll();
http
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/rest/author/all");
}
private PasswordEncoder getPasswordEncoder() {
return new PasswordEncoder() {
@Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
@Override
public boolean matches(CharSequence charSequence, String s) {
return true;
}
};
}
}
RESTAuthenticationEntryPoint.java
@Component
public class RESTAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
RESTAuthenticationFailureHandler.java
@Component
public class RESTAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException,
ServletException {
super.onAuthenticationFailure(request, response, exception);
}
}
RESTAuthenticationSuccessfulHandler.java
@Component
public class RESTAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
// clearAuthenticationAttributes(request);
HttpSession session = request.getSession();
session.setAttribute("username", "c");
response.setStatus(HttpServletResponse.SC_OK);
}
}
BooksController.java
@RestController
@RequestMapping("/rest/book")
public class BookController {
@CrossOrigin(origins = "http://localhost:8888")
@PreAuthorize("hasAnyRole('ROLE_ADMIN')")
@RequestMapping(value = "/anna/all", method = RequestMethod.GET)
public List<String> securedHello() {
List<String> word = new ArrayList<>();
word.add("all");
System.out.print(word);
return word;
}
}