Java Authentication and Authorization (Spring Security)

Loading

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:

  1. Add JWT dependencies (e.g., jjwt).
  2. Create a JWT utility class for token generation and validation.
  3. Implement a custom filter to validate JWT tokens.
  4. 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:

  1. Register your application with an OAuth2 provider (e.g., Google).
  2. 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

  1. Use HTTPS: Always secure your application with HTTPS.
  2. Store Secrets Securely: Use environment variables or a secrets manager for sensitive data.
  3. Validate Input: Sanitize and validate all user inputs to prevent injection attacks.
  4. Use Strong Password Hashing: Use algorithms like BCrypt or Argon2 for password storage.
  5. Enable CSRF Protection: Protect against Cross-Site Request Forgery (CSRF) attacks.

Leave a Reply

Your email address will not be published. Required fields are marked *