Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Document @AuthenticationPrincipal meta-annotations
Browse files Browse the repository at this point in the history
jzheaux committed Aug 10, 2024

Verified

This commit was signed with the committer’s verified signature. The key has expired.
renovate-bot Mend Renovate
1 parent ca9ed7e commit 711f806
Showing 1 changed file with 113 additions and 0 deletions.
113 changes: 113 additions & 0 deletions docs/modules/ROOT/pages/servlet/integrations/mvc.adoc
Original file line number Diff line number Diff line change
@@ -503,6 +503,119 @@ 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()
}
----
======

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

0 comments on commit 711f806

Please sign in to comment.