En aquest tema, aprendrem a implementar l'autenticació basada en JSON Web Tokens (JWT) en una aplicació Spring Boot. Els JWT són una manera segura i estàndard de representar reclamacions entre dues parts. Són àmpliament utilitzats per a l'autenticació i l'autorització en aplicacions web.

Objectius

  • Entendre què és un JWT i com funciona.
  • Configurar Spring Security per utilitzar JWT.
  • Crear un servei per generar i validar JWT.
  • Implementar filtres de seguretat per gestionar les sol·licituds autenticades.

Què és un JWT?

Un JSON Web Token (JWT) és un estàndard obert (RFC 7519) que defineix una manera compacta i autònoma de transmetre informació de manera segura entre les parts com un objecte JSON. Aquesta informació pot ser verificada i confiada perquè està signada digitalment.

Estructura d'un JWT

Un JWT consta de tres parts separades per punts (.):

  1. Header (Capçalera): Conté el tipus de token (JWT) i l'algoritme de signatura utilitzat (per exemple, HMAC SHA256).
  2. Payload (Càrrega útil): Conté les reclamacions. Les reclamacions són declaracions sobre una entitat (normalment, l'usuari) i dades addicionals.
  3. Signature (Signatura): La signatura és utilitzada per verificar que el remitente del JWT és qui diu ser i per assegurar que el missatge no ha estat alterat.

Exemple d'un JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMSIsImV4cCI6MTYwOTI3MjAwMH0.4fP5Zc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8FZc8F## Configuració de Spring Security per utilitzar JWT

### Dependències Maven

Primer, necessitem afegir les dependències necessàries al nostre projecte. Afegeix les següents dependències al teu fitxer `pom.xml`:
org.springframework.boot spring-boot-starter-security io.jsonwebtoken jjwt 0.9.1
### Configuració de seguretat

Crea una classe de configuració de seguretat que estengui `WebSecurityConfigurerAdapter` i sobreescriu els mètodes necessaris per configurar la seguretat de la teva aplicació.

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.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable()
        .authorizeRequests()
        .antMatchers("/auth/**").permitAll()
        .anyRequest().authenticated()
        .and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}

@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
    return new JwtAuthenticationFilter();
}

}

### Creació del servei JWT

Crea una classe `JwtTokenProvider` que s'encarregarà de generar i validar els tokens JWT.

import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component;

import java.util.Date;

@Component public class JwtTokenProvider {

@Value("${jwt.secret}")
private String jwtSecret;

@Value("${jwt.expiration}")
private long jwtExpiration;

public String generateToken(String username) {
    Date now = new Date();
    Date expiryDate = new Date(now.getTime() + jwtExpiration);

    return Jwts.builder()
            .setSubject(username)
            .setIssuedAt(now)
            .setExpiration(expiryDate)
            .signWith(SignatureAlgorithm.HS512, jwtSecret)
            .compact();
}

public String getUsernameFromJWT(String token) {
    Claims claims = Jwts.parser()
            .setSigningKey(jwtSecret)
            .parseClaimsJws(token)
            .getBody();

    return claims.getSubject();
}

public boolean validateToken(String authToken) {
    try {
        Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
        return true;
    } catch (Exception e) {
        return false;
    }
}

}

### Implementació del filtre JWT

Crea una classe `JwtAuthenticationFilter` que extengui `OncePerRequestFilter` per interceptar les sol·licituds HTTP i validar el token JWT.

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException;

@Component public class JwtAuthenticationFilter extends OncePerRequestFilter {

@Autowired
private JwtTokenProvider tokenProvider;

@Autowired
private UserDetailsService userDetailsService;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
    String jwt = getJwtFromRequest(request);

    if (jwt != null && tokenProvider.validateToken(jwt)) {
        String username = tokenProvider.getUsernameFromJWT(jwt);

        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        if (userDetails != null) {
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
    }

    filterChain.doFilter(request, response);
}

private String getJwtFromRequest(HttpServletRequest request) {
    String bearerToken = request.getHeader("Authorization");
    if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
        return bearerToken.substring(7);
    }
    return null;
}

}

### Controlador d'autenticació

Crea un controlador per gestionar les sol·licituds d'autenticació i generar el token JWT.

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.web.bind.annotation.*;

@RestController @RequestMapping("/auth") public class AuthController {

@Autowired
private AuthenticationManager authenticationManager;

@Autowired
private JwtTokenProvider tokenProvider;

@Autowired
private UserDetailsService userDetailsService;

@PostMapping("/login")
public String authenticateUser(@RequestBody LoginRequest loginRequest) {
    try {
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        loginRequest.getUsername(),
                        loginRequest.getPassword()
                )
        );

        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        return tokenProvider.generateToken(userDetails.getUsername());
    } catch (AuthenticationException e) {
        throw new RuntimeException("Invalid credentials");
    }
}

}

### Model de sol·licitud de login

Crea una classe `LoginRequest` per encapsular les dades de la sol·licitud de login.

public class LoginRequest { private String username; private String password;

// Getters and setters

}

## Resum

En aquesta secció, hem après a implementar l'autenticació basada en JWT en una aplicació Spring Boot. Hem configurat Spring Security per utilitzar JWT, creat un servei per generar i validar tokens, implementat filtres de seguretat per gestionar les sol·licituds autenticades i creat un controlador per gestionar les sol·licituds d'autenticació. Amb aquests coneixements, estàs preparat per protegir la teva aplicació Spring Boot amb autenticació JWT.

Curs de Spring Boot

Mòdul 1: Introducció a Spring Boot

Mòdul 2: Conceptes bàsics de Spring Boot

Mòdul 3: Construint serveis web RESTful

Mòdul 4: Accés a dades amb Spring Boot

Mòdul 5: Seguretat a Spring Boot

Mòdul 6: Proves a Spring Boot

Mòdul 7: Funcions avançades de Spring Boot

Mòdul 8: Desplegant aplicacions Spring Boot

Mòdul 9: Rendiment i monitorització

Mòdul 10: Millors pràctiques i consells

© Copyright 2024. Tots els drets reservats