การรักษาความปลอดภัยระดับเมธอดด้วยแอนโนเทชัน
รักษาความปลอดภัยให้แต่ละเมธอดในชั้นบริการด้วยแอนโนเทชันของ Spring Security เช่น `@PreAuthorize` และ `@PostAuthorize`
การรักษาความปลอดภัยระดับเมธอดด้วยแอนโนเทชัน เป็นบทเรียน Spring Security 6 & JWT Authentication ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Spring Security 6 & JWT Authentication และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Spring Security 6 & JWT Authentication มีบทเรียนทั้งหมด 4 บทเรียน
บางส่วนของบทเรียนนี้ยังไม่ได้รับการแปล และแสดงเป็นภาษาอังกฤษ
Intro to Method Security
Welcome! In Spring Security, we often secure web endpoints using HttpSecurity. But what if you need finer control within your application logic?
Method-level security lets you protect individual methods in your service layer, ensuring only authorized users can call them. This adds another powerful layer of defense!
Enabling Method Security
To use method-level security, you need to enable it in your Spring Security configuration. This is done with the @EnableMethodSecurity annotation.
Place it on your main security configuration class, usually one extending WebSecurityConfigurerAdapter (though in Spring Security 6, you often just use a @Configuration class with a 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: Before Execution
The @PreAuthorize annotation checks authorization before a method is executed. If the condition isn't met, the method won't run, and an AccessDeniedException is thrown.
It uses Spring Expression Language (SpEL) to define powerful authorization rules. Common uses include checking roles or specific authorities.
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.";
}
}Running @PreAuthorize Example
Let's see @PreAuthorize in action. This runnable example simulates how Spring Security would process the hasRole('ADMIN') check by manually setting a user's role in the security context.
Try running it to see how access is granted or denied based on the assigned role.
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 for Dynamic Checks
@PreAuthorize can do more than just check roles! You can use SpEL to access method arguments, the authenticated principal, or even custom beans.
For example, #userId refers to a method parameter named userId. authentication.principal.username gets the current user's username.
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: After Execution
Sometimes, you need to make an authorization decision after the method has executed and you have access to its return value. This is where @PostAuthorize comes in.
It's useful for ensuring that the authenticated user is allowed to *see* the data that was just retrieved or processed by the method. You can access the return value using returnObject in 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");
}
}Running @PostAuthorize Example
This example demonstrates @PostAuthorize. We simulate a UserProfile being returned, and then check if the current authenticated user is allowed to view it.
Notice how the check happens *after* the getUserProfile method has produced its result.
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();
}
}Filtering Collections: @PreFilter & @PostFilter
For methods that accept or return collections, Spring Security offers @PreFilter and @PostFilter.
@PreFilter: Filters a collection argument *before* the method executes.@PostFilter: Filters the returned collection *after* the method executes.
These are powerful for scenarios like showing a user only the items in a list they own, or allowing updates only to specific elements in a 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
}
}Best Practices for Method Security
To use method-level security effectively:
- Apply to Service Layer: Keep business logic separate from controllers. Secure your service methods.
- Combine with HttpSecurity: Use URL-based security for broad access control, and method-level for fine-grained permissions. They complement each other.
- Keep SpEL Concise: Complex SpEL can be hard to read and debug. Consider custom permission evaluators for very intricate logic.
- Test Thoroughly: Always test your security rules to ensure they behave as expected.
Quick Check: Method Annotations
You're building an application and need to ensure that a method public void deleteUser(String userId) can only be called by a user with the ADMIN role, AND only if the userId matches the currently authenticated user's ID.
Which @PreAuthorize expression would correctly enforce this?
Recap & Next Steps
Great job! You've learned about method-level security in Spring Security.
@EnableMethodSecurityactivates this feature.@PreAuthorizechecks permissions *before* method execution.@PostAuthorizechecks permissions *after* method execution, inspecting the return value.@PreFilterand@PostFilterhelp filter collection arguments or return values.
This powerful tool allows for very precise control over who can access your application's internal operations. Keep practicing and exploring the capabilities of SpEL!
คำถามที่พบบ่อย
บทเรียน “การรักษาความปลอดภัยระดับเมธอดด้วยแอนโนเทชัน” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การรักษาความปลอดภัยระดับเมธอดด้วยแอนโนเทชัน” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Spring Security 6 & JWT Authentication ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Spring Security 6 & JWT Authentication มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การรักษาความปลอดภัยระดับเมธอดด้วยแอนโนเทชัน”
รักษาความปลอดภัยให้แต่ละเมธอดในชั้นบริการด้วยแอนโนเทชันของ Spring Security เช่น `@PreAuthorize` และ `@PostAuthorize` คุณปฏิบัติ Spring Security 6 & JWT Authentication ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Spring Security 6 & JWT Authentication หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Spring Security 6 & JWT Authentication บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “การรักษาความปลอดภัยระดับเมธอดด้วยแอนโนเทชัน” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Spring Security 6 & JWT Authentication นี้ได้ไหม
ได้ บทเรียน Spring Security 6 & JWT Authentication ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การควบคุมการเข้าถึงตามบทบาท (RBAC)
- การรักษาความปลอดภัยระดับเมธอดด้วยแอนโนเทชัน
- เจาะลึกการกำหนดค่า HttpSecurity
- รักษาความปลอดภัยให้ปลายทางด้วยกฎการเข้าถึงแบบกำหนดเอง