Spring Security is a robust and customizable framework used to secure Java applications. It provides authentication, authorization, and other security-related functionalities, making it easier to secure web applications, RESTful services, and microservices.
Authentication is about verifying who the user is. Spring Security allows you to authenticate users through various means, such as:
It supports multiple methods and providers, such as in-memory, database, LDAP, and custom implementations.
Authorization controls what actions a user can perform. After a user is authenticated, Spring Security checks if they have the right permissions or roles to access specific resources.
/admin
).@PreAuthorize
or @Secured
to restrict access to specific service methods based on roles or permissions.When a user submits login credentials, these details are wrapped in an Authentication object, which holds:
Prvider Manager: Provider Manager will identify the appropiate authenticate provider to perform this authentication mechanism.
UserDetailsService: UserDetailsService will go to external DB or cache and it will load the UserDetails object and return to AuthenticationProvider.
Spring Web
, Spring Security
, Spring Data JPA
, MySQL Driver
Add the following configuration to connect to the MySQL database:
spring.datasource.url=jdbc:mysql://localhost:3306/your_database_name
spring.datasource.username=your_db_username
spring.datasource.password=your_db_password
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
Create an entity class that represents the table in the MySQL database:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
private String role;
// getters and setters
}
Create a repository interface for CRUD operations:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
}
To secure the API, use Spring Security to configure basic authentication. This example uses in-memory authentication with passwords stored in the database.
Configure a password encoder bean:
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
Create a SecurityConfig
class to define HTTP security rules:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.antMatchers("/api/private/**").authenticated()
.and()
.httpBasic();
}
}
Define CustomUserDetails
class that implements UserDetails
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Collections;
public class CustomUserDetails implements UserDetails {
private final User user;
public CustomUserDetails(User user) {
this.user = user;
}
@Override
public Collection extends GrantedAuthority> getAuthorities() {
return Collections.singletonList(new SimpleGrantedAuthority(user.getRole()));
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public User getUser() {
return user;
}
}
Implement UserDetailsService
to load user details from the database:
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
return new CustomUserDetails(user);
}
}
Create a REST controller to handle HTTP requests. Here’s an example of a basic API:
@RestController
@RequestMapping("/api")
public class UserController {
@Autowired
private UserRepository userRepository;
@GetMapping("/public/hello")
public String publicHello() {
return "Hello, this is a public endpoint!";
}
@GetMapping("/private/hello")
public String privateHello() {
return "Hello, this is a private endpoint!";
}
@GetMapping("/users")
public List<User> getAllUsers() {
return userRepository.findAll();
}
@PostMapping("/users")
public User createUser(@RequestBody User user) {
user.setPassword(new BCryptPasswordEncoder().encode(user.getPassword()));
return userRepository.save(user);
}
}
mvn spring-boot:run
.GET /api/public/hello
should be accessible without authentication.GET /api/private/hello
and GET /api/users
should require authentication.This setup secures the API using HTTP Basic authentication. You can extend it by using JWT (JSON Web Token) authentication for a more secure and stateless approach.