2016-07-20 5 views
4

У меня есть конечная точка RESTful, которую я защитил простой проверкой полномочий с помощью настраиваемого ContainerRequestFilter. Фильтр проверяет, является ли вся информация, содержащаяся в HTTP-сессии является правильным, и если не он выполняет это:Джерси бросает NPE для каждого вызова после использования ContainerRequestContext.abortWith()

requestContext.abortWith(Response.status(Response.Status.FORBIDDEN) 
.entity("Forbidden").build()); 

Это все хорошо и денди. Странно то, что когда я снова делаю тот же запрос GET, сервер Джерси сообщает NPE и ничего не возвращает.

NPE StackTrace:

Jul 20, 2016 5:27:53 PM org.glassfish.jersey.server.ServerRuntime$Responder writeResponse 
SEVERE: An I/O error has occurred while writing a response message entity to the container output stream. 
java.lang.IllegalStateException: The output stream has already been closed. 
    at org.glassfish.jersey.message.internal.CommittingOutputStream.setStreamProvider(CommittingOutputStream.java:147) 
    at org.glassfish.jersey.message.internal.OutboundMessageContext.setStreamProvider(OutboundMessageContext.java:803) 
    at org.glassfish.jersey.server.ContainerResponse.setStreamProvider(ContainerResponse.java:372) 
    at org.glassfish.jersey.server.ServerRuntime$Responder.writeResponse(ServerRuntime.java:694) 
    at org.glassfish.jersey.server.ServerRuntime$Responder.processResponse(ServerRuntime.java:444) 
    at org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.java:434) 
    at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:329) 
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271) 
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267) 
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315) 
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297) 
    at org.glassfish.jersey.internal.Errors.process(Errors.java:267) 
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317) 
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305) 
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154) 
    at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer.service(GrizzlyHttpContainer.java:384) 
    at org.glassfish.grizzly.http.server.HttpHandler$1.run(HttpHandler.java:224) 
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:591) 
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:571) 
    at java.lang.Thread.run(Thread.java:745) 

Что происходит? Я не хочу закрывать выходной поток. Мне нужен только способ, чтобы вернуть код возврата http + обратно к запрашивающему.

ответ

15

В случае, если кто-либо столкнется с тем же вопросом, у меня был ответ: вы не можете повторно использовать ответы! Экземпляр писателя потока потока связан с запросом, и как только вы его используете в abortWith(), поток будет передаваться навсегда. По существу, он больше не может использоваться для отправки ответов.

Вам необходимо инициализировать новый запрос с помощью метода ().

простых слов, не следует использовать такие конструкции:

private static final Response ACCESS_FORBIDDEN = Response.status(Response.Status.FORBIDDEN) 
      .entity("Access blocked for all users !!").build(); 

Кстати, я Пикен этого сломанного куска кода здесь: http://howtodoinjava.com/jersey/jersey-rest-security/ Так что будьте осторожны при выполнении этой RequestFilter.

+2

Вы можете повторно использовать ResponseBuilder и вызвать метод build() для него для каждого запроса. –

+1

lol, сделал этот точный точный учебник некоторое время назад и в конце концов попал сюда – svarog

0

Я полностью согласен. Я нашел этот образец от RESTful Java with JAX-RS 2.0, 2nd Edition, который я нашел полезным после обеда копания.

import javax.ws.rs.container.ContainerRequestFilter; 
import javax.ws.rs.container.ContainerRequestContext; 
import javax.ws.rs.NotAuthorizedException; 

@Provider 
@PreMatching 

public class BearerTokenFilter implements ContainerRequestFilter { 
    public void filter(ContainerRequestContext ctx) throws IOException { 
    String authHeader = request.getHeaderString(HttpHeaders.AUTHORIZATION); 
    if (authHeader == null) throw new NotAuthorizedException("Bearer"); 
    String token = parseToken(authHeader); 
    if (verifyToken(token) == false) { 

     throw new NotAuthorizedException("Bearer error=\"invalid_token\""); 
    } 
} 

private String parseToken(String header) {...} 
private boolean verifyToken(String token) {...} 
} 

«В этом примере, если нет заголовка авторизации или он является недействительным, то запрос будет прервана с NotAuthorizedException. Клиент получает 401 ответ с WWW-Authenticate заголовка устанавливается равным значение, переданное в конструктор NotAuthorizedException.Если вы хотите избежать сопоставления исключений, тогда вы можете использовать метод ContainerRequestContext.abortWith(). Однако, как правило, я предпочитаю исключать исключения ».

Я думаю, поэтому автор предпочел исключать исключения.

1

Как уже упоминалось, проблема заключается в статическом ACCESS_DENIED Response, который уже был установлен.

private static final Response ACCESS_DENIED = Response.status(Response.Status.UNAUTHORIZED).entity("Accesso não Autorizado").build(); 

Так заменить:

requestContext.abortWith(ACCESS_DENIED)

по

requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity("Accesso não Autorizado").build()); 
1

Вам не нужно использовать requestContext.abortWith() внутри метода фильтра. Вместо этого сделайте следующее.

String authCredentials = containerRequest.getHeaderString(HttpHeaders.AUTHORIZATION); 

Заголовок отправляется с вашего клиента REST [/ почтальона/SoapUI/клиентской программы джерси браузера] будет иметь формат "Basic encodedString". Кодированная строка может быть легко декодирована с использованием java.util.Base64. После его декодирования на конце сервера строка будет иметь формат username: password. Используйте String.split(":"); для разделения имени пользователя и пароля. Перейдите к проверке. Если валидация становится ложной, отправьте WebApplicationException из фильтра фильтра ContainerRequestFilter, который вы здесь переопределяете.

 Смежные вопросы

  • Нет связанных вопросов^_^