Security context-based validation pattern¶
The Security context-based validation pattern is a specific implementation of [[method-level security]] where access control logic is delegated to a dedicated validator bean that inspects the runtime security context.^[001-TODO__code-getway.md]
This pattern is commonly used in Spring Security applications to enforce permissions that depend on dynamic parameters, such as the value of a method argument.^[001-TODO__code-getway.md]
Implementation¶
The pattern typically consists of three components: a validator bean, a secured controller method, and the parameterized permission logic.
Validator Bean¶
A standard Spring component (@Component or defined via @Bean) is created to encapsulate the security logic^[001-TODO__code-getway.md]. This bean accesses the current user's authentication details via SecurityContextHolder.getContext().getAuthentication()^[001-TODO__code-getway.md].
The logic extracts the principal (often cast to a custom UserDetails implementation) and compares the user's granted authorities against a required string derived from the input parameters^[001-TODO__code-getway.md].
Controller Integration¶
The controller is secured using the @PreAuthorize annotation^[001-TODO__code-getway.md]. Instead of checking a hard-coded role (e.g., hasRole('ADMIN')), the annotation references the validator bean using Spring Expression Language (SpEL), typically with a syntax like @beanName.hasAuthority(#paramName)^[001-TODO__code-getway.md].
This passes the method argument (e.g., reportType) directly to the validator, allowing the security check to vary based on the specific data being accessed^[001-TODO__code-getway.md].
Example Code¶
In the following example, ReportSecurityValidator checks if the user has a specific authority string associated with the requested ReportType^[001-TODO__code-getway.md].
// Validator Bean
public class ReportSecurityValidator {
public boolean hasAuthority(@NonNull ReportType reportType) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
AccountDetail details = (AccountDetail) auth.getPrincipal();
String requiredAuthority = reportType.getAuthority();
return details.getAuthorities().stream()
.anyMatch(a -> a.getAuthority().equals(requiredAuthority));
}
}
// Controller
@RestController
public class ReportManageController {
@PreAuthorize("@reportSecurityValidator.hasAuthority(#reportType)")
@GetMapping("/v1/report")
public SuccessResp<Void> startingMakeReport(@RequestParam ReportType reportType) {
// ...
}
}
Benefits¶
- Dynamic Authorization: Permissions are evaluated at runtime based on method arguments, enabling fine-grained access control for resources that share the same endpoint but represent different data types or domains^[001-TODO__code-getway.md].
- Centralized Logic: Security rules are managed within a dedicated bean, promoting cleaner code and easier maintenance compared to duplicating checks inside controller methods^[001-TODO__code-getway.md].
- Type Safety: By using domain objects (like Enums) as parameters, the validation logic can map specific types to specific permission strings (e.g.,
PROXY-> "proxy:domain:edit")^[001-TODO__code-getway.md].
Related Concepts¶
- Spring Security
- [[Access Control List]] (ACL)
- [[Method Security]]
Sources¶
001-TODO__code-getway.md