#Spring returns 403 instead of 500 or 404

1 messages · Page 1 of 1 (latest)

hollow nacelle
#

I have Spring Boot RestAPI and I if I throw any Exception in the endpoint method Spring automatically returns 403 instead of 500. Also if a parameter is missing or the endpoint does not exist it returns 403 and not 400 or a 404

I think the problem is in my Spring Security Configuration. My SecurityConfigurer looks like this:

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfigurer {

    private final JwtRequestFilter jwtRequestFilter;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        final String BASE = APP.API_BASE_URL;

        http
            .csrf().disable()
            .authorizeHttpRequests()
                .requestMatchers(BASE + "/login").permitAll()
                .anyRequest().authenticated()
            .and().sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(15);
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }
}
quasi chasmBOT
#

<@&1004656351647117403> please have a look, thanks.

quasi chasmBOT
#

While you are waiting for getting help, here are some tips to improve your experience:

Code is much easier to read if posted with syntax highlighting and proper formatting.

If nobody is calling back, that usually means that your question was not well asked and hence nobody feels confident enough answering. Try to use your time to elaborate, provide details, context, more code, examples and maybe some screenshots. With enough info, someone knows the answer for sure.

Don't forget to close your thread using the command </help-thread close:1027500463647621170> when your question has been answered, thanks.

hollow nacelle
#

If I am not telling Spring Security to be authenticated at every Request it works.

The Spring Debug Output is the following

2023-05-10T19:34:13.126+02:00 DEBUG 31568 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Failed to complete request: java.lang.NullPointerException: Test
2023-05-10T19:34:13.127+02:00 ERROR 31568 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.NullPointerException: Test] with root cause
// Stack Trace
2023-05-10T19:34:13.135+02:00 DEBUG 31568 --- [nio-8080-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)

I cant paste in the stacktrace because it is too long for discord.

#

If you need to see some more code feel free to ask.

#

Endpoint

@DeleteMapping()
public ResponseEntity<?> removeRecord(@RequestHeader String Authorization, @RequestBody Record record) {
    throw new NullPointerException("Test");
}

Postman:

ancient jetty
#

403 usually thrown because authorization fail. You probably need to show the JwtRequestFilter part.

hollow nacelle
#
@Component
@Order(1)
@RequiredArgsConstructor
public class JwtRequestFilter extends OncePerRequestFilter {

    private final JwtUtil jwtUtil;
    private final UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) throws ServletException, IOException {

        final String authorizationHeader = request.getHeader("Authorization");

        String username = null;
        String jwt = null;

        if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
            filterChain.doFilter(request, response);
            return;
        }

        jwt = authorizationHeader.substring(7);
        username = jwtUtil.extractUsername(jwt);

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
            if (jwtUtil.isTokenValid(jwt, userDetails)) {
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            }
        }

        filterChain.doFilter(request, response);
    }
}
ancient jetty
#

What about the UserDetailsService? Did you implement one?

regal gate
#

giving 403 is the default behaviour for the spring security for any urls that are not present

#

and in the stack trace I can see your request is going to default Error controller which uses /error endpoint to show you the errors so you might have to add /error endpoint in permit all requestMatchers for it to work

#

note: exception handler does not work for errors occurred in filters. So any error which happens in your jwtfilter will not be handled by Exceptionhandler

hollow nacelle
#

UserDetailsService

@Service
public class AppUserDetailsService implements UserDetailsService {

    @PersistenceContext
    private EntityManager em;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        String sql = "SELECT * FROM users WHERE username = :username";

        UserDetails user = null;
        ArrayList<AppUser> users = null;

        try {
            users = (ArrayList<AppUser>) em.createNativeQuery(sql, AppUser.class).setParameter("username", username).getResultList();
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (users != null ) {
            for (AppUser user : users) {
                user = new User(user.getUsername(), user.getPassword(), getAuthorities(user));
            }
        } else {
            throw new UsernameNotFoundException("User not found");
        }

        return user;
    }

    private Collection<? extends GrantedAuthority> getAuthorities(AppUser user) {
        List<GrantedAuthority> authorities = new ArrayList<>();
        if (user.isOwner()) {
            authorities.add(new SimpleGrantedAuthority("OWNER"));
            authorities.add(new SimpleGrantedAuthority("MANAGER"));
            authorities.add(new SimpleGrantedAuthority("USER"));
        } else if (user.isManager()) {
            authorities.add(new SimpleGrantedAuthority("MANAGER"));
            authorities.add(new SimpleGrantedAuthority("USER"));
        } else if (!user.isDisabled()) {
            authorities.add(new SimpleGrantedAuthority("USER"));
        }

        return authorities;
    }
}
quasi chasmBOT
hollow nacelle
hollow nacelle
#

Did not think of this.

regal gate
# hollow nacelle Oh thank you. Is this new in Spring Security 6, because in the earlier versions ...

I am not sure when it is added to the spring security. but f you want to change the default bheaviour you can make your own authenticationEntryPoint
this question from stack overflow explains it in detail
https://stackoverflow.com/questions/70054528/receiving-403-instead-of-404-when-calling-non-existing-endpoint

hollow nacelle
#

After reading the question and the answers. I also think it is better to not tell non authenticated users if the endpoint exists or not. Thanks for the link and for you help