我想在我的Jax-RS服务标头中包含一个身份验证令牌,但我不想在每个请求中添加一个参数来获取标头并检查它:
public Response getUser(@Context HttpHeaders headers) {
if(authorize(headers.getRequestHeader("token").get(0)) {
// Do something
}
}
我更愿意为每个请求添加一个属性(如果可能的话,甚至是类:
@Authorize
public Response getUser() {
// Do something
}
这样我也可以只将属性添加到我想要的请求中。
如果请求未经授权,我可以覆盖它并返回401。
自定义属性很容易编写,但是如何在不传递属性的情况下获取属性中的标题信息?
注意:我宁愿不使用web.xml。我现在没有,我不喜欢使用它们。我希望在没有xml的情况下保持我的代码干净,我想如果我使用过滤器/ web.xml它将适用于所有调用。如果这是唯一的方法,我会,但我更喜欢使用自定义属性的方法。
答案 0 :(得分:2)
"我想如果我使用过滤器/ web.xml,它将适用于所有来电"
实际上我们可以使用@NameBinding
个注释。例如
@NameBinding
@Rentention(RetentionPoilicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Authorize {}
然后只需注释过滤器和要过滤的方法/类。
@Authorize
public Response getUser() {
// Do something
}
@Provider
@Authorize
@Priority(Priorities.AUTHORIZATION)
public class AuthorizationRequestFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext)
throws IOException {
MultivauledMap<String, String> headers - requestContext.getHeaders();
...
if (!authorized) {
throw new NotAuthorizedException();
}
}
}
请注意@Priority
的使用。这个很重要。假设您也想要进行身份验证,因此您可以创建用于身份验证的过滤器。如果您没有设置优先级,则可以先进行过滤。这是不可预测的。如果我们使用@Priority(Priorities.AUTHENTICATION)
提供身份验证过滤器,则该过滤器将始终位于@Priority(Priorities.AUTHORIZATION)
过滤器之前。
您还需要将此过滤器注册到Application
子类(请参阅其他Deployment Options (Jersey, but the Application subclass is portable with implementations))
@ApplicationPath("/api")
public class YourApplication extends Application {
private Set<Class<?>> classes = new HashSet<>();
private Set<Object> singletons = new HashSet<>();
public YourApplication() {
classes.add(AuthorizationRequestFilter.class);
}
@Override
public Set<Class<?>> getClasses() {
return classes;
}
@Override
public Set<Object> singletons() {
return singletons;
}
}
答案 1 :(得分:1)
解决用例的最佳方法是使用名称绑定和过滤器。通过这种方式,您可以使用过滤器来执行授权逻辑,并在未经授权的请求的情况下返回401。
您可以找到更多信息here。
Name binding via annotations is only supported as part of the Server API. In name binding, a name-binding annotation is first defined using the @NameBinding meta-annotation:
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(value = RetentionPolicy.RUNTIME)
@NameBinding
public @interface Logged { }
The defined name-binding annotation is then used to decorate a filter or interceptor class (more than one filter or interceptor may be decorated with the same name-binding annotation):
@Logged
public class LoggingFilter
implements ContainerRequestFilter, ContainerResponseFilter {
...
}
At last, the name-binding annotation is applied to the resource method(s) to which the name-bound JAX-RS provider(s) should be bound to:
@Path("/")
public class MyResourceClass {
@GET
@Produces("text/plain")
@Path("{name}")
@Logged
public String hello(@PathParam("name") String name) {
return "Hello " + name;
}
}
A name-binding annotation may also be attached to a custom JAX-RS Application subclass. In such case a name-bound JAX-RS provider bound by the annotation will be applied to all resource and sub-resource methods in the JAX-RS application:
@Logged
@ApplicationPath("myApp")
public class MyApplication extends javax.ws.rs.core.Application {
...
}
答案 2 :(得分:1)
基于peeskillet的回答,概念是正确的,但代码有些错误,这是答案的最终代码。
使用@NameBinding,这有效:
<强> Authorize.java 强>
@NameBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Authorize {
}
<强> AuthorizeFilter.java 强>
注意:仍然需要进行实际的令牌授权。这只是检查令牌是否存在。
@Provider
@Authorize
@Priority(Priorities.AUTHORIZATION)
public class AuthorizeFilter implements ContainerRequestFilter
{
@Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
MultivaluedMap<String, String> headers = requestContext.getHeaders();
String token = headers.getFirst("token");
if (token == null || token.isEmpty()) {
Response.ResponseBuilder responseBuilder = Response
.status(Response.Status.UNAUTHORIZED)
.type(MediaType.APPLICATION_JSON)
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Credentials", "true")
.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
.header("Access-Control-Max-Age", "1209600");
requestContext.abortWith(responseBuilder.build());
}
}
}
<强> ApplicationConfig.java 强>
注意:在此添加过滤器,因此不必包含在web.xml中
@ApplicationScoped
@ApplicationPath("/api")
public class ApplicationConfig extends Application
{
@Override
public Set<Class<?>> getClasses()
{
return getRestResourceClasses();
}
private Set<Class<?>> getRestResourceClasses()
{
Set<Class<?>> resources = new java.util.HashSet<Class<?>>();
resources.add(com.example.AuthorizeFilter.class);
resources.add(com.example.UserService.class);
return resources;
}
}