Spring Security
Comprehensive authentication and authorization framework
🔐 What is Spring Security?
Spring Security provides comprehensive security services for Java applications. It handles authentication, authorization, protection against attacks, and integrates seamlessly with Spring applications for robust security implementation.
// Enable security with simple annotation
@EnableWebSecurity
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults())
.build();
}
}
Security Features
Authentication
Verify user identity with multiple methods
@EnableWebSecurity
public class SecurityConfig
Authorization
Control access to resources and methods
@PreAuthorize("hasRole('ADMIN')")
public void adminMethod()
CSRF Protection
Prevent Cross-Site Request Forgery attacks
.csrf(csrf -> csrf.disable())
Session Management
Control user sessions and concurrent access
.sessionManagement(session ->
session.maximumSessions(1))
🔹 Basic Security Configuration
Configure Spring Security for your application:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/", "/home", "/register").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/dashboard")
.permitAll()
)
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/")
.permitAll()
)
.build();
}
}
Security Rules:
Public: /, /home, /register
Admin only: /admin/**
User/Admin: /user/**
All others: Authenticated users
🔹 User Details Service
Implement custom user authentication:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
private String username;
private String password;
private String email;
@Enumerated(EnumType.STRING)
private Role role;
private boolean enabled = true;
// Constructors, getters, setters
}
public enum Role {
USER, ADMIN, MODERATOR
}
@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: " + username));
return org.springframework.security.core.userdetails.User.builder()
.username(user.getUsername())
.password(user.getPassword())
.authorities("ROLE_" + user.getRole().name())
.accountExpired(false)
.accountLocked(false)
.credentialsExpired(false)
.disabled(!user.isEnabled())
.build();
}
}
🔹 Method-Level Security
Secure individual methods with annotations:
@Service
public class UserService {
// Only users with ADMIN role can access
@PreAuthorize("hasRole('ADMIN')")
public List<User> getAllUsers() {
return userRepository.findAll();
}
// Users can only access their own data
@PreAuthorize("hasRole('ADMIN') or #username == authentication.name")
public User getUserByUsername(String username) {
return userRepository.findByUsername(username);
}
// Check after method execution
@PostAuthorize("returnObject.username == authentication.name or hasRole('ADMIN')")
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
// Secure with custom expression
@PreAuthorize("@userService.canAccessUser(authentication.name, #userId)")
public void updateUser(Long userId, User user) {
// Update user logic
}
public boolean canAccessUser(String currentUsername, Long userId) {
User currentUser = userRepository.findByUsername(currentUsername);
return currentUser.getId().equals(userId) ||
currentUser.getRole() == Role.ADMIN;
}
}
🔹 JWT Authentication
Implement JWT-based authentication for APIs:
@Component
public class JwtUtil {
private String secret = "mySecretKey";
private int jwtExpiration = 86400; // 24 hours
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return createToken(claims, userDetails.getUsername());
}
private String createToken(Map<String, Object> claims, String subject) {
return Jwts.builder()
.setClaims(claims)
.setSubject(subject)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + jwtExpiration * 1000))
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
}
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtUtil jwtUtil;
@Autowired
private CustomUserDetailsService userDetailsService;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getUsername(),
loginRequest.getPassword())
);
} catch (BadCredentialsException e) {
throw new BadCredentialsException("Invalid credentials", e);
}
final UserDetails userDetails = userDetailsService
.loadUserByUsername(loginRequest.getUsername());
final String jwt = jwtUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtResponse(jwt));
}
}
🔹 Security in Controllers
Access security information in your controllers:
@RestController
@RequestMapping("/api/secure")
public class SecureController {
// Get current authenticated user
@GetMapping("/profile")
public ResponseEntity<User> getCurrentUser(Authentication authentication) {
String username = authentication.getName();
User user = userService.findByUsername(username);
return ResponseEntity.ok(user);
}
// Using SecurityContextHolder
@GetMapping("/info")
public ResponseEntity<String> getSecurityInfo() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth.getName();
Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
return ResponseEntity.ok("User: " + username + ", Roles: " + authorities);
}
// Using @AuthenticationPrincipal
@GetMapping("/dashboard")
public ResponseEntity<String> getDashboard(
@AuthenticationPrincipal UserDetails userDetails) {
return ResponseEntity.ok("Welcome " + userDetails.getUsername());
}
}
Response Example:
GET /api/secure/info
"User: john_doe, Roles: [ROLE_USER]"