Spring Security 6 og JWT-godkendelse · Lektion

Sikkerhed på metodeniveau med annotations

Beskyt individuelle metoder i Deres servicelag ved hjælp af Spring Security-annotations som `@PreAuthorize` og `@PostAuthorize`.

Lektion 2 af 411 trin

Sikkerhed på metodeniveau med annotations er en gratis Spring Security 6 og JWT-godkendelse-lektion på CoddyKit. Dette er lektion 2 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Spring Security 6 og JWT-godkendelse, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Spring Security 6 og JWT-godkendelse-kurset indeholder 4 lektioner i alt.

Introduktion til metodesikkerhed

Velkommen! I Spring Security beskytter vi ofte web-endpoints ved hjælp af HttpSecurity. Men hvad gør du, hvis du har brug for mere finmasket kontrol inde i din applikationslogik?

Sikkerhed på metodeniveau lader dig beskytte individuelle metoder i dit servicelag, så kun autoriserede brugere kan kalde dem. Det tilføjer endnu et effektivt forsvarslag!

Aktivering af metodesikkerhed

Hvis du vil bruge sikkerhed på metodeniveau, skal du aktivere den i din Spring Security-konfiguration. Det gør du med annotationen @EnableMethodSecurity.

Placér den på din primære sikkerhedskonfigurationsklasse, som normalt nedarver fra WebSecurityConfigurerAdapter (i Spring Security 6 bruger du dog ofte blot en @Configuration-klasse med en FilterChainBean).

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;

@Configuration
@EnableMethodSecurity // Enables @PreAuthorize, @PostAuthorize, etc.
public class SecurityConfig {
    // Your security filter chain bean goes here
}

@PreAuthorize: Før udførelse

Annotationen @PreAuthorize kontrollerer autorisation før en metode udføres. Hvis betingelsen ikke er opfyldt, bliver metoden ikke kørt, og der kastes en AccessDeniedException.

Den bruger Spring Expression Language (SpEL) til at definere effektive autorisationsregler. Almindelige anvendelser omfatter kontrol af roller eller bestemte beføjelser.

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

@Service
public class AdminService {

    @PreAuthorize("hasRole('ADMIN')")
    public String deleteSensitiveData() {
        return "Sensitive data deleted!";
    }

    @PreAuthorize("hasAuthority('product:write')")
    public String createProduct(String productName) {
        return "Product '" + productName + "' created.";
    }
}

Kørsel af et @PreAuthorize-eksempel

Lad os se @PreAuthorize i praksis. Dette kørbare eksempel simulerer, hvordan Spring Security ville behandle kontrollen hasRole('ADMIN'), ved manuelt at angive en brugers rolle i sikkerhedskonteksten.

Prøv at køre det for at se, hvordan adgang gives eller nægtes ud fra den tildelte rolle.

import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;

import java.util.Collections;
import java.util.List;

public class Main {

    // This method simulates a service method protected by @PreAuthorize("hasRole('ADMIN')")
    public static String getAdminMessage() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        // In a real Spring app, AOP would handle this check before method entry.
        // We simulate it here for demonstration.
        if (auth != null && auth.getAuthorities().stream()
                .anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"))) {
            return "Welcome, Admin! Here's your secret message.";
        } else {
            return "Access Denied: ADMIN role required.";
        }
    }

    public static void main(String[] args) {
        // Scenario 1: User with ADMIN role
        List<GrantedAuthority> adminAuthorities = Collections.singletonList(new SimpleGrantedAuthority("ROLE_ADMIN"));
        Authentication adminAuth = new UsernamePasswordAuthenticationToken("adminUser", "pass", adminAuthorities);
        SecurityContextHolder.getContext().setAuthentication(adminAuth);
        System.out.println("Admin user attempt: " + getAdminMessage());

        // Scenario 2: User with USER role
        List<GrantedAuthority> userAuthorities = Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"));
        Authentication userAuth = new UsernamePasswordAuthenticationToken("regularUser", "pass", userAuthorities);
        SecurityContextHolder.getContext().setAuthentication(userAuth);
        System.out.println("\nRegular user attempt: " + getAdminMessage());

        SecurityContextHolder.clearContext(); // Clean up
    }
}

SpEL til dynamiske kontroller

@PreAuthorize kan mere end blot at kontrollere roller! Du kan bruge SpEL til at få adgang til metodeargumenter, den godkendte bruger eller endda tilpassede beans.

For eksempel henviser #userId til en metodeparameter med navnet userId. authentication.principal.username henter den aktuelle brugers brugernavn.

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    // Only allow users to view their own profile
    @PreAuthorize("#userId == authentication.principal.username")
    public String viewUserProfile(String userId) {
        return "Viewing profile for: " + userId;
    }

    // Allow ADMIN or the owner of the resource
    @PreAuthorize("hasRole('ADMIN') or #resourceOwner == authentication.principal.username")
    public String editResource(String resourceId, String resourceOwner) {
        return "Editing resource '" + resourceId + "' by '" + resourceOwner + "'.";
    }
}

@PostAuthorize: Efter udførelse

Nogle gange skal du træffe en autorisationsbeslutning efter, at metoden er udført, og du har adgang til dens returværdi. Det er her, @PostAuthorize kommer ind i billedet.

Det er nyttigt, når du vil sikre, at den godkendte bruger har tilladelse til at *se* de data, som metoden netop har hentet eller behandlet. Du kan få adgang til returværdien ved hjælp af returnObject i SpEL.

import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.stereotype.Service;

// A simple data class for demonstration
class UserProfile {
    String username;
    String email;
    public UserProfile(String username, String email) {
        this.username = username;
        this.email = email;
    }
    public String getUsername() { return username; }
    public String getEmail() { return email; }
}

@Service
public class ProfileService {

    // Only allow access to profile if current user is the owner or an ADMIN
    @PostAuthorize("returnObject.username == authentication.principal.username or hasRole('ADMIN')")
    public UserProfile getUserProfile(String requestedUsername) {
        // In a real app, this would fetch from a database
        if ("john.doe".equals(requestedUsername)) {
            return new UserProfile("john.doe", "john@example.com");
        }
        return new UserProfile("guest", "guest@example.com");
    }
}

Kørsel af et @PostAuthorize-eksempel

Dette eksempel demonstrerer @PostAuthorize. Vi simulerer, at der returneres en UserProfile, og kontrollerer derefter, om den aktuelle godkendte bruger har tilladelse til at se den.

Bemærk, at kontrollen sker *efter*, at metoden getUserProfile har produceret sit resultat.

import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;

import java.util.Collections;
import java.util.List;

// Simple data class for demonstration
class UserProfile {
    String username;
    String email;
    public UserProfile(String username, String email) {
        this.username = username;
        this.email = email;
    }
    public String getUsername() { return username; }
    public String getEmail() { return email; }
    @Override
    public String toString() { return "Profile: " + username + " (" + email + ")"; }
}

public class Main {

    // Simulates a service method with @PostAuthorize("returnObject.username == authentication.principal.username")
    public static UserProfile getSecuredUserProfile(String requestedUsername) {
        UserProfile profile = new UserProfile(requestedUsername, requestedUsername + "@example.com");
        
        // Simulate @PostAuthorize check
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null && auth.isAuthenticated() && 
            profile.getUsername().equals(auth.getName())) {
            return profile; // Allowed to view own profile
        } else if (auth != null && auth.isAuthenticated() && 
                   auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"))) {
            return profile; // Admin can view any profile
        } else {
            // In a real app, this would be an AccessDeniedException
            System.out.println("Access Denied to view profile for " + requestedUsername);
            return null; // Or throw an exception
        }
    }

    public static void main(String[] args) {
        // Scenario 1: User viewing their own profile
        List<GrantedAuthority> userAuthorities = Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"));
        Authentication userAuth = new UsernamePasswordAuthenticationToken("alice", "pass", userAuthorities);
        SecurityContextHolder.getContext().setAuthentication(userAuth);
        System.out.println("Alice viewing her own profile: " + getSecuredUserProfile("alice"));

        // Scenario 2: Alice trying to view Bob's profile
        System.out.println("\nAlice viewing Bob's profile: " + getSecuredUserProfile("bob"));

        // Scenario 3: Admin viewing Bob's profile
        List<GrantedAuthority> adminAuthorities = Collections.singletonList(new SimpleGrantedAuthority("ROLE_ADMIN"));
        Authentication adminAuth = new UsernamePasswordAuthenticationToken("admin", "pass", adminAuthorities);
        SecurityContextHolder.getContext().setAuthentication(adminAuth);
        System.out.println("\nAdmin viewing Bob's profile: " + getSecuredUserProfile("bob"));

        SecurityContextHolder.clearContext();
    }
}

Filtrering af samlinger: @PreFilter og @PostFilter

Til metoder, der modtager eller returnerer samlinger, tilbyder Spring Security @PreFilter og @PostFilter.

  • @PreFilter: Filtrerer et samlingsargument *før* metoden udføres.
  • @PostFilter: Filtrerer den returnerede samling *efter*, at metoden er udført.

De er effektive i situationer, hvor du f.eks. kun vil vise en bruger de elementer i en liste, som brugeren ejer, eller kun tillade opdateringer af bestemte elementer i en batch.

import org.springframework.security.access.prepost.PreFilter;
import org.springframework.security.access.prepost.PostFilter;
import java.util.List;

class Item { String owner; String name; }

public class ItemService {

    // Only allow items owned by the current user to be processed
    @PreFilter("filterObject.owner == authentication.principal.username")
    public void processItems(List<Item> items) {
        // ... process only allowed items ...
    }

    // Only return items owned by the current user
    @PostFilter("filterObject.owner == authentication.principal.username")
    public List<Item> getAllItemsForUser() {
        // ... fetch all items from DB ...
        return List.of(new Item(), new Item()); // Returns filtered list
    }
}

Bedste praksis for metodesikkerhed

Sådan bruger du sikkerhed på metodeniveau effektivt:

  • Anvend den i servicelaget: Hold forretningslogikken adskilt fra controllere. Beskyt dine servicemetoder.
  • Kombiner med HttpSecurity: Brug URL-baseret sikkerhed til bred adgangskontrol og sikkerhed på metodeniveau til finmasket styring af tilladelser. De supplerer hinanden.
  • Hold SpEL kort og præcis: Kompleks SpEL kan være svær at læse og fejlfinde. Overvej tilpassede tilladelsesevaluatorer til meget indviklet logik.
  • Test grundigt: Test altid dine sikkerhedsregler for at sikre, at de fungerer som forventet.

Hurtig kontrol: Metodeannoteringer

Du er ved at bygge en applikation og skal sikre, at en metode public void deleteUser(String userId) kun kan kaldes af en bruger med rollen ADMIN OG kun hvis userId matcher ID'et på den aktuelt godkendte bruger.

Hvilket @PreAuthorize-udtryk håndhæver dette korrekt?

Opsummering og næste trin

Godt klaret! Du har lært om sikkerhed på metodeniveau i Spring Security.

  • @EnableMethodSecurity aktiverer denne funktion.
  • @PreAuthorize kontrollerer tilladelser *før* metoden udføres.
  • @PostAuthorize kontrollerer tilladelser *efter* metoden er udført og undersøger returværdien.
  • @PreFilter og @PostFilter hjælper med at filtrere samlingsargumenter eller returværdier.

Dette effektive værktøj giver dig meget præcis kontrol over, hvem der kan få adgang til din applikations interne funktioner. Bliv ved med at øve dig og udforske mulighederne i SpEL!

Gratis at komme i gang

Lær Java med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
12
Lektioner
48

Ofte stillede spørgsmål

Er lektionen “Sikkerhed på metodeniveau med annotations” gratis?

Ja — alle 3 lektioner i læringssporet Spring Security 6 og JWT-godkendelse, inklusive “Sikkerhed på metodeniveau med annotations”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Spring Security 6 og JWT-godkendelse-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Sikkerhed på metodeniveau med annotations”?

Beskyt individuelle metoder i Deres servicelag ved hjælp af Spring Security-annotations som `@PreAuthorize` og `@PostAuthorize`. Du øver dig i Spring Security 6 og JWT-godkendelse med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Spring Security 6 og JWT-godkendelse?

Der kræves ingen tidligere erfaring. Spring Security 6 og JWT-godkendelse på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 4.

Hvor lang tid tager lektionen “Sikkerhed på metodeniveau med annotations”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Spring Security 6 og JWT-godkendelse-lektion?

Ja. Alle Spring Security 6 og JWT-godkendelse-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Rollebaseret adgangskontrol (RBAC)
  2. Sikkerhed på metodeniveau med annotations
  3. Dybdegående konfiguration af HttpSecurity
  4. Sikring af endpoints med brugerdefinerede adgangsregler
← Tilbage til Spring Security 6 og JWT-godkendelse