0
0
Spring Bootframework~15 mins

Custom permission evaluator in Spring Boot - Deep Dive

Choose your learning style9 modes available
Overview - Custom permission evaluator
What is it?
A custom permission evaluator in Spring Boot is a way to control access to parts of an application by defining your own rules. It lets you decide who can do what based on your specific needs, beyond the default settings. This is done by creating a class that checks permissions in a way that fits your app's logic. It helps keep your app secure and flexible.
Why it matters
Without custom permission evaluators, you would be stuck with generic access rules that might not fit your app's unique needs. This could lead to either too much access, risking security, or too little, frustrating users. Custom permission evaluators let you tailor security precisely, protecting sensitive data and actions while keeping the user experience smooth.
Where it fits
Before learning custom permission evaluators, you should understand basic Spring Security concepts like authentication and authorization. After mastering this, you can explore advanced security topics like method security, expression handlers, and integrating with OAuth2 or JWT for complex access control.
Mental Model
Core Idea
A custom permission evaluator is a special checker that decides if a user can do a specific action based on your own rules.
Think of it like...
It's like a bouncer at a club who doesn't just check your ID but also asks if you have a VIP pass or if you're on the guest list for a special event.
┌───────────────────────────────┐
│        User Request            │
└──────────────┬────────────────┘
               │
               ▼
┌───────────────────────────────┐
│   Custom Permission Evaluator  │
│  (Checks user rights & rules)  │
└──────────────┬────────────────┘
               │
       Yes ┌───┴───┐ No
            ▼       ▼
      ┌────────┐ ┌─────────┐
      │ Allow  │ │ Deny    │
      └────────┘ └─────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding Spring Security Basics
🤔
Concept: Learn what authentication and authorization mean in Spring Security.
Authentication is proving who you are, like logging in with a username and password. Authorization is deciding what you can do after logging in, like accessing certain pages or features. Spring Security handles these by default with roles and simple rules.
Result
You know how Spring Security checks if a user is logged in and what roles they have.
Understanding the difference between authentication and authorization is key to knowing where custom permission evaluators fit in.
2
FoundationRole-Based Access Control Basics
🤔
Concept: Learn how Spring Security uses roles to allow or deny access.
Roles are labels like 'ADMIN' or 'USER' assigned to users. You can protect parts of your app by saying only users with certain roles can access them. This is simple but sometimes too broad.
Result
You can secure endpoints or methods by roles using annotations like @PreAuthorize("hasRole('ADMIN')").
Role-based control is easy but limited; it can't handle complex rules like ownership or dynamic conditions.
3
IntermediateIntroducing PermissionEvaluator Interface
🤔Before reading on: do you think Spring Security allows you to define your own permission checks beyond roles? Commit to yes or no.
Concept: Spring Security provides a PermissionEvaluator interface to create custom permission checks.
PermissionEvaluator has methods like hasPermission(Authentication, Object, Object) that you implement to define your own logic. This lets you check if a user can access a specific domain object or perform an action.
Result
You can write code that decides permissions based on your app's data and rules, not just roles.
Knowing that Spring Security lets you plug in your own permission logic opens the door to flexible, fine-grained security.
4
IntermediateImplementing a Custom PermissionEvaluator
🤔Before reading on: do you think your custom evaluator needs to know who the user is and what object they want to access? Commit to yes or no.
Concept: Create a class implementing PermissionEvaluator and override its methods to check permissions.
You get the Authentication object (who the user is), the target domain object, and the permission requested. Your code checks if the user meets your rules, like owning the object or having a special flag.
Result
Your app can now decide access based on detailed conditions, like 'user can edit only their own posts'.
Understanding the inputs to the evaluator helps you write precise permission rules tailored to your app's needs.
5
IntermediateUsing Custom Evaluator in Security Expressions
🤔Before reading on: do you think you can call your custom permission checks directly in @PreAuthorize annotations? Commit to yes or no.
Concept: Integrate your custom evaluator with Spring Security expressions to use it in annotations.
Configure Spring Security to use your PermissionEvaluator. Then in @PreAuthorize, use expressions like hasPermission(#post, 'edit') to call your logic. This makes your security rules readable and close to the code they protect.
Result
You can protect methods or endpoints with your own permission logic in a clean, declarative way.
Knowing how to connect your evaluator to expressions makes your security both powerful and maintainable.
6
AdvancedHandling Complex Permission Logic
🤔Before reading on: do you think your evaluator can handle multiple conditions like roles, ownership, and time-based rules together? Commit to yes or no.
Concept: Your evaluator can combine many rules to decide permissions, not just one simple check.
For example, allow editing only if the user owns the object and the object is not archived, or if the user has an admin role. You write this logic inside your evaluator methods, possibly calling services or databases.
Result
Your app enforces nuanced security policies that reflect real business needs.
Understanding that evaluators can encapsulate complex logic helps you build secure, flexible applications.
7
ExpertPerformance and Security Considerations
🤔Before reading on: do you think calling database queries inside your evaluator can slow down your app or cause security holes? Commit to yes or no.
Concept: Custom evaluators run often, so they must be efficient and secure to avoid slowdowns or leaks.
Avoid heavy database calls inside evaluators or cache results. Be careful not to expose sensitive data in error messages. Also, understand that evaluators run after authentication, so they rely on correct user identity.
Result
Your app remains fast and secure even with complex permission checks.
Knowing the internal costs and risks of evaluators helps you write production-ready security code.
Under the Hood
When a secured method or endpoint is accessed, Spring Security intercepts the call and evaluates the security expression. If the expression uses hasPermission, Spring calls the configured PermissionEvaluator's methods, passing the current user's Authentication, the target object, and the requested permission. The evaluator runs your custom logic and returns true or false. Based on this, access is granted or denied. This happens at runtime, ensuring dynamic and context-aware security.
Why designed this way?
Spring Security was designed to be flexible and extensible. The PermissionEvaluator interface allows developers to plug in their own rules without changing the framework core. This design separates generic security concerns from application-specific logic, enabling reuse and customization. Alternatives like hardcoding rules or using only roles were too rigid for complex apps, so this interface was introduced to balance power and simplicity.
┌───────────────────────────────┐
│  User Request to Secured Code │
└──────────────┬────────────────┘
               │
               ▼
┌───────────────────────────────┐
│ Spring Security Interceptor    │
│ (Evaluates @PreAuthorize etc) │
└──────────────┬────────────────┘
               │
               ▼
┌───────────────────────────────┐
│ PermissionEvaluator Interface  │
│ (Custom implementation called) │
└──────────────┬────────────────┘
               │
       Returns true/false
               │
               ▼
┌───────────────────────────────┐
│ Access Granted or Denied       │
└───────────────────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Do you think a custom permission evaluator replaces roles completely? Commit to yes or no.
Common Belief:A custom permission evaluator means you no longer need roles for access control.
Tap to reveal reality
Reality:Custom evaluators often work alongside roles, not instead of them. Roles provide broad access categories, while evaluators handle fine details.
Why it matters:Ignoring roles can lead to overly complex evaluators and harder-to-maintain security rules.
Quick: Do you think the PermissionEvaluator runs before user authentication? Commit to yes or no.
Common Belief:PermissionEvaluator checks happen before the user is authenticated.
Tap to reveal reality
Reality:PermissionEvaluator runs after authentication, using the authenticated user's details to decide permissions.
Why it matters:Assuming it runs before can cause confusion about available data and lead to security holes.
Quick: Do you think you can safely call any service or database inside your evaluator without performance impact? Commit to yes or no.
Common Belief:Calling services or databases inside the evaluator is always safe and fast.
Tap to reveal reality
Reality:Heavy calls inside evaluators can slow down your app and cause timeouts or errors.
Why it matters:Poor performance in evaluators can degrade user experience and cause security checks to fail.
Quick: Do you think the hasPermission expression only works with domain objects? Commit to yes or no.
Common Belief:hasPermission only works when you pass actual domain objects to it.
Tap to reveal reality
Reality:hasPermission can also work with identifiers or other parameters, but you must handle them carefully in your evaluator.
Why it matters:Misunderstanding this limits your ability to secure methods that receive IDs or other references instead of full objects.
Expert Zone
1
Custom permission evaluators can be combined with expression handlers to create even more powerful security expressions.
2
Caching permission decisions inside evaluators can improve performance but requires careful invalidation strategies.
3
Evaluators can be designed to support hierarchical permissions, allowing inheritance of access rights.
When NOT to use
Avoid custom permission evaluators when simple role-based access control suffices, as they add complexity. For very large-scale or distributed systems, consider externalized policy engines like OAuth scopes or Attribute-Based Access Control (ABAC) systems instead.
Production Patterns
In production, evaluators often check ownership of resources by querying databases or services, combine role checks with business rules, and integrate with audit logging. They are used in microservices to enforce security consistently across APIs.
Connections
Attribute-Based Access Control (ABAC)
Custom permission evaluators implement ABAC principles by evaluating user attributes and resource properties.
Understanding ABAC helps design evaluators that consider multiple factors, not just roles, for access decisions.
Design Patterns - Strategy Pattern
Custom permission evaluators follow the Strategy pattern by encapsulating permission logic in interchangeable classes.
Recognizing this pattern clarifies how Spring Security allows swapping different permission logic without changing core code.
Legal Compliance in Data Privacy
Custom permission evaluators help enforce data access rules required by laws like GDPR by controlling who can see or modify personal data.
Knowing this connection highlights the real-world importance of precise permission checks beyond technical needs.
Common Pitfalls
#1Writing permission logic that trusts user input without validation.
Wrong approach:public boolean hasPermission(Authentication auth, Object target, Object perm) { String requestedPermission = (String) perm; return requestedPermission.equals("edit"); // blindly trusts input }
Correct approach:public boolean hasPermission(Authentication auth, Object target, Object perm) { if (!(perm instanceof String)) return false; String requestedPermission = (String) perm; // check user roles and ownership here return checkUserPermission(auth, target, requestedPermission); }
Root cause:Misunderstanding that permission strings can be manipulated and must be validated.
#2Performing heavy database queries inside the evaluator without caching.
Wrong approach:public boolean hasPermission(Authentication auth, Object target, Object perm) { return databaseService.isOwner(auth.getName(), target.getId()); // called every time }
Correct approach:public boolean hasPermission(Authentication auth, Object target, Object perm) { return cache.getOrCompute(auth.getName(), target.getId(), () -> databaseService.isOwner(auth.getName(), target.getId())); }
Root cause:Not considering performance impact of repeated database calls.
#3Not registering the custom PermissionEvaluator in the security configuration.
Wrong approach:@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { // missing permissionEvaluator bean }
Correct approach:@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Bean public PermissionEvaluator permissionEvaluator() { return new CustomPermissionEvaluator(); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .expressionHandler(expressionHandler()); } @Bean public DefaultWebSecurityExpressionHandler expressionHandler() { DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler(); handler.setPermissionEvaluator(permissionEvaluator()); return handler; } }
Root cause:Forgetting to wire the custom evaluator means Spring uses the default, ignoring your logic.
Key Takeaways
Custom permission evaluators let you define detailed access rules beyond simple roles, making your app's security flexible and precise.
They work by implementing an interface that Spring Security calls during authorization checks, using information about the user and the target object.
Integrating your evaluator with security expressions allows you to write clear and maintainable access rules close to your business logic.
Be mindful of performance and security when writing evaluators, avoiding heavy operations and validating inputs carefully.
Understanding when and how to use custom permission evaluators helps you build secure, scalable, and compliant applications.