Add @AuthenticationPrincipal expression
It is now possible to provide a SpEL expression for @AuthenticationPrincipal. This allows invoking custom logic including methods on the principal object. Fixes gh-3859
This commit is contained in:
parent
78bf6e2bd5
commit
9745de9510
|
@ -30,9 +30,9 @@ import org.springframework.security.core.Authentication;
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
*
|
*
|
||||||
* See: <a href="{@docRoot}/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolver.html">
|
* See: <a href=
|
||||||
* AuthenticationPrincipalArgumentResolver
|
* "{@docRoot}/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolver.html"
|
||||||
* </a>
|
* > AuthenticationPrincipalArgumentResolver </a>
|
||||||
*/
|
*/
|
||||||
@Target({ ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
|
@Target({ ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@ -46,4 +46,33 @@ public @interface AuthenticationPrincipal {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
boolean errorOnInvalidType() default false;
|
boolean errorOnInvalidType() default false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If specified will use the provided SpEL expression to resolve the principal. This
|
||||||
|
* is convenient if users need to transform the result.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* For example, perhaps the user wants to resolve a CustomUser object that is final
|
||||||
|
* and is leveraging a UserDetailsService. This can be handled by returning an object
|
||||||
|
* that looks like:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public class CustomUserUserDetails extends User {
|
||||||
|
* // ...
|
||||||
|
* public CustomUser getCustomUser() {
|
||||||
|
* return customUser;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Then the user can specify an annotation that looks like:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* @AuthenticationPrincipal(expression = "customUser")
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @return the expression to use.
|
||||||
|
*/
|
||||||
|
String expression() default "";
|
||||||
}
|
}
|
||||||
|
|
|
@ -388,6 +388,7 @@ Here is the list of improvements:
|
||||||
* <<headers-hpkp,HTTP Public Key Pinning (HPKP)>>
|
* <<headers-hpkp,HTTP Public Key Pinning (HPKP)>>
|
||||||
* <<csrf-cookie,CookieCsrfTokenRepository>> provides simple AngularJS & CSRF integration
|
* <<csrf-cookie,CookieCsrfTokenRepository>> provides simple AngularJS & CSRF integration
|
||||||
* Added `ForwardAuthenticationFailureHandler` & `ForwardAuthenticationSuccessHandler`
|
* Added `ForwardAuthenticationFailureHandler` & `ForwardAuthenticationSuccessHandler`
|
||||||
|
* <<mvc-authentication-principal,AuthenticationPrincipal>> supports expression attribute to support transforming the `Authentication.getPrincipal()` object (i.e. handling immutable custom `User` domain objects)
|
||||||
|
|
||||||
=== Authorization Improvements
|
=== Authorization Improvements
|
||||||
* <<el-access-web-path-variables,Path Variables in Web Security Expressions>>
|
* <<el-access-web-path-variables,Path Variables in Web Security Expressions>>
|
||||||
|
@ -6630,6 +6631,36 @@ public ModelAndView findMessagesForUser(@AuthenticationPrincipal CustomUser cust
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
|
Sometimes it may be necessary to transform the principal in some way.
|
||||||
|
For example, if `CustomUser` needed to be final it could not be extended.
|
||||||
|
In this situation the `UserDetailsService` might returns an `Object` that implements `UserDetails` and provides a method named `getCustomUser` to access `CustomUser`.
|
||||||
|
For example, it might look like:
|
||||||
|
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
public class CustomUserUserDetails extends User {
|
||||||
|
// ...
|
||||||
|
public CustomUser getCustomUser() {
|
||||||
|
return customUser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
We could then access the `CustomUser` using a https://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html[SpEL expression] that uses `Authentication.getPrincipal()` as the root object:
|
||||||
|
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
@RequestMapping("/messages/inbox")
|
||||||
|
public ModelAndView findMessagesForUser(@AuthenticationPrincipal(expression = "customUser") CustomUser customUser) {
|
||||||
|
|
||||||
|
// .. find messags for this user and return them ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
We can further remove our dependency on Spring Security by making `@AuthenticationPrincipal` a meta annotation on our own annotation. Below we demonstrate how we could do this on an annotation named `@CurrentUser`.
|
We can further remove our dependency on Spring Security by making `@AuthenticationPrincipal` a meta annotation on our own annotation. Below we demonstrate how we could do this on an annotation named `@CurrentUser`.
|
||||||
|
|
||||||
NOTE: It is important to realize that in order to remove the dependency on Spring Security, it is the consuming application that would create `@CurrentUser`. This step is not strictly required, but assists in isolating your dependency to Spring Security to a more central location.
|
NOTE: It is important to realize that in order to remove the dependency on Spring Security, it is the consuming application that would create `@CurrentUser`. This step is not strictly required, but assists in isolating your dependency to Spring Security to a more central location.
|
||||||
|
|
|
@ -19,12 +19,17 @@ import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
|
import org.springframework.expression.Expression;
|
||||||
|
import org.springframework.expression.ExpressionParser;
|
||||||
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
import org.springframework.messaging.Message;
|
import org.springframework.messaging.Message;
|
||||||
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
|
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows resolving the {@link Authentication#getPrincipal()} using the
|
* Allows resolving the {@link Authentication#getPrincipal()} using the
|
||||||
|
@ -79,6 +84,8 @@ import org.springframework.stereotype.Controller;
|
||||||
public final class AuthenticationPrincipalArgumentResolver
|
public final class AuthenticationPrincipalArgumentResolver
|
||||||
implements HandlerMethodArgumentResolver {
|
implements HandlerMethodArgumentResolver {
|
||||||
|
|
||||||
|
private ExpressionParser parser = new SpelExpressionParser();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
|
@ -106,10 +113,22 @@ public final class AuthenticationPrincipalArgumentResolver
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Object principal = authentication.getPrincipal();
|
Object principal = authentication.getPrincipal();
|
||||||
|
|
||||||
|
AuthenticationPrincipal authPrincipal = findMethodAnnotation(
|
||||||
|
AuthenticationPrincipal.class, parameter);
|
||||||
|
|
||||||
|
String expressionToParse = authPrincipal.expression();
|
||||||
|
if (StringUtils.hasLength(expressionToParse)) {
|
||||||
|
StandardEvaluationContext context = new StandardEvaluationContext();
|
||||||
|
context.setRootObject(principal);
|
||||||
|
context.setVariable("this", principal);
|
||||||
|
|
||||||
|
Expression expression = this.parser.parseExpression(expressionToParse);
|
||||||
|
principal = expression.getValue(context);
|
||||||
|
}
|
||||||
|
|
||||||
if (principal != null
|
if (principal != null
|
||||||
&& !parameter.getParameterType().isAssignableFrom(principal.getClass())) {
|
&& !parameter.getParameterType().isAssignableFrom(principal.getClass())) {
|
||||||
AuthenticationPrincipal authPrincipal = findMethodAnnotation(
|
|
||||||
AuthenticationPrincipal.class, parameter);
|
|
||||||
if (authPrincipal.errorOnInvalidType()) {
|
if (authPrincipal.errorOnInvalidType()) {
|
||||||
throw new ClassCastException(principal + " is not assignable to "
|
throw new ClassCastException(principal + " is not assignable to "
|
||||||
+ parameter.getParameterType());
|
+ parameter.getParameterType());
|
||||||
|
|
|
@ -116,6 +116,24 @@ public class AuthenticationPrincipalArgumentResolverTests {
|
||||||
expectedPrincipal);
|
expectedPrincipal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveArgumentSpel() throws Exception {
|
||||||
|
CustomUserPrincipal principal = new CustomUserPrincipal();
|
||||||
|
setAuthenticationPrincipal(principal);
|
||||||
|
this.expectedPrincipal = principal.property;
|
||||||
|
assertThat(this.resolver.resolveArgument(showUserSpel(), null))
|
||||||
|
.isEqualTo(this.expectedPrincipal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveArgumentSpelCopy() throws Exception {
|
||||||
|
CopyUserPrincipal principal = new CopyUserPrincipal("property");
|
||||||
|
setAuthenticationPrincipal(principal);
|
||||||
|
Object resolveArgument = this.resolver.resolveArgument(showUserSpelCopy(), null);
|
||||||
|
assertThat(resolveArgument).isEqualTo(principal);
|
||||||
|
assertThat(resolveArgument).isNotSameAs(principal);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveArgumentNullOnInvalidType() throws Exception {
|
public void resolveArgumentNullOnInvalidType() throws Exception {
|
||||||
setAuthenticationPrincipal(new CustomUserPrincipal());
|
setAuthenticationPrincipal(new CustomUserPrincipal());
|
||||||
|
@ -170,6 +188,14 @@ public class AuthenticationPrincipalArgumentResolverTests {
|
||||||
return getMethodParameter("showUserCustomAnnotation", CustomUserPrincipal.class);
|
return getMethodParameter("showUserCustomAnnotation", CustomUserPrincipal.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MethodParameter showUserSpel() {
|
||||||
|
return getMethodParameter("showUserSpel", String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodParameter showUserSpelCopy() {
|
||||||
|
return getMethodParameter("showUserSpelCopy", CopyUserPrincipal.class);
|
||||||
|
}
|
||||||
|
|
||||||
private MethodParameter showUserAnnotationObject() {
|
private MethodParameter showUserAnnotationObject() {
|
||||||
return getMethodParameter("showUserAnnotation", Object.class);
|
return getMethodParameter("showUserAnnotation", Object.class);
|
||||||
}
|
}
|
||||||
|
@ -218,9 +244,62 @@ public class AuthenticationPrincipalArgumentResolverTests {
|
||||||
|
|
||||||
public void showUserAnnotation(@AuthenticationPrincipal Object user) {
|
public void showUserAnnotation(@AuthenticationPrincipal Object user) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void showUserSpel(
|
||||||
|
@AuthenticationPrincipal(expression = "property") String user) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showUserSpelCopy(
|
||||||
|
@AuthenticationPrincipal(expression = "new org.springframework.security.messaging.context.AuthenticationPrincipalArgumentResolverTests$CopyUserPrincipal(#this)") CopyUserPrincipal user) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class CustomUserPrincipal {
|
static class CustomUserPrincipal {
|
||||||
|
public final String property = "property";
|
||||||
|
}
|
||||||
|
|
||||||
|
static class CopyUserPrincipal {
|
||||||
|
public final String property;
|
||||||
|
|
||||||
|
CopyUserPrincipal(String property) {
|
||||||
|
this.property = property;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CopyUserPrincipal(CopyUserPrincipal toCopy) {
|
||||||
|
this.property = toCopy.property;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result
|
||||||
|
+ ((this.property == null) ? 0 : this.property.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CopyUserPrincipal other = (CopyUserPrincipal) obj;
|
||||||
|
if (this.property == null) {
|
||||||
|
if (other.property != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!this.property.equals(other.property)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setAuthenticationPrincipal(Object principal) {
|
private void setAuthenticationPrincipal(Object principal) {
|
||||||
|
|
|
@ -19,10 +19,15 @@ import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
|
import org.springframework.expression.Expression;
|
||||||
|
import org.springframework.expression.ExpressionParser;
|
||||||
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||||
import org.springframework.web.context.request.NativeWebRequest;
|
import org.springframework.web.context.request.NativeWebRequest;
|
||||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||||
|
@ -81,6 +86,8 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
public final class AuthenticationPrincipalArgumentResolver
|
public final class AuthenticationPrincipalArgumentResolver
|
||||||
implements HandlerMethodArgumentResolver {
|
implements HandlerMethodArgumentResolver {
|
||||||
|
|
||||||
|
private ExpressionParser parser = new SpelExpressionParser();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
|
@ -109,10 +116,23 @@ public final class AuthenticationPrincipalArgumentResolver
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Object principal = authentication.getPrincipal();
|
Object principal = authentication.getPrincipal();
|
||||||
|
|
||||||
|
AuthenticationPrincipal authPrincipal = findMethodAnnotation(
|
||||||
|
AuthenticationPrincipal.class, parameter);
|
||||||
|
|
||||||
|
String expressionToParse = authPrincipal.expression();
|
||||||
|
if (StringUtils.hasLength(expressionToParse)) {
|
||||||
|
StandardEvaluationContext context = new StandardEvaluationContext();
|
||||||
|
context.setRootObject(principal);
|
||||||
|
context.setVariable("this", principal);
|
||||||
|
|
||||||
|
Expression expression = this.parser.parseExpression(expressionToParse);
|
||||||
|
principal = expression.getValue(context);
|
||||||
|
}
|
||||||
|
|
||||||
if (principal != null
|
if (principal != null
|
||||||
&& !parameter.getParameterType().isAssignableFrom(principal.getClass())) {
|
&& !parameter.getParameterType().isAssignableFrom(principal.getClass())) {
|
||||||
AuthenticationPrincipal authPrincipal = findMethodAnnotation(
|
|
||||||
AuthenticationPrincipal.class, parameter);
|
|
||||||
if (authPrincipal.errorOnInvalidType()) {
|
if (authPrincipal.errorOnInvalidType()) {
|
||||||
throw new ClassCastException(principal + " is not assignable to "
|
throw new ClassCastException(principal + " is not assignable to "
|
||||||
+ parameter.getParameterType());
|
+ parameter.getParameterType());
|
||||||
|
|
|
@ -119,6 +119,25 @@ public class AuthenticationPrincipalArgumentResolverTests {
|
||||||
.isEqualTo(expectedPrincipal);
|
.isEqualTo(expectedPrincipal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveArgumentSpel() throws Exception {
|
||||||
|
CustomUserPrincipal principal = new CustomUserPrincipal();
|
||||||
|
setAuthenticationPrincipal(principal);
|
||||||
|
this.expectedPrincipal = principal.property;
|
||||||
|
assertThat(this.resolver.resolveArgument(showUserSpel(), null, null, null))
|
||||||
|
.isEqualTo(this.expectedPrincipal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveArgumentSpelCopy() throws Exception {
|
||||||
|
CopyUserPrincipal principal = new CopyUserPrincipal("property");
|
||||||
|
setAuthenticationPrincipal(principal);
|
||||||
|
Object resolveArgument = this.resolver.resolveArgument(showUserSpelCopy(), null,
|
||||||
|
null, null);
|
||||||
|
assertThat(resolveArgument).isEqualTo(principal);
|
||||||
|
assertThat(resolveArgument).isNotSameAs(principal);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveArgumentNullOnInvalidType() throws Exception {
|
public void resolveArgumentNullOnInvalidType() throws Exception {
|
||||||
setAuthenticationPrincipal(new CustomUserPrincipal());
|
setAuthenticationPrincipal(new CustomUserPrincipal());
|
||||||
|
@ -175,6 +194,14 @@ public class AuthenticationPrincipalArgumentResolverTests {
|
||||||
return getMethodParameter("showUserCustomAnnotation", CustomUserPrincipal.class);
|
return getMethodParameter("showUserCustomAnnotation", CustomUserPrincipal.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MethodParameter showUserSpel() {
|
||||||
|
return getMethodParameter("showUserSpel", String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodParameter showUserSpelCopy() {
|
||||||
|
return getMethodParameter("showUserSpelCopy", CopyUserPrincipal.class);
|
||||||
|
}
|
||||||
|
|
||||||
private MethodParameter showUserAnnotationObject() {
|
private MethodParameter showUserAnnotationObject() {
|
||||||
return getMethodParameter("showUserAnnotation", Object.class);
|
return getMethodParameter("showUserAnnotation", Object.class);
|
||||||
}
|
}
|
||||||
|
@ -223,9 +250,62 @@ public class AuthenticationPrincipalArgumentResolverTests {
|
||||||
|
|
||||||
public void showUserAnnotation(@AuthenticationPrincipal Object user) {
|
public void showUserAnnotation(@AuthenticationPrincipal Object user) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void showUserSpel(
|
||||||
|
@AuthenticationPrincipal(expression = "property") String user) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showUserSpelCopy(
|
||||||
|
@AuthenticationPrincipal(expression = "new org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolverTests$CopyUserPrincipal(#this)") CopyUserPrincipal user) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class CustomUserPrincipal {
|
static class CustomUserPrincipal {
|
||||||
|
public final String property = "property";
|
||||||
|
}
|
||||||
|
|
||||||
|
static class CopyUserPrincipal {
|
||||||
|
public final String property;
|
||||||
|
|
||||||
|
CopyUserPrincipal(String property) {
|
||||||
|
this.property = property;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CopyUserPrincipal(CopyUserPrincipal toCopy) {
|
||||||
|
this.property = toCopy.property;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result
|
||||||
|
+ ((this.property == null) ? 0 : this.property.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CopyUserPrincipal other = (CopyUserPrincipal) obj;
|
||||||
|
if (this.property == null) {
|
||||||
|
if (other.property != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!this.property.equals(other.property)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setAuthenticationPrincipal(Object principal) {
|
private void setAuthenticationPrincipal(Object principal) {
|
||||||
|
|
Loading…
Reference in New Issue