Browse Source

Default resolution of non-annotated Principal argument

Closes gh-25981
pull/25999/head
Rossen Stoyanchev 4 years ago
parent
commit
141c79bc8f
  1. 68
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/PrincipalMethodArgumentResolver.java
  2. 1
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java
  3. 4
      spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletRequestMethodArgumentResolver.java
  4. 110
      spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/PrincipalMethodArgumentResolverTests.java

68
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/PrincipalMethodArgumentResolver.java

@ -0,0 +1,68 @@ @@ -0,0 +1,68 @@
/*
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.mvc.method.annotation;
import java.security.Principal;
import javax.servlet.http.HttpServletRequest;
import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* Resolves an argument of type {@link Principal}, similar to
* {@link ServletRequestMethodArgumentResolver} but irrespective of whether the
* argument is annotated or not. This is doen to enable custom argument
* resolution of a {@link Principal} argument (with custom annotation).
*
* @author Rossen Stoyanchev
* @since 5.3.1
*/
public class PrincipalMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
return Principal.class.isAssignableFrom(paramType);
}
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Class<?> paramType = parameter.getParameterType();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request == null) {
throw new IllegalStateException(
"Current request is not of type [HttpServletRequest]: " + webRequest);
}
Principal principal = request.getUserPrincipal();
if (principal != null && !paramType.isInstance(principal)) {
throw new IllegalStateException(
"Current user principal is not of type [" + paramType.getName() + "]: " + principal);
}
return principal;
}
}

1
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java

@ -715,6 +715,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter @@ -715,6 +715,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
}
// Catch-all
resolvers.add(new PrincipalMethodArgumentResolver());
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
return resolvers;

4
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletRequestMethodArgumentResolver.java

@ -50,7 +50,9 @@ import org.springframework.web.servlet.support.RequestContextUtils; @@ -50,7 +50,9 @@ import org.springframework.web.servlet.support.RequestContextUtils;
* <li>{@link MultipartRequest}
* <li>{@link HttpSession}
* <li>{@link PushBuilder} (as of Spring 5.0 on Servlet 4.0)
* <li>{@link Principal}
* <li>{@link Principal} but only if not annotated in order to allow custom
* resolvers to resolve it, and the falling back on
* {@link PrincipalMethodArgumentResolver}.
* <li>{@link InputStream}
* <li>{@link Reader}
* <li>{@link HttpMethod} (as of Spring 4.0)

110
spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/PrincipalMethodArgumentResolverTests.java

@ -0,0 +1,110 @@ @@ -0,0 +1,110 @@
/*
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.mvc.method.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.security.Principal;
import javax.servlet.ServletRequest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.core.MethodParameter;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Unit tests for {@link PrincipalMethodArgumentResolver}.
*
* @author Rossen Stoyanchev
*/
public class PrincipalMethodArgumentResolverTests {
private PrincipalMethodArgumentResolver resolver;
private ModelAndViewContainer mavContainer;
private MockHttpServletRequest servletRequest;
private ServletWebRequest webRequest;
private Method method;
@BeforeEach
public void setup() throws Exception {
resolver = new PrincipalMethodArgumentResolver();
mavContainer = new ModelAndViewContainer();
servletRequest = new MockHttpServletRequest("GET", "");
webRequest = new ServletWebRequest(servletRequest, new MockHttpServletResponse());
method = getClass().getMethod("supportedParams", ServletRequest.class, Principal.class);
}
@Test
public void principal() throws Exception {
Principal principal = () -> "Foo";
servletRequest.setUserPrincipal(principal);
MethodParameter principalParameter = new MethodParameter(method, 1);
assertThat(resolver.supportsParameter(principalParameter)).as("Principal not supported").isTrue();
Object result = resolver.resolveArgument(principalParameter, null, webRequest, null);
assertThat(result).as("Invalid result").isSameAs(principal);
}
@Test
public void principalAsNull() throws Exception {
MethodParameter principalParameter = new MethodParameter(method, 1);
assertThat(resolver.supportsParameter(principalParameter)).as("Principal not supported").isTrue();
Object result = resolver.resolveArgument(principalParameter, null, webRequest, null);
assertThat(result).as("Invalid result").isNull();
}
@Test // gh-25780
public void annotatedPrincipal() throws Exception {
Principal principal = () -> "Foo";
servletRequest.setUserPrincipal(principal);
Method principalMethod = getClass().getMethod("supportedParamsWithAnnotatedPrincipal", Principal.class);
MethodParameter principalParameter = new MethodParameter(principalMethod, 0);
assertThat(resolver.supportsParameter(principalParameter)).isTrue();
}
@SuppressWarnings("unused")
public void supportedParams(ServletRequest p0, Principal p1) {}
@Target({ ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthenticationPrincipal {}
@SuppressWarnings("unused")
public void supportedParamsWithAnnotatedPrincipal(@AuthenticationPrincipal Principal p) {}
}
Loading…
Cancel
Save