Authentication and authorization are critical components of securing Java applications. Spring Security is a powerful and customizable framework that provides comprehensive support for both authentication (verifying who a user is) and authorization (determining what a user is allowed to do). Below is a detailed guide to implementing authentication and authorization using Spring Security.
1. Key Concepts
Authentication
- Verifies the identity of a user (e.g., username and password, OAuth2 token).
- Common methods:
- Form-based authentication
- Basic authentication
- OAuth2
- JWT (JSON Web Tokens)
Authorization
- Determines what an authenticated user is allowed to do.
- Typically implemented using roles or permissions.
2. Setting Up Spring Security
Add Spring Security Dependency
Include Spring Security in your pom.xml
(for Maven) or build.gradle
(for Gradle).
Maven:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Gradle:
implementation 'org.springframework.boot:spring-boot-starter-security'
3. Basic Authentication
Default Behavior
- By default, Spring Security enables basic authentication.
- A default user
user
is created with a randomly generated password (logged at startup).
Customize Authentication
Override the default behavior by extending WebSecurityConfigurerAdapter
(deprecated in Spring Security 6) or using the new SecurityFilterChain
approach.
Example: Custom User Details
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public").permitAll()
.anyRequest().authenticated()
)
.httpBasic(); // Enable basic authentication
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
4. Form-Based Authentication
Enable Form Login
Spring Security provides a default login form. Customize it as needed.
Example:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login") // Custom login page
.permitAll()
)
.logout(logout -> logout
.permitAll()
);
return http.build();
}
Custom Login Page
Create a custom login page (e.g., login.html
) and configure it in the SecurityFilterChain
.
5. Role-Based Authorization
Define Roles
Assign roles to users and restrict access based on roles.
Example:
@Bean
public UserDetailsService userDetailsService() {
UserDetails admin = User.withDefaultPasswordEncoder()
.username("admin")
.password("admin123")
.roles("ADMIN")
.build();
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(admin, user);
}
Restrict Access by Role
Use hasRole()
or hasAuthority()
to restrict access.
Example:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin").hasRole("ADMIN")
.requestMatchers("/user").hasRole("USER")
.anyRequest().authenticated()
)
.formLogin();
return http.build();
}
6. JWT (JSON Web Tokens) Authentication
What is JWT?
- A compact, URL-safe token format for securely transmitting information between parties.
- Commonly used for stateless authentication.
Steps to Implement JWT:
- Add JWT dependencies (e.g.,
jjwt
). - Create a JWT utility class for token generation and validation.
- Implement a custom filter to validate JWT tokens.
- Configure Spring Security to use the JWT filter.
Example: JWT Utility Class
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import java.security.Key;
import java.util.Date;
public class JwtUtil {
private static final Key SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256);
private static final long EXPIRATION_TIME = 864_000_000; // 10 days
public static String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SECRET_KEY)
.compact();
}
public static String extractUsername(String token) {
return Jwts.parserBuilder()
.setSigningKey(SECRET_KEY)
.build()
.parseClaimsJws(token)
.getBody()
.getSubject();
}
}
Example: JWT Filter
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
public class JwtFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String token = extractToken(request);
if (token != null && JwtUtil.validateToken(token)) {
String username = JwtUtil.extractUsername(token);
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(username, null, null);
SecurityContextHolder.getContext().setAuthentication(auth);
}
filterChain.doFilter(request, response);
}
private String extractToken(HttpServletRequest request) {
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
return header.substring(7);
}
return null;
}
}
Configure JWT Filter in Spring Security:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.addFilterBefore(new JwtFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
)
.csrf().disable();
return http.build();
}
7. OAuth2 Authentication
What is OAuth2?
- A protocol for authorization, commonly used for third-party authentication (e.g., Google, Facebook).
Steps to Implement OAuth2:
- Register your application with an OAuth2 provider (e.g., Google).
- Configure Spring Security to use OAuth2.
Example: OAuth2 with Google
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
)
.oauth2Login(); // Enable OAuth2 login
return http.build();
}
8. Best Practices
- Use HTTPS: Always secure your application with HTTPS.
- Store Secrets Securely: Use environment variables or a secrets manager for sensitive data.
- Validate Input: Sanitize and validate all user inputs to prevent injection attacks.
- Use Strong Password Hashing: Use algorithms like BCrypt or Argon2 for password storage.
- Enable CSRF Protection: Protect against Cross-Site Request Forgery (CSRF) attacks.