How do I stop the SecurityFilterChain in Spring Boot 3?

48 views Asked by At

I am using Spring Boot 3.1.4

Requirements:

  • Requests to auth/registration do not need any authorization or roles.
  • Requests anywhere else need basic auth.
@Configuration
public class AuthConfig {

    @Autowired
    private BasicAuthFilter basicAuthFilter;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity
                .authorizeHttpRequests(authorizationManagerRequestMatcherRegistry -> {
                    authorizationManagerRequestMatcherRegistry.requestMatchers("/auth/registration").permitAll();
                    authorizationManagerRequestMatcherRegistry.anyRequest().authenticated();
                })
                .csrf(AbstractHttpConfigurer::disable)
                .addFilterBefore(basicAuthFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }
}
@Component
public class BasicAuthFilter extends OncePerRequestFilter {

    @Autowired
    private PersonService personService;
    private final BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String requestURI = request.getRequestURI();

        if (requestURI.equals("/auth/registration")) {
            // TODO: remove
            Collection<GrantedAuthority> grantedAuthorities = new ArrayList<>();
            GrantedAuthority grantedAuthority = new GrantedAuthority() {
                @Override
                public String getAuthority() {
                    return "admin";
                }
            };
            grantedAuthorities.add(grantedAuthority);

            Authentication authentication = new UsernamePasswordAuthenticationToken(new Person(), new Person(), grantedAuthorities);
            SecurityContextHolder.getContext().setAuthentication(authentication);

            filterChain.doFilter(request, response);
            return;
        }

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

        if (authorizationHeader == null) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
            return;
        }

        if (!authorizationHeader.startsWith("Basic ")) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
            return;
        }

        String base64Credentials = authorizationHeader.substring("Basic ".length());

        String decodedCredentials = new String(Base64.getDecoder().decode(base64Credentials), StandardCharsets.UTF_8);
        String[] credentials = decodedCredentials.split(":");

        String username = credentials[0];
        String password = credentials[1];

        Person person = personService.getPerson(username);

        if (bCryptPasswordEncoder.matches(password, person.getPassword())) {
            Collection<GrantedAuthority> grantedAuthorities = new ArrayList<>();
            GrantedAuthority grantedAuthority = new GrantedAuthority() {
                @Override
                public String getAuthority() {
                    return "admin";
                }
            };
            grantedAuthorities.add(grantedAuthority);

            Authentication authentication = new UsernamePasswordAuthenticationToken(person, person.getPassword(), grantedAuthorities);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            filterChain.doFilter(request, response);
        } else {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
        }
    }
}

When I make request to e.g. /persons without authorization I want to send an 401 back to the client. And that's actually what I do

        if (authorizationHeader == null) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
            return;
        }

Unfortunately the FilterChain doesn't stop there and continues to run for every other filter too. Ultimately the AuthorizationFilter fails too and sends an 403.

IMO when the auth is missing, I want to return 401 and not 403.

How do I return 401? What is the best practice?

0

There are 0 answers