Dynamic Annotations and Return Object Protection in Spring Security 6.3
This article explains the new authorization features of Spring Boot 3.3 and Spring Security 6.3, including dynamic annotation parameters, return‑object protection for data security, and custom 403 error handling with code examples and practical guidance.
Spring Boot 3.3 and Spring Security 6.3 introduce several notable changes in authorization. This article explores these new features and demonstrates their usage with concrete code examples.
1. Dynamic Annotation Parameters
Spring Security method security supports meta‑annotations, allowing you to create readable custom annotations. For example, the standard
@PreAuthorize("hasRole('USER')")can be wrapped in a meta‑annotation:
<code>@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasRole('USER')")
public @interface IsUser {
String[] value();
}
</code>You can then use
@IsUseron service methods:
<code>@Service
public class DemoService {
@IsUser
public Data demoService() {
return "data";
}
}
</code>Instead of creating separate annotations for each role, Spring Security 6.3 lets you define a meta‑annotation that accepts role parameters. Define a bean for template defaults:
<code>@Bean
PrePostTemplateDefaults prePostTemplateDefaults() {
return new PrePostTemplateDefaults();
}
</code>Then create a meta‑annotation that can handle any role:
<code>@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasAnyRole({value})")
public @interface CustomHasAnyRole {
String[] value();
}
</code>Use it in a service class:
<code>@Service
public class DemoService {
private final List<Message> messages;
public DemoService() {
messages = new ArrayList<>();
messages.add(new Message(1, "Message 1"));
}
@CustomHasAnyRole({"'USER'", "'ADMIN'"})
public Message readMessage(Integer id) {
return messages.get(0);
}
@CustomHasAnyRole({"'ADMIN'"})
public String writeMessage(Message message) {
return "Message Written";
}
@CustomHasAnyRole({"'ADMIN'"})
public String deleteMessage(Integer id) {
return "Message Deleted";
}
}
</code>In the PIG micro‑service framework, a similar approach is used with
@HasPermission:
<code>@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("@pms.hasPermission('{value}'.split(','))")
public @interface HasPermission {
/**
* Permission strings
*/
String[] value();
}
</code>Usage becomes clearer:
<code>@PreAuthorize("@pms.hasPermission('job_sys_job_add')")
↓
@HasPermission("job_sys_job_add")
</code>2. Return‑Object Protection (Data Permissions)
Spring Security 6.3 adds the
@AuthorizeReturnObjectannotation, which secures the returned domain object. Only users with the appropriate authority can access the protected fields.
<code>public class Account {
private String iban;
private Double balance;
public String getIban() { return iban; }
@PreAuthorize("hasAuthority('read')")
public Double getBalance() { return balance; }
}
</code>The service returning the object is annotated as follows:
<code>@Service
public class AccountService {
@AuthorizeReturnObject
public Optional<Account> getAccountByIban(String iban) {
return Optional.of(new Account("XX1234567809", 2345.6));
}
}
</code>The annotation ensures that only callers with the "read" authority can retrieve the
Accountinstance.
3. 403 Error Handling
When
@AuthorizeReturnObjectdenies access, Spring Security throws an
AccessDeniedException. You can handle this with a custom
MethodAuthorizationDeniedHandlerimplementation that returns a masked value instead of the exception.
<code>@Component
public class MaskMethodAuthorizationDeniedHandler implements MethodAuthorizationDeniedHandler {
@Override
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
return "****";
}
}
</code>Apply the handler to a method:
<code>@PreAuthorize("hasAuthority('read')")
@HandleAuthorizationDenied(handlerClass = MaskMethodAuthorizationDeniedHandler.class)
public String getIban() {
return iban;
}
</code>Feature Preview
Based on Spring Boot 3.3's flexible annotation capabilities, the new version of the PIG micro‑service platform implements the #I8I2YL permission design as a Sa‑Token alternative, compatible with Spring Authorization Server and Sa‑Token OAuth 2.0 models.
All code and architecture remain unchanged while supporting both Spring Authorization Server and Sa‑Token.
Source code: https://github.com/pig-mesh/pig/tree/next-sa
Reference: #I8I2YL Permission Design – Sa‑Token alternative via plugin mechanism
Java Architecture Diary
Committed to sharing original, high‑quality technical articles; no fluff or promotional content.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.