401 Error in Postman: Issue with Spring Backend

I am trying to build a backend using Spring. When I register a user, the API works fine, and the record is successfully created in the database. However, when I try to make a GET request, I receive a 401 Unauthorized error.

I have enabled basic authentication in Postman, where I input the credentials of an existing user, but the 401 error persists. Can someone please help me understand what might be going wrong?

Below is the relevant code:

0.SecurityConfig

package com.example.new_messager.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {

        return new BCryptPasswordEncoder();

    }
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        http

            .csrf().disable() // Disabled CSRF for testing (configure it properly in production)

            .authorizeHttpRequests(authorize -> authorize
            .requestMatchers("/api/users/register").permitAll() // Allow registration without authentication

                .anyRequest().authenticated() // Require authentication for other endpoints

            )

            .httpBasic(); // Use basic authentication
        return http.build();

    }
}

1.UserController

package com.example.new_messager.controller;

import com.example.new_messager.model.User;
import com.example.new_messager.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/users")
public class UserController {
    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping("/register")
    public void registerUser(@RequestBody User user) {
        userService.register(user);
    }

    @GetMapping("/{username}")
    public User findByUsername(@PathVariable String username) {
        return userService.findByUsername(username);
    }
}

2.User

package com.example.new_messager.model;

import jakarta.persistence.*;

@Entity
@Table(name = "app_user")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false)
    private String username;

    @Column(nullable = false)
    private String password;

    public void setPassword(String encode) {
        this.password = encode;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }
}
  1. UserService
package com.example.new_messager.service;

import com.example.new_messager.model.User;
import com.example.new_messager.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import org.springframework.stereotype.Service;

@Service
public class UserService {

    private final UserRepository userRepository;

    private final BCryptPasswordEncoder passwordEncoder;

    @Autowired
    public UserService(UserRepository userRepository, BCryptPasswordEncoder passwordEncoder) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
    }

    public void register(User user) {

        if (user.getPassword() == null || user.getPassword().isEmpty()) {
            throw new IllegalArgumentException("Password cannot be null or empty");
        }

        if (user.getUsername() == null || user.getPassword() == null) {
            throw new IllegalArgumentException("Username and password must not be null");
        }

       String encodedPassword = passwordEncoder.encode(user.getPassword());
       user.setPassword(encodedPassword);
       userRepository.save(user);
    }

    public boolean validatePassword(String rawPassword, String encodedPassword) {
        return passwordEncoder.matches(rawPassword, encodedPassword);
    }

    public User findByUsername(String username) {

        return userRepository.findByUsername(username)
                .orElseThrow(() -> new RuntimeException("User not found"));
    }
}
package com.example.new_messager.repository;

import com.example.new_messager.model.User;

import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {

    Optional<User> findByUsername(String username);

}

I tried it without encoding the password, but the problem remains. If you allow unauthorized users to make requests, the request passes.

Upvotes: 0

Views: 61

Answers (1)

sab1tm
sab1tm

Reputation: 56

  1. implement a Custom UserDetailsService

Update your SecurityConfig to use a custom UserDetailsService:

@Configuration
public class SecurityConfig {

    private final UserDetailsService userDetailsService;

    public SecurityConfig(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

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

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeHttpRequests(authorize -> authorize
                .requestMatchers("/api/users/register").permitAll()
                .anyRequest().authenticated())
            .httpBasic();

        return http.build();
    }

    @Autowired
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
}
  1. add a UserDetailsService Implementation
@Service
public class CustomUserDetailsService implements UserDetailsService {

    private final UserRepository userRepository;

    public CustomUserDetailsService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username)
            .orElseThrow(() -> new UsernameNotFoundException("User not found"));

        return User.builder() // org.springframework.security.core.userdetails.User
            .username(user.getUsername())
            .password(user.getPassword())
            .roles("USER")
            .build();
    }
}

Upvotes: 0

Related Questions