将 SecurityContext 信息和请求主体注入 @BeanParam
Inject SecurityContext info and Request Body into @BeanParam
目前我正在 MessageBodyReader
中渲染命令对象,但我希望能够在 @BeanParam
中执行此操作:
注入一个派生自 SecurityContext
的字段(转换中有什么地方可以挂钩吗?)。
有一个字段注入已被 MessageBodyReader
实现。
这可能吗?
注意:转到更新。我猜 是 可以使用 @BeanParam
。尽管您需要将 SecurityContext
注入 bean 并提取名称信息。
无法通过 @BeanParam
已更正 实现此目的。您 可以 按您的方式使用 MessageBodyReader
,但在我看来,这更像是一种技巧。相反,我实现它的方法是按照它们应该使用的方式使用框架组件,这涉及自定义参数注入。
要实现这一点,您需要两件事,一个 ValueFactoryProvider
来提供参数值,一个 InjectionResolver
带有您自己的自定义注释。我不会对下面的示例做太多解释,但是您可以在
中找到很好的解释
您可以 运行 下面的示例就像任何 JUnit 测试一样。一切都包含在一个 class 中。这些是我使用的依赖项。
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.19</version>
<scope>test</scope>
</dependency>
这是测试
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.security.Principal;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Entity;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.api.TypeLiteral;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory;
import org.glassfish.jersey.server.internal.inject.AbstractValueFactoryProvider;
import org.glassfish.jersey.server.internal.inject.MultivaluedParameterExtractorProvider;
import org.glassfish.jersey.server.internal.inject.ParamInjectionResolver;
import org.glassfish.jersey.server.model.Parameter;
import org.glassfish.jersey.server.spi.internal.ValueFactoryProvider;
import org.glassfish.jersey.test.JerseyTest;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class CustomInjectionTest extends JerseyTest {
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public static @interface CustomParam {
}
public static class CustomModel {
public String name;
public RequestBody body;
}
public static class RequestBody {
public String message;
}
public static class CustomParamValueFactory
extends AbstractContainerRequestValueFactory<CustomModel> {
@Override
public CustomModel provide() {
ContainerRequest request = getContainerRequest();
String name = request.getSecurityContext().getUserPrincipal().getName();
RequestBody body = request.readEntity(RequestBody.class);
CustomModel model = new CustomModel();
model.body = body;
model.name = name;
return model;
}
}
public static class CustomValueFactoryProvider extends AbstractValueFactoryProvider {
@Inject
public CustomValueFactoryProvider(MultivaluedParameterExtractorProvider multiProvider,
ServiceLocator locator) {
super(multiProvider, locator, Parameter.Source.UNKNOWN);
}
@Override
protected Factory<?> createValueFactory(Parameter parameter) {
if (CustomModel.class == parameter.getType()
&& parameter.isAnnotationPresent(CustomParam.class)) {
return new CustomParamValueFactory();
}
return null;
}
}
public static class CustomParamInjectionResolver extends ParamInjectionResolver<CustomParam> {
public CustomParamInjectionResolver() {
super(CustomValueFactoryProvider.class);
}
}
private static class CustomInjectBinder extends AbstractBinder {
@Override
protected void configure() {
bind(CustomValueFactoryProvider.class)
.to(ValueFactoryProvider.class)
.in(Singleton.class);
bind(CustomParamInjectionResolver.class)
.to(new TypeLiteral<InjectionResolver<CustomParam>>(){})
.in(Singleton.class);
}
}
private static final String PRINCIPAL_NAME = "peeskillet";
@PreMatching
public static class SecurityContextFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestContext.setSecurityContext(new SecurityContext(){
public Principal getUserPrincipal() {
return new Principal() {
public String getName() { return PRINCIPAL_NAME; }
};
}
public boolean isUserInRole(String role) { return false; }
public boolean isSecure() { return true;}
public String getAuthenticationScheme() { return null; }
});
}
}
@Path("test")
public static class TestResource {
@POST
@Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.APPLICATION_JSON)
public String post(@CustomParam CustomModel model) {
return model.name + ":" + model.body.message;
}
}
@Override
public ResourceConfig configure() {
return new ResourceConfig(TestResource.class)
.register(SecurityContextFilter.class)
.register(new CustomInjectBinder());
}
@Test
public void should_return_name_with_body() {
RequestBody body = new RequestBody();
body.message = "Hello World";
Response response = target("test").request()
.post(Entity.json(body));
assertEquals(200, response.getStatus());
String responseBody = response.readEntity(String.class);
assertEquals(PRINCIPAL_NAME + ":" + body.message, responseBody);
System.out.println(responseBody);
}
}
请注意,我从 CustomParamValueFactory
中的 ContainerRequest
读取了请求正文。 RequestBody
与我在 @Test
.
中的请求中发送的 JSON 相同
更新
令我惊讶的是, 可以使用 @BeanParam
。这是我用来测试的以下 bean
public static class CustomModel {
@Context
public SecurityContext securityContext;
public RequestBody body;
}
public static class RequestBody {
public String message;
}
与之前测试的不同之处在于,我们需要注入整个 SecurityContext
,而不是 SecurityContext.Principal
中的 name
。注入无法从 Principal
中获取名称,所以我们将手动完成。
最让我吃惊的是,我们能够注入 RequestBody
实体。我不知道这是可能的。
这是完整的测试
import java.io.IOException;
import java.security.Principal;
import javax.ws.rs.BeanParam;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Entity;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class CustomInjectTestTake2 extends JerseyTest {
private static final String PRINCIPAL_NAME = "peeskillet";
private static final String MESSAGE = "Hello World";
private static final String RESPONSE = PRINCIPAL_NAME + ":" + MESSAGE;
public static class CustomModel {
@Context
public SecurityContext securityContext;
public RequestBody body;
}
public static class RequestBody {
public String message;
}
@PreMatching
public static class SecurityContextFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestContext.setSecurityContext(new SecurityContext(){
public Principal getUserPrincipal() {
return new Principal() {
public String getName() { return PRINCIPAL_NAME; }
};
}
public boolean isUserInRole(String role) { return false; }
public boolean isSecure() { return true;}
public String getAuthenticationScheme() { return null; }
});
}
}
@Path("test")
public static class TestResource {
@POST
@Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.APPLICATION_JSON)
public String post(@BeanParam CustomModel model) {
return model.securityContext.getUserPrincipal().getName()
+ ":" + model.body.message;
}
}
@Override
public ResourceConfig configure() {
return new ResourceConfig(TestResource.class)
.register(SecurityContextFilter.class);
}
@Test
public void should_return_name_with_body() {
RequestBody body = new RequestBody();
body.message = "Hello World";
Response response = target("test").request()
.post(Entity.json(body));
assertEquals(200, response.getStatus());
String responseBody = response.readEntity(String.class);
assertEquals(RESPONSE, responseBody);
System.out.println(responseBody);
}
}
另请参阅:
目前我正在 MessageBodyReader
中渲染命令对象,但我希望能够在 @BeanParam
中执行此操作:
注入一个派生自
SecurityContext
的字段(转换中有什么地方可以挂钩吗?)。有一个字段注入已被
MessageBodyReader
实现。
这可能吗?
注意:转到更新。我猜 是 可以使用 @BeanParam
。尽管您需要将 SecurityContext
注入 bean 并提取名称信息。
无法通过 已更正 实现此目的。您 可以 按您的方式使用 @BeanParam
MessageBodyReader
,但在我看来,这更像是一种技巧。相反,我实现它的方法是按照它们应该使用的方式使用框架组件,这涉及自定义参数注入。
要实现这一点,您需要两件事,一个 ValueFactoryProvider
来提供参数值,一个 InjectionResolver
带有您自己的自定义注释。我不会对下面的示例做太多解释,但是您可以在
您可以 运行 下面的示例就像任何 JUnit 测试一样。一切都包含在一个 class 中。这些是我使用的依赖项。
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.19</version>
<scope>test</scope>
</dependency>
这是测试
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.security.Principal;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Entity;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.api.TypeLiteral;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory;
import org.glassfish.jersey.server.internal.inject.AbstractValueFactoryProvider;
import org.glassfish.jersey.server.internal.inject.MultivaluedParameterExtractorProvider;
import org.glassfish.jersey.server.internal.inject.ParamInjectionResolver;
import org.glassfish.jersey.server.model.Parameter;
import org.glassfish.jersey.server.spi.internal.ValueFactoryProvider;
import org.glassfish.jersey.test.JerseyTest;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class CustomInjectionTest extends JerseyTest {
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public static @interface CustomParam {
}
public static class CustomModel {
public String name;
public RequestBody body;
}
public static class RequestBody {
public String message;
}
public static class CustomParamValueFactory
extends AbstractContainerRequestValueFactory<CustomModel> {
@Override
public CustomModel provide() {
ContainerRequest request = getContainerRequest();
String name = request.getSecurityContext().getUserPrincipal().getName();
RequestBody body = request.readEntity(RequestBody.class);
CustomModel model = new CustomModel();
model.body = body;
model.name = name;
return model;
}
}
public static class CustomValueFactoryProvider extends AbstractValueFactoryProvider {
@Inject
public CustomValueFactoryProvider(MultivaluedParameterExtractorProvider multiProvider,
ServiceLocator locator) {
super(multiProvider, locator, Parameter.Source.UNKNOWN);
}
@Override
protected Factory<?> createValueFactory(Parameter parameter) {
if (CustomModel.class == parameter.getType()
&& parameter.isAnnotationPresent(CustomParam.class)) {
return new CustomParamValueFactory();
}
return null;
}
}
public static class CustomParamInjectionResolver extends ParamInjectionResolver<CustomParam> {
public CustomParamInjectionResolver() {
super(CustomValueFactoryProvider.class);
}
}
private static class CustomInjectBinder extends AbstractBinder {
@Override
protected void configure() {
bind(CustomValueFactoryProvider.class)
.to(ValueFactoryProvider.class)
.in(Singleton.class);
bind(CustomParamInjectionResolver.class)
.to(new TypeLiteral<InjectionResolver<CustomParam>>(){})
.in(Singleton.class);
}
}
private static final String PRINCIPAL_NAME = "peeskillet";
@PreMatching
public static class SecurityContextFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestContext.setSecurityContext(new SecurityContext(){
public Principal getUserPrincipal() {
return new Principal() {
public String getName() { return PRINCIPAL_NAME; }
};
}
public boolean isUserInRole(String role) { return false; }
public boolean isSecure() { return true;}
public String getAuthenticationScheme() { return null; }
});
}
}
@Path("test")
public static class TestResource {
@POST
@Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.APPLICATION_JSON)
public String post(@CustomParam CustomModel model) {
return model.name + ":" + model.body.message;
}
}
@Override
public ResourceConfig configure() {
return new ResourceConfig(TestResource.class)
.register(SecurityContextFilter.class)
.register(new CustomInjectBinder());
}
@Test
public void should_return_name_with_body() {
RequestBody body = new RequestBody();
body.message = "Hello World";
Response response = target("test").request()
.post(Entity.json(body));
assertEquals(200, response.getStatus());
String responseBody = response.readEntity(String.class);
assertEquals(PRINCIPAL_NAME + ":" + body.message, responseBody);
System.out.println(responseBody);
}
}
请注意,我从 CustomParamValueFactory
中的 ContainerRequest
读取了请求正文。 RequestBody
与我在 @Test
.
更新
令我惊讶的是, 可以使用 @BeanParam
。这是我用来测试的以下 bean
public static class CustomModel {
@Context
public SecurityContext securityContext;
public RequestBody body;
}
public static class RequestBody {
public String message;
}
与之前测试的不同之处在于,我们需要注入整个 SecurityContext
,而不是 SecurityContext.Principal
中的 name
。注入无法从 Principal
中获取名称,所以我们将手动完成。
最让我吃惊的是,我们能够注入 RequestBody
实体。我不知道这是可能的。
这是完整的测试
import java.io.IOException;
import java.security.Principal;
import javax.ws.rs.BeanParam;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Entity;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class CustomInjectTestTake2 extends JerseyTest {
private static final String PRINCIPAL_NAME = "peeskillet";
private static final String MESSAGE = "Hello World";
private static final String RESPONSE = PRINCIPAL_NAME + ":" + MESSAGE;
public static class CustomModel {
@Context
public SecurityContext securityContext;
public RequestBody body;
}
public static class RequestBody {
public String message;
}
@PreMatching
public static class SecurityContextFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestContext.setSecurityContext(new SecurityContext(){
public Principal getUserPrincipal() {
return new Principal() {
public String getName() { return PRINCIPAL_NAME; }
};
}
public boolean isUserInRole(String role) { return false; }
public boolean isSecure() { return true;}
public String getAuthenticationScheme() { return null; }
});
}
}
@Path("test")
public static class TestResource {
@POST
@Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.APPLICATION_JSON)
public String post(@BeanParam CustomModel model) {
return model.securityContext.getUserPrincipal().getName()
+ ":" + model.body.message;
}
}
@Override
public ResourceConfig configure() {
return new ResourceConfig(TestResource.class)
.register(SecurityContextFilter.class);
}
@Test
public void should_return_name_with_body() {
RequestBody body = new RequestBody();
body.message = "Hello World";
Response response = target("test").request()
.post(Entity.json(body));
assertEquals(200, response.getStatus());
String responseBody = response.readEntity(String.class);
assertEquals(RESPONSE, responseBody);
System.out.println(responseBody);
}
}
另请参阅: