Heim Java javaLernprogramm Frühlingssicherheit mit JWT

Frühlingssicherheit mit JWT

Aug 16, 2024 am 06:35 AM

Spring Security with JWT

In diesem Artikel erfahren Sie, wie Sie Spring Security mit JWT integrieren, um eine solide Sicherheitsschicht für Ihre Anwendung aufzubauen. Wir gehen jeden Schritt durch, von der Grundkonfiguration bis zur Implementierung eines benutzerdefinierten Authentifizierungsfilters, um sicherzustellen, dass Sie über die notwendigen Tools verfügen, um Ihre APIs effizient und skalierbar zu schützen.

Konfiguration

Beim Spring Initializr werden wir ein Projekt mit Java 21, Maven, Jar und diesen Abhängigkeiten erstellen:

  • Spring Data JPA
  • Frühlingsnetz
  • Lombok
  • Frühlingssicherheit
  • PostgreSQL-Treiber
  • OAuth2-Ressourcenserver

Richten Sie die PostgreSQL-Datenbank ein

Mit Docker erstellen Sie eine PostgreSql-Datenbank mit Docker-Compose.
Erstellen Sie eine docker-compose.yaml-Datei im Stammverzeichnis Ihres Projekts.

services:
  postgre:
    image: postgres:latest
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_DB=database
      - POSTGRES_USER=admin
      - POSTGRES_PASSWORD=admin
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

Nach dem Login kopieren

Führen Sie den Befehl aus, um den Container zu starten.

docker compose up -d
Nach dem Login kopieren

Richten Sie die Datei „application.properties“ ein

Diese Datei ist die Konfiguration für die Spring Boot-Anwendung.

spring.datasource.url=jdbc:postgresql://localhost:5432/database
spring.datasource.username=admin
spring.datasource.password=admin

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

jwt.public.key=classpath:public.key
jwt.private.key=classpath:private.key
Nach dem Login kopieren

Jwt.public.key und jwt.private.key sind Schlüssel, die wir weiter erstellen werden.

Generieren Sie die privaten und öffentlichen Schlüssel

NIEMALSübertragen Sie diese Schlüssel an Ihren Github

Auf der Konsole ausführen, um den privaten Schlüssel im Ressourcenverzeichnis
zu generieren

cd src/main/resources
openssl genrsa > private.key
Nach dem Login kopieren

Erstellen Sie anschließend den öffentlichen Schlüssel, der mit dem privaten Schlüssel verknüpft ist.

openssl rsa -in private.key -pubout -out public.key 
Nach dem Login kopieren

Code

Erstellen Sie eine SecurityConfig-Datei

Erstellen Sie näher an der Hauptfunktion ein Verzeichnis „configs“ und darin eine Datei „SecurityConfig.java“.

import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.web.SecurityFilterChain;

import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {

    @Value("${jwt.public.key}")
    private RSAPublicKey publicKey;

    @Value("${jwt.private.key}")
    private RSAPrivateKey privateKey;

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf(csrf -> csrf.disable())
                .authorizeHttpRequests(auth -> auth.requestMatchers(HttpMethod.POST, "/signin").permitAll()
                        .requestMatchers(HttpMethod.POST, "/login").permitAll()
                        .anyRequest().authenticated())
                .oauth2ResourceServer(config -> config.jwt(jwt -> jwt.decoder(jwtDecoder())));

        return http.build();
    }

    @Bean
    BCryptPasswordEncoder bPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    JwtEncoder jwtEncoder() {
        var jwk = new RSAKey.Builder(this.publicKey).privateKey(this.privateKey).build();

        var jwks = new ImmutableJWKSet<>(new JWKSet(jwk));

        return new NimbusJwtEncoder(jwks);
    }

    @Bean
    JwtDecoder jwtDecoder() {
        return NimbusJwtDecoder.withPublicKey(publicKey).build();
    }
}
Nach dem Login kopieren

Erläuterung

  • @EnableWebScurity: Wenn Sie @EnableWebSecurity verwenden, wird automatisch die Spring Security-Konfiguration zum Sichern von Webanwendungen ausgelöst. Diese Konfiguration umfasst das Einrichten von Filtern, das Sichern von Endpunkten und das Anwenden verschiedener Sicherheitsregeln.

  • @EnableMethodSecurity: ist eine Annotation in Spring Security, die Sicherheit auf Methodenebene in Ihrer Spring-Anwendung ermöglicht. Es ermöglicht Ihnen, Sicherheitsregeln direkt auf Methodenebene anzuwenden, indem Sie Annotationen wie @PreAuthorize, @PostAuthorize, @Secured und @RolesAllowed verwenden.

  • privateKey und publicKey: sind die öffentlichen und privaten RSA-Schlüssel, die zum Signieren und Verifizieren von JWTs verwendet werden. Die @Value-Annotation fügt die Schlüssel aus der Eigenschaftendatei (application.properties) in diese Felder ein.

  • CSRF: Deaktiviert den CSRF-Schutz (Cross-Site Request Forgery), der häufig in zustandslosen REST-APIs deaktiviert ist, in denen JWT zur Authentifizierung verwendet wird.

  • authorizeHttpRequests: Konfiguriert URL-basierte Autorisierungsregeln.

    • requestMatchers(HttpMethod.POST, "/signin").permitAll(): Ermöglicht den nicht authentifizierten Zugriff auf die Endpunkte /signin und /login, was bedeutet, dass jeder auf diese Routen zugreifen kann, ohne angemeldet zu sein.
    • anyRequest().authenticated(): Erfordert Authentifizierung für alle anderen Anfragen.
  • oauth2ResourceServer: Konfiguriert die Anwendung als OAuth 2.0-Ressourcenserver, der JWT zur Authentifizierung verwendet.

    • config.jwt(jwt -> jwt.decoder(jwtDecoder())): Gibt die JWT-Decoder-Bean (jwtDecoder) an, die zum Dekodieren und Validieren der JWT-Tokens verwendet wird.
  • BCryptPasswordEncoder: Diese Bean definiert einen Passwort-Encoder, der den BCrypt-Hashing-Algorithmus zum Codieren von Passwörtern verwendet. BCrypt ist eine beliebte Wahl für die sichere Speicherung von Passwörtern, da es anpassungsfähig ist und dadurch resistent gegen Brute-Force-Angriffe ist.

  • JwtEncoder: Diese Bean ist für die Codierung (Signierung) von JWT-Tokens verantwortlich.

    • RSAKey.Builder: Erstellt einen neuen RSA-Schlüssel unter Verwendung der bereitgestellten öffentlichen und privaten RSA-Schlüssel.
    • ImmutableJWKSet<>(new JWKSet(jwk)): Verpackt den RSA-Schlüssel in einen JSON Web Key Set (JWKSet) und macht ihn so unveränderlich.
    • NimbusJwtEncoder(jwks): Verwendet die Nimbus-Bibliothek, um einen JWT-Encoder zu erstellen, der Token mit dem privaten RSA-Schlüssel signiert.
  • JwtDecoder: Diese Bean ist für die Dekodierung (Verifizierung) von JWT-Tokens verantwortlich.

    • NimbusJwtDecoder.withPublicKey(publicKey).build(): Erstellt einen JWT-Decoder mit dem öffentlichen RSA-Schlüssel, der zur Überprüfung der Signatur von JWT-Tokens verwendet wird.

Juristische Person

import org.springframework.security.crypto.password.PasswordEncoder;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Entity
@Table(name = "tb_clients")
@Getter
@Setter
@NoArgsConstructor
public class ClientEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    @Column(name = "client_id")
    private Long clientId;

    private String name;

    @Column(unique = true)
    private String cpf;

    @Column(unique = true)
    private String email;

    private String password;

    @Column(name = "user_type")
    private String userType = "client";

    public Boolean isLoginCorrect(String password, PasswordEncoder passwordEncoder) {
        return passwordEncoder.matches(password, this.password);
    }
}
Nach dem Login kopieren

Repository

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import example.com.challengePicPay.entities.ClientEntity;

@Repository
public interface ClientRepository extends JpaRepository<ClientEntity, Long> {
    Optional<ClientEntity> findByEmail(String email);

    Optional<ClientEntity> findByCpf(String cpf);

    Optional<ClientEntity> findByEmailOrCpf(String email, String cpf);
}
Nach dem Login kopieren

Dienstleistungen

Kundenservice

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;

import example.com.challengePicPay.entities.ClientEntity;
import example.com.challengePicPay.repositories.ClientRepository;

@Service
public class ClientService {

    @Autowired
    private ClientRepository clientRepository;

    @Autowired
    private BCryptPasswordEncoder bPasswordEncoder;

    public ClientEntity createClient(String name, String cpf, String email, String password) {

        var clientExists = this.clientRepository.findByEmailOrCpf(email, cpf);

        if (clientExists.isPresent()) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Email/Cpf already exists.");
        }

        var newClient = new ClientEntity();

        newClient.setName(name);
        newClient.setCpf(cpf);
        newClient.setEmail(email);
        newClient.setPassword(bPasswordEncoder.encode(password));

        return clientRepository.save(newClient);
    }
}
Nach dem Login kopieren

Token-Service

import java.time.Instant;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;

import example.com.challengePicPay.repositories.ClientRepository;

@Service
public class TokenService {

    @Autowired
    private ClientRepository clientRepository;

    @Autowired
    private JwtEncoder jwtEncoder;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    public String login(String email, String password) {

        var client = this.clientRepository.findByEmail(email)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "Email not found"));

        var isCorrect = client.isLoginCorrect(password, bCryptPasswordEncoder);

        if (!isCorrect) {
            throw new BadCredentialsException("Email/password invalid");
        }

        var now = Instant.now();
        var expiresIn = 300L;

        var claims = JwtClaimsSet.builder()
                .issuer("pic_pay_backend")
                .subject(client.getEmail())
                .issuedAt(now)
                .expiresAt(now.plusSeconds(expiresIn))
                .claim("scope", client.getUserType())
                .build();

        var jwtValue = jwtEncoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();

        return jwtValue;

    }
}
Nach dem Login kopieren

Controller

Client-Controller

package example.com.challengePicPay.controllers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import example.com.challengePicPay.controllers.dto.NewClientDTO;
import example.com.challengePicPay.entities.ClientEntity;
import example.com.challengePicPay.services.ClientService;

@RestController
public class ClientController {

    @Autowired
    private ClientService clientService;

    @PostMapping("/signin")
    public ResponseEntity<ClientEntity> createNewClient(@RequestBody NewClientDTO client) {
        var newClient = this.clientService.createClient(client.name(), client.cpf(), client.email(), client.password());

        return ResponseEntity.status(HttpStatus.CREATED).body(newClient);
    }

    @GetMapping("/protectedRoute")
    @PreAuthorize("hasAuthority('SCOPE_client')")
    public ResponseEntity<String> protectedRoute(JwtAuthenticationToken token) {
        return ResponseEntity.ok("Authorized");
    }

}
Nach dem Login kopieren

Explanation

  • The /protectedRoute is a private route that can only be accessed with a JWT after logging in.

  • The token must be included in the headers as a Bearer token, for example.

  • You can use the token information later in your application, such as in the service layer.

  • @PreAuthorize: The @PreAuthorize annotation in Spring Security is used to perform authorization checks before a method is invoked. This annotation is typically applied at the method level in a Spring component (like a controller or a service) to restrict access based on the user's roles, permissions, or other security-related conditions.
    The annotation is used to define the condition that must be met for the method to be executed. If the condition evaluates to true, the method proceeds. If it evaluates to false, access is denied,

  • "hasAuthority('SCOPE_client')": It checks if the currently authenticated user or client has the specific authority SCOPE_client. If they do, the method protectedRoute() is executed. If they don't, access is denied.


Token Controller: Here, you can log in to the application, and if successful, it will return a token.

package example.com.challengePicPay.controllers;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;

import example.com.challengePicPay.controllers.dto.LoginDTO;
import example.com.challengePicPay.services.TokenService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

@RestController
public class TokenController {

    @Autowired
    private TokenService tokenService;

    @PostMapping("/login")
    public ResponseEntity<Map<String, String>> login(@RequestBody LoginDTO loginDTO) {
        var token = this.tokenService.login(loginDTO.email(), loginDTO.password());

        return ResponseEntity.ok(Map.of("token", token));
    }

}
Nach dem Login kopieren

Reference

  • Spring Security
  • Spring Security-Toptal article

Das obige ist der detaillierte Inhalt vonFrühlingssicherheit mit JWT. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

Video Face Swap

Video Face Swap

Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heißer Artikel

<🎜>: Bubble Gum Simulator Infinity - So erhalten und verwenden Sie Royal Keys
4 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
Nordhold: Fusionssystem, erklärt
4 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
Mandragora: Flüstern des Hexenbaum
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen

Java-Tutorial
1671
14
PHP-Tutorial
1276
29
C#-Tutorial
1256
24
Verursacht die Sicherheitssoftware des Unternehmens, die die Anwendung nicht ausführt? Wie kann man es beheben und es lösen? Verursacht die Sicherheitssoftware des Unternehmens, die die Anwendung nicht ausführt? Wie kann man es beheben und es lösen? Apr 19, 2025 pm 04:51 PM

Fehlerbehebung und Lösungen für die Sicherheitssoftware des Unternehmens, die dazu führt, dass einige Anwendungen nicht ordnungsgemäß funktionieren. Viele Unternehmen werden Sicherheitssoftware bereitstellen, um die interne Netzwerksicherheit zu gewährleisten. ...

Wie konvertiere ich Namen in Zahlen, um die Sortierung zu implementieren und die Konsistenz in Gruppen aufrechtzuerhalten? Wie konvertiere ich Namen in Zahlen, um die Sortierung zu implementieren und die Konsistenz in Gruppen aufrechtzuerhalten? Apr 19, 2025 pm 11:30 PM

Lösungen zum Umwandeln von Namen in Zahlen zur Implementierung der Sortierung in vielen Anwendungsszenarien müssen Benutzer möglicherweise in Gruppen sortieren, insbesondere in einem ...

Wie vereinfachte ich Probleme mit der Feldzuordnung im Systemdocking mithilfe des Mapstruct? Wie vereinfachte ich Probleme mit der Feldzuordnung im Systemdocking mithilfe des Mapstruct? Apr 19, 2025 pm 06:21 PM

Die Verarbeitung von Feldzuordnungen im Systemdocken stößt häufig auf ein schwieriges Problem bei der Durchführung von Systemdocken: So kartieren Sie die Schnittstellenfelder des Systems und ...

Wie identifiziert Intellij IDEA die Portnummer eines Spring -Boot -Projekts, ohne ein Protokoll auszugeben? Wie identifiziert Intellij IDEA die Portnummer eines Spring -Boot -Projekts, ohne ein Protokoll auszugeben? Apr 19, 2025 pm 11:45 PM

Beginnen Sie den Frühling mit der Intellijideaultimate -Version ...

Wie kann ich elegante Entitätsklassenvariablennamen erhalten, um Datenbankabfragebedingungen zu erstellen? Wie kann ich elegante Entitätsklassenvariablennamen erhalten, um Datenbankabfragebedingungen zu erstellen? Apr 19, 2025 pm 11:42 PM

Bei Verwendung von MyBatis-Plus oder anderen ORM-Frameworks für Datenbankvorgänge müssen häufig Abfragebedingungen basierend auf dem Attributnamen der Entitätsklasse erstellt werden. Wenn Sie jedes Mal manuell ...

Wie kann ich Java -Objekte sicher in Arrays umwandeln? Wie kann ich Java -Objekte sicher in Arrays umwandeln? Apr 19, 2025 pm 11:33 PM

Konvertierung von Java-Objekten und -Arrays: Eingehende Diskussion der Risiken und korrekten Methoden zur Konvertierung des Guss-Typs Viele Java-Anfänger werden auf die Umwandlung eines Objekts in ein Array stoßen ...

E-Commerce-Plattform SKU und SPU-Datenbankdesign: Wie berücksichtigen Sie sowohl benutzerdefinierte Attribute als auch Attributloses Produkte? E-Commerce-Plattform SKU und SPU-Datenbankdesign: Wie berücksichtigen Sie sowohl benutzerdefinierte Attribute als auch Attributloses Produkte? Apr 19, 2025 pm 11:27 PM

Detaillierte Erläuterung des Designs von SKU- und SPU-Tabellen auf E-Commerce-Plattformen In diesem Artikel werden die Datenbankdesignprobleme von SKU und SPU in E-Commerce-Plattformen erörtert, insbesondere wie man mit benutzerdefinierten Verkäufen umgeht ...

Wie verwendet ich die Redis -Cache -Lösung, um die Anforderungen der Produktranking -Liste effizient zu erkennen? Wie verwendet ich die Redis -Cache -Lösung, um die Anforderungen der Produktranking -Liste effizient zu erkennen? Apr 19, 2025 pm 11:36 PM

Wie erkennt die Redis -Caching -Lösung die Anforderungen der Produktranking -Liste? Während des Entwicklungsprozesses müssen wir uns häufig mit den Anforderungen der Ranglisten befassen, z. B. das Anzeigen eines ...

See all articles