Add expression based Kotlin samples

Issue gh-8172
This commit is contained in:
Eleftheria Stein 2020-09-30 10:57:10 +02:00
parent 0286d368c3
commit 320567128a
1 changed files with 115 additions and 9 deletions

View File

@ -114,7 +114,9 @@ So if you aren't using the namespace and want to use expressions, you will have
If you wish to extend the expressions that are available, you can easily refer to any Spring Bean you expose.
For example, assuming you have a Bean with the name of `webSecurity` that contains the following method signature:
[source,java]
====
.Java
[source,java,role="primary"]
----
public class WebSecurity {
public boolean check(Authentication authentication, HttpServletRequest request) {
@ -123,6 +125,17 @@ public class WebSecurity {
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
class WebSecurity {
fun check(authentication: Authentication?, request: HttpServletRequest?): Boolean {
// ...
}
}
----
====
You could refer to the method using:
.Refer to method
@ -167,7 +180,9 @@ For example, consider a RESTful application that looks up a user by id from the
You can easily refer to the path variable by placing it in the pattern.
For example, if you had a Bean with the name of `webSecurity` that contains the following method signature:
[source,java]
====
.Java
[source,java,role="primary"]
----
public class WebSecurity {
public boolean checkUserId(Authentication authentication, int id) {
@ -176,6 +191,17 @@ public class WebSecurity {
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
class WebSecurity {
fun checkUserId(authentication: Authentication?, id: Int): Boolean {
// ...
}
}
----
====
You could refer to the method using:
.Path Variables
@ -234,22 +260,42 @@ Their use is enabled through the `global-method-security` namespace element:
The most obviously useful annotation is `@PreAuthorize` which decides whether a method can actually be invoked or not.
For example (from the "Contacts" sample application)
[source,java]
====
.Java
[source,java,role="primary"]
----
@PreAuthorize("hasRole('USER')")
public void create(Contact contact);
----
.Kotlin
[source,kotlin,role="secondary"]
----
@PreAuthorize("hasRole('USER')")
fun create(contact: Contact?)
----
====
which means that access will only be allowed for users with the role "ROLE_USER".
Obviously the same thing could easily be achieved using a traditional configuration and a simple configuration attribute for the required role.
But what about:
[source,java]
====
.Java
[source,java,role="primary"]
----
@PreAuthorize("hasPermission(#contact, 'admin')")
public void deletePermission(Contact contact, Sid recipient, Permission permission);
----
.Kotlin
[source,kotlin,role="secondary"]
----
@PreAuthorize("hasPermission(#contact, 'admin')")
fun deletePermission(contact: Contact?, recipient: Sid?, permission: Permission?)
----
====
Here we're actually using a method argument as part of the expression to decide whether the current user has the "admin"permission for the given contact.
The built-in `hasPermission()` expression is linked into the Spring Security ACL module through the application context, as we'll <<el-permission-evaluator,see below>>.
You can access any of the method arguments by name as expression variables.
@ -264,7 +310,9 @@ For example:
+
[source,java]
====
.Java
[source,java,role="primary"]
----
import org.springframework.security.access.method.P;
@ -274,6 +322,18 @@ import org.springframework.security.access.method.P;
public void doSomething(@P("c") Contact contact);
----
.Kotlin
[source,kotlin,role="secondary"]
----
import org.springframework.security.access.method.P
...
@PreAuthorize("#c.name == authentication.name")
fun doSomething(@P("c") contact: Contact?)
----
====
+
Behind the scenes this is implemented using `AnnotationParameterNameDiscoverer` which can be customized to support the value attribute of any specified annotation.
@ -284,7 +344,9 @@ For example:
+
[source,java]
====
.Java
[source,java,role="primary"]
----
import org.springframework.data.repository.query.Param;
@ -294,6 +356,18 @@ import org.springframework.data.repository.query.Param;
Contact findContactByName(@Param("n") String name);
----
.Kotlin
[source,kotlin,role="secondary"]
----
import org.springframework.data.repository.query.Param
...
@PreAuthorize("#n == authentication.name")
fun findContactByName(@Param("n") name: String?): Contact?
----
====
+
Behind the scenes this is implemented using `AnnotationParameterNameDiscoverer` which can be customized to support the value attribute of any specified annotation.
@ -311,12 +385,22 @@ Any Spring-EL functionality is available within the expression, so you can also
For example, if you wanted a particular method to only allow access to a user whose username matched that of the contact, you could write
--
[source,java]
====
.Java
[source,java,role="primary"]
----
@PreAuthorize("#contact.name == authentication.name")
public void doSomething(Contact contact);
----
.Kotlin
[source,kotlin,role="secondary"]
----
@PreAuthorize("#contact.name == authentication.name")
fun doSomething(contact: Contact?)
----
====
Here we are accessing another built-in expression, `authentication`, which is the `Authentication` stored in the security context.
You can also access its "principal" property directly, using the expression `principal`.
The value will often be a `UserDetails` instance, so you might use an expression like `principal.username` or `principal.enabled`.
@ -333,13 +417,24 @@ Spring Security supports filtering of collections, arrays, maps and streams usin
This is most commonly performed on the return value of a method.
For example:
[source,java]
====
.Java
[source,java,role="primary"]
----
@PreAuthorize("hasRole('USER')")
@PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')")
public List<Contact> getAll();
----
.Kotlin
[source,kotlin,role="secondary"]
----
@PreAuthorize("hasRole('USER')")
@PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')")
fun getAll(): List<Contact?>
----
====
When using the `@PostFilter` annotation, Spring Security iterates through the returned collection or map and removes any elements for which the supplied expression is false.
For an array, a new array instance will be returned containing filtered elements.
The name `filterObject` refers to the current object in the collection.
@ -412,13 +507,24 @@ For example, consider the following:
Instead of repeating this everywhere, we can create a meta annotation that can be used instead.
[source,java]
====
.Java
[source,java,role="primary"]
----
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("#contact.name == authentication.name")
public @interface ContactPermission {}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@Retention(AnnotationRetention.RUNTIME)
@PreAuthorize("#contact.name == authentication.name")
annotation class ContactPermission
----
====
Meta annotations can be used for any of the Spring Security method security annotations.
In order to remain compliant with the specification JSR-250 annotations do not support meta annotations.