Skip to content

Commit

Permalink
Document @AuthenticationPrincipal meta-annotations
Browse files Browse the repository at this point in the history
Issue gh-15286
  • Loading branch information
jzheaux committed Aug 10, 2024
1 parent 9aaf959 commit f4d9d0d
Showing 1 changed file with 120 additions and 0 deletions.
120 changes: 120 additions & 0 deletions docs/modules/ROOT/pages/servlet/integrations/mvc.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,126 @@ open fun findMessagesForUser(@CurrentUser customUser: CustomUser?): ModelAndView
----
======

Once it is a meta-annotation, parameterization is also available to you.

For example, consider when you have a JWT as your principal and you want to say which claim to retrieve.
As a meta-annotation, you might do:

[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Target({ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AuthenticationPrincipal(expression = "claims['sub']")
public @interface CurrentUser {}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@AuthenticationPrincipal(expression = "claims['sub']")
annotation class CurrentUser
----
======

which is already quite powerful.
But, it is also limited to retrieving the `sub` claim.

To make this more flexible, first publish the `AnnotationTemplateExpressionDefaults` bean like so:

[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
public AnnotationTemplateExpressionDefaults templateDefaults() {
return new AnnotationTemplateExpressionDeafults();
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
fun templateDefaults(): AnnotationTemplateExpressionDefaults {
return AnnotationTemplateExpressionDeafults()
}
----
Xml::
+
[source,xml,role="secondary"]
----
<b:bean name="annotationExpressionTemplateDefaults" class="org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults"/>
----
======

and then you can supply a parameter to `@CurrentUser` like so:

[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Target({ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AuthenticationPrincipal(expression = "claims['{claim}']")
public @interface CurrentUser {
String claim() default 'sub';
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@AuthenticationPrincipal(expression = "claims['{claim}']")
annotation class CurrentUser(val claim: String = "sub")
----
======

This will allow you more flexibility across your set of applications in the following way:

[tabs]
======
Java::
+
[source,java,role="primary"]
----
@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(@CurrentUser("user_id") String userId) {
// .. find messages for this user and return them ...
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@RequestMapping("/messages/inbox")
open fun findMessagesForUser(@CurrentUser("user_id") userId: String?): ModelAndView {
// .. find messages for this user and return them ...
}
----
======

[[mvc-async]]
== Spring MVC Async Integration
Expand Down

0 comments on commit f4d9d0d

Please sign in to comment.