So I am working on my first full-stack application (spring boot rest API and Vue.js frontend) and I came across a problem by using sonarqube.
My sonarqube gives the following warning:
Make sure disabling Spring Security's CSRF protection is safe here.
and it is coming from this file:
#Configuration
#AllArgsConstructor
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class
WebSecurityConfig extends WebSecurityConfigurerAdapter {//provides security for endpoints
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
private final AccountService accountService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()/*.disable()*/.and()//So we can send post requests without being rejected(if we using form based indication we want to enable this)
.authorizeRequests()
.antMatchers("/login", "/authenticate","/register", "/register/**")
.permitAll()//any request that goes trough that end point we want to allow.
.anyRequest()
.authenticated().and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
http.cors();
http.logout().permitAll();
http.logout().logoutSuccessHandler((new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK)));
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider =
new DaoAuthenticationProvider();
provider.setPasswordEncoder(bCryptPasswordEncoder);
provider.setUserDetailsService(jwtUserDetailsService);
return provider;
}
}
More specifically this piece of code:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()/*.disable()*/.and()//So we can send post requests without being rejected(if we using form based indication we want to enable this)
.authorizeRequests()
.antMatchers("/login", "/authenticate","/register", "/register/**")
.permitAll()//any request that goes trough that end point we want to allow.
.anyRequest()
.authenticated().and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
http.cors();
http.logout().permitAll();
http.logout().logoutSuccessHandler((new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK)));
When I remove the first .and() and use disable (which is commented out now) my program works, but I want to find a solution where I can .csrf() let be enabled (I know it is standard enabled) and where my login stops giving me a 401 error.
Thanks in advance!
Apparently, you are using JWTs for authenticating requests. This typically does not involve cookies (tokens are usually sent as request headers). If this is the case for you (JWT is received in a header) you can disregard the Sonarqube warning, you don't need CSRF protection.
The reason for this is CSRF is an attack where the attacker exploits the existing session of a victim user, when the victim visits a malicious website. This is based on cookies being sent with a request by the browser only depend on the destination, and not the origin (ie. a javascript on attacker.com can make a request to victim.com, and a user's cookies for victim.com will be sent automatically). If request headers are used to transmit a token, that cannot be accessed by an attacker on their malicious domain.
If you still wanted to make it work (because for example your JWTs are indeed received from a cookie), you would have to send the correct CSRF token from your frontend (VueJS) with every request that's not a get, so it's a change for your frontend, not your backend.
Related
I'm trying to implement SessionCreationPolicy.ALWAYS for the /testMVCController/** endpoint and SessionCreationPolicy.STATELESS for rest of endpoints (/**).
Expected scenario:
When accessing to /testMVCController/displayUsers the user logs in once and the log I have implemented in UserDetailsService logs the authorities associated to that user.
After that, all the requests to /testMVCController/displayUsers or other URL under /testMVCController/** will not log the authorities again because the session creation policy is always and the user is already logged in.
This works when I don't specify the 2nd security configuration (X509ClientSessionCreationPolicyStateless) but when I add it, all the requests become session stateless.
It is not working with the current security configuration because after I log in with my client certificate, at any request executed under /testMVCController/** endpoint (e.g. /testMVCController/displayUsers), the authenticationUserDetailsService is consulted and the list of authorities is logged for each file request the browser makes (.js file, .css files, ...), even after the initial login.
So, if there are 3 requests (/testMVCController/displayUsers, displayUsers.js, displayUsers.css) the list of authorities log present in authenticationUserDetailsService is logged 3 times.
I configured SecurityConfiguration as shown below but it is not working:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
public class SecurityConfiguration {
#Configuration
#Order(1)
public static class X509ClientSessionCreationPolicyAlways extends WebSecurityConfigurerAdapter {
#Autowired
private X509CUDService x509CUDService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/testMVCController/**")
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.x509()
.authenticationUserDetailsService(x509CUDService)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
}
}
#Configuration
#Order(2)
public static class X509ClientSessionCreationPolicyStateless extends WebSecurityConfigurerAdapter {
#Autowired
private X509CUDService X509CUDService ;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/**")
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.x509()
.authenticationUserDetailsService(X509CUDService);
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
}
I've searched this issue and I found various links (e.g. Spring session creation policy per-request?, Spring Session: How to create separate session management policies for different URLs and Multiple HttpSecurity) but none of them worked.
Thanks in advance!
I was missing some details on my configuration. I was catching all the requests to /testMVCController/** and that was working, but in addition to catch the requests to any endpoint of the type /testMVCController/** (e.g.: /testMVCController/usersList), I also have to catch the requests that these pages make to get their scripts (.js files, .css files, .png files).
What was happening was: the request to /testMVCController/usersList), was configured with SessionCreationPolicy.ALWAYS, but the subsequent requests such as usersList.js, usersList.css, etc were configured with SessionCreationPolicy.STATELESS, and in these cases the X509CustomUserDetailsService was always consulted.
Example:
GET request to /testMVCController/usersList works, but there also requests in this usersList page to usersList.js, usersList.css, etc.
So, once I included these resource paths in the antMatchers all worked perfectly.
I have two different users in my application. Ldap users and api users. Ldap users have privilege to access an endpoint and api users a different endpoint. I have implemented the api user authentication using UserDetailsService and having the details in my application.yaml file.
The issue I am facing now is, The endpoint that only Ldap users should access is now being accessed my api users as well. How can I prevent this. Please find my code snippet below
public class ServiceSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("ldapProvider")
private AuthenticationProvider authenticationProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
// security for apiuser
http
.authorizeRequests()
.antMatchers(“/abcd/**).hasRole(“admin”)
.and()
.httpBasic().and().userDetailsService(userDetailsService());
// security for ldap users
http
.csrf().disable()
.authorizeRequests()
.antMatchers(“/ghhgh” + "/**").fullyAuthenticated()
.antMatchers("/login*").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().and()
.authenticationProvider(authenticationProvider)
.exceptionHandling();
}
public UserDetailsService userDetailsService() {
UserDetails user = User.withUsername(“api”)
.password(passwordEncoder().encode(“test”))
.roles(“admin”)
return new InMemoryUserDetailsManager(user);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
In spring security it is indeed possible to register multiple authentication mechanisms.
BUT you cannot register a specific authentication provider to a specific route.
The spring securty docs say:
ProviderManager is the most commonly used implementation of AuthenticationManager. ProviderManager delegates to a List of AuthenticationProviders. Each AuthenticationProvider has an opportunity to indicate that authentication should be successful, fail, or indicate it cannot make a decision and allow a downstream AuthenticationProvider to decide.
So in every request, the registered AuthenticationProviders are checked one after the other until one is successful, or all fail.
To solve your problem, you need to define multiple custom authorities, that you assign your users.
Then you secure your endpoints using these authorities.
E.g. you give every ldap user the authority LDAP_USER and every api user the authority API_USER. Then you configure your security accordingly:
Register all AuthenticationProviders:
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(ldapProvider);
auth.userDetailsService(userDetailsService());
}
And configure the endpoints:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
(...)
.authorizeRequests()
// security for apiuser
.antMatchers(“/abcd/**).hasRole(“API_USER”)
// security for ldap users
.antMatchers(“/ghhgh” + "/**").hasRole("LDAP_USER")
(...)
}
I am making an API with Spring boot + Spring Security. I am having issues with Spring Security "letting" any request go through.
What is happening is that I have two endpoints:
users/register/app
users/recoverpasswordbyemail
And I am testing them with Postman, the thing is that if I call one of those endpoints for the first time after the app started without an Authoritation header or a wrong one, it won't let me in and gives a 401 error. However, If I call the other method or the same method again with a correct Authoritation header and then call one of them without the Header again, it'll let me pass. I do not think it's suppossed to work without an auth header.
I am not sure if it is because I have already done some kind of "log in" when I put the correct auth header or if it's a Postman's issue. But I want that every time a call to one of those endpoints a check is done for the user.
This is my Spring config file:
#Configuration #EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserServiceImpl userService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/users/register/app", "/users/recoverpasswordbyemail")
.hasAnyRole("ADMIN", "SADMIN")
.and().httpBasic().authenticationEntryPoint(new NotAuthorizedExceptionMapper())
.and().sessionManagement().disable();
}
#Override
public void configure(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(userService);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Also every time this happens, the user doesn't get first if exists (however it does when I have a wrong auth header) or whatever as I have in my UsersService:
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("Hi");
User user = userRepository.findById(username).get();
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), user.getRole());
}
This is the URL I use to call the endpoints:
http://localhost:8680/ulises/usersserver/users/register/app
where /ulises/usersserver/ is the servlet context
Has anyone a clue of why this can be? I researched quite a lot but saw nothing that could solve it.
Thank.
Okay, it turns out that a session was being created despite of sessions being disabled. I don't understand why this is happening but using
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
solved my problem.
I'm developing a REST API based on Spring Boot (spring-boot-starter-web) where I use Spring Security (spring-security-core e spring-security-config) to protect the different endpoints.
The authentication is done by using a local database that contains users with two different sets of roles: ADMIN andUSER. USER should be able toGET all API endpoints and POST to endpoints based onrouteA. ADMIN should be able to do the same asUSER plus POST andDELETE to endpoints based on `routeB
However the behavior I'm getting is that I can do GET requests to any endpoint but POST requests always return HTTP 403 Forbidden for either type of user - ADMIN and USER - which is not expected what I'm expecting based on my SecurityConfiguration.
Any ideas of what am I missing?
SecurityConfiguration.java
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);
#Autowired
private RESTAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private DataSource dataSource;
#Override
public void configure(AuthenticationManagerBuilder builder) throws Exception {
logger.info("Using database as the authentication provider.");
builder.jdbcAuthentication().dataSource(dataSource).passwordEncoder(new BCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().
authorizeRequests().antMatchers(HttpMethod.GET, "/**").hasAnyRole("ADMIN", "USER")
.antMatchers(HttpMethod.POST, "/routeA/*").hasAnyRole("ADMIN", "USER")
.antMatchers(HttpMethod.POST, "/routeB/*").hasRole("ADMIN")
.antMatchers(HttpMethod.DELETE, "/routeB/*").hasRole("ADMIN").and().
requestCache().requestCache(new NullRequestCache()).and().
httpBasic().authenticationEntryPoint(authenticationEntryPoint).and().
cors();
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
RouteBController .java
#RestController
public class RouteBController {
static final Logger logger = LoggerFactory.getLogger(RouteBController.class);
public RouteBController() { }
#RequestMapping(value = "routeB", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.GET)
public String getStuff() {
return "Got a hello world!";
}
#RequestMapping(value = "routeB", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.POST)
public String postStuff() {
return "Posted a hello world!";
}
}
RESTAuthenticationEntryPoint.java
#Component
public class RESTAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
#Override
public void afterPropertiesSet() throws Exception {
setRealmName("AppNameHere");
super.afterPropertiesSet();
}
}
BEFORE disabling the CSFR as a way of fixing this issue, please check the resources on Mohd Waseem's answer to better understand why it is important and to have an idea of how it can be properly set up. As RCaetano has said, CSFR is here to help us from attacks and it should not be disabled blindly.
Since this answer still explained the 2 issues on my original questions, I'll leave it as the marked answer to create awareness about possible issues with the CSFT and security routes but don't take it literally.
There were 2 issues in SecurityConfiguration.java that made it misbehave.
Although the 403 Forbidden error message didn't contain any message indication of why it was failing (see example below) it turns out it was due to having CSRF enabled. Disabling it allowed for POST and DELETE requests to be processed.
{
"timestamp": "2018-06-26T09:17:19.672+0000",
"status": 403,
"error": "Forbidden",
"message": "Forbidden",
"path": "/routeB"
}
Also the expression used in antMatched(HttpMethod, String) for RouteB was incorrect because /routeB/* expects it to have something after /. The correct configurtion is /routeB/** since more paths can be present (or not).
The corrected SecurityConfiguration.java is
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().
authorizeRequests().antMatchers(HttpMethod.GET, "/**").hasAnyRole("ADMIN", "USER")
.antMatchers(HttpMethod.POST, "/routeA/**").hasAnyRole("ADMIN", "USER")
.antMatchers(HttpMethod.POST, "/routeB/**").hasRole("ADMIN")
.antMatchers(HttpMethod.DELETE, "/routeB/**").hasRole("ADMIN").and().
requestCache().requestCache(new NullRequestCache()).and().
httpBasic().authenticationEntryPoint(authenticationEntryPoint).and().
cors().and().
csrf().disable();
}
Source: StackOverflow em Português
Cross-site request forgery is a web security vulnerability that allows an attacker to induce users to perform actions that they do not
intend to perform.
In your case disabling CSRF protection exposes user to this vulnerability.
Note: If it was pure Rest API with O-Auth protection then CSRF was not
needed. Should I use CSRF protection on Rest API endpoints?
But In your case when user logs in a session is created and cookie is returned in response and without CSRF token Attacker can exploit it and perform CSRF.
It wouldn't be a good idea to disable CSRF instead you can configure your app to return CSRF token in response headers and then use it in all your subsequent state changing calls.
Add this line of code in your SecurityConfiguration.java
// CSRF tokens handling
http.addFilterAfter(new CsrfTokenResponseHeaderBindingFilter(), CsrfFilter.class);
CsrfTokenResponseHeaderBindingFilter.java
public class CsrfTokenResponseHeaderBindingFilter extends OncePerRequestFilter {
protected static final String REQUEST_ATTRIBUTE_NAME = "_csrf";
protected static final String RESPONSE_HEADER_NAME = "X-CSRF-HEADER";
protected static final String RESPONSE_PARAM_NAME = "X-CSRF-PARAM";
protected static final String RESPONSE_TOKEN_NAME = "X-CSRF-TOKEN";
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, javax.servlet.FilterChain filterChain) throws ServletException, IOException {
CsrfToken token = (CsrfToken) request.getAttribute(REQUEST_ATTRIBUTE_NAME);
if (token != null) {
response.setHeader(RESPONSE_HEADER_NAME, token.getHeaderName());
response.setHeader(RESPONSE_PARAM_NAME, token.getParameterName());
response.setHeader(RESPONSE_TOKEN_NAME, token.getToken());
}
filterChain.doFilter(request, response);
}
}
Header Response form Server:
Note that we now have CSRF token in the header. This will not change untill the session expires.
Also read: Spring Security’s CSRF protection for REST services: the client side and the server side for better understanding.
It's simple CSRF enabled issue that doesn't allow POST requests. I faced the same problem here's the solution: (Explained)
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.POST,"/form").hasRole("ADMIN") // Specific api method request based on role.
.antMatchers("/home","/basic").permitAll() // permited urls to guest users(without login).
.anyRequest().authenticated()
.and()
.formLogin() // not specified form page to use default login page of spring security
.permitAll()
.and()
.logout().deleteCookies("JSESSIONID") // delete memory of browser after logout
.and()
.rememberMe().key("uniqueAndSecret"); // remember me check box enabled.
http.csrf().disable(); // ADD THIS CODE TO DISABLE CSRF IN PROJECT.**
}
Above code:
http.csrf().disable();
will solve the problem.
I'm trying to secure my website using Spring Security following the guides on the web.
So on my server side I have the following classes.
My WebSecurityConfigurerAdapter:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements ApplicationContextAware {
#Override
protected void registerAuthentication(AuthenticationManagerBuilde rauthManagerBuilder) throws Exception {
authManagerBuilder.inMemoryAuthentication().withUser("user").password("password").roles("ADMIN");
}
}
My controller:
#Controller
//#RequestMapping("/course")
public class CourseController implements ApplicationContextAware {
#RequestMapping(value="/course", method = RequestMethod.GET, produces="application/json")
public #ResponseBody List<Course> get( // The criterion used to find.
#RequestParam(value = "what", required = true) String what,
#RequestParam(value = "value", required = true) String value) {
//.....
}
#RequestMapping(value = "/course", method = RequestMethod.POST, produces = "application/json")
public List<Course> upload(#RequestBody Course[] cs) {
}
}
What confused me very much is the server does not respond to the POST/DELETE method, while the GET method works fine. BTW, I'm using RestTemplate on the client side.
Exceptions are:
Exception in thread "main" org.springframework.web.client.HttpClientErrorException: 403 Forbidden
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
at org.springframework.web.client.RestTemplate.handleResponseError(RestTemplate.java:574)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:530)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:487)
at org.springframework.web.client.RestTemplate.delete(RestTemplate.java:385)
at hello.Application.createRestTemplate(Application.java:149)
at hello.Application.main(Application.java:99)
I've searched the internet for days. Still don't have a clue. Please help. Thanks so much
The issue is likely due to CSRF protection. If users will not be using your application in a web browser, then it is safe to disable CSRF protection. Otherwise you should ensure to include the CSRF token in the request.
To disable CSRF protection you can use the following:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig
extends WebSecurityConfigurerAdapter implements ApplicationContextAware {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.csrf().disable();
}
#Override
protected void registerAuthentication(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
authManagerBuilder
.inMemoryAuthentication()
.withUser("user").password("password").roles("ADMIN");
}
}
The issue is likely due to CSRF protection, agree with the top comment. Nevertheless, by using this configuration, the method cancells the spring security.
So you can use the following code:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
auth
.inMemoryAuthentication()
.withUser("admin")
.password(encoder.encode("admin"))
.roles("ADMIN", "USER")
.and()
.withUser("user")
.password(encoder.encode("password"))
.roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic();
http.csrf().disable();
}
}
The issue may be related to CSRF or CORS Security Protection.
FOR CSRF: You can disable it if the application users did not use it from browsers.
For CORS: You can specify the origin and allow HTTP Methods.
The below code disable CSRF and allow all origins and HTTP methods. so be aware when using it.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("*");
}
}
I've been looking for days too! Simply disabling CSRF on your configure method with http.csrf().disable(); is all that needed to be done for my put requests to stop receiving 403.
Check your token which you are sending through 'Header' and also query in your database for the same token whether that token exist or not.
Note : The above is applicable only in case you are using Spring Boot token authentication mechanism.
I'm posting this in case someone else finds it useful in the future. I spent hours looking for what was failing and finally I find the solution, on Postman making a POST to http://localhost:8080/login/ is not the same as making a POST to http://localhost:8080/login (with "/" at the end of request it will return 403 forbidden)
I had this problem after upgrading my service to Spring Boot 3. Automatic tests started to fail with a 403 status. After quite a lot of headaches, I found out it is caused by removing trailing slash from URL matching.
The change is described here. So check that you are calling the correct URL.
Wrong:
/api/foo/
Right:
/api/foo
http.httpBasic().disable();
http.authorizeRequests().antMatchers("/signup").permitAll().antMatchers("/*")
.fullyAuthenticated().and().formLogin()
.and().csrf().disable();
http.csrf().disable();