Reputation: 1
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;
}
}
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
Reputation: 56
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());
}
}
@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