2015-08-18 15 views
8

Muszę przedłużyć ochronę sprężyny, aby zaszyfrować zawartość odpowiedzi HTTP i umieścić wynik wewnątrz nagłówka. Moje podejście polega na utworzeniu filtru serwletów, który odczytuje odpowiedź i umieszcza odpowiedni nagłówek. Filtr jest rejestrowany z bezpieczeństwem sprężynowym za pomocą osobnej wtyczki. Wdrożenie w dużej mierze pochodzi z .Zabezpieczenia sprężyn Graal, filtry serwletów i odpowiedź

Cała instalacja działa idealnie, gdy ostateczna aplikacja używa "render" w kontrolerze, aby wyprowadzić JSON do klienta. Jeśli jednak te same dane są sformatowane za pomocą "odpowiedzi", 404 jest zwracana do klienta. Nie jestem w stanie wyjaśnić różnicy.

Dla porównania wszystko jest grails wersji 2.3.11 i sprężyny Core Security w wersji 2.0-RC4

zarejestrować filtra poprzez doWithSpring mojego pluginu

responseHasher(ResponseHasher) 

SpringSecurityUtils.registerFilter(
       'responseHasher', SecurityFilterPosition.LAST.order - 1) 

Moje wykonania filtr

public class ResponseHasher implements Filter{ 

    @Override 
    public void init(FilterConfig config) throws ServletException { 
    } 

    @Override 
    public void doFilter(ServletRequest request, 
         ServletResponse response, FilterChain chain) 
      throws IOException, ServletException { 


     HttpServletResponseCopier wrapper = new HttpServletResponseCopier((HttpServletResponse)response); 

     chain.doFilter(request, wrapper); 
     wrapper.flushBuffer(); 

     /* 
     Take the response, hash it, and set it in a header. for brevity sake just prove we can read it for now 
     and set a static header 
     */ 
     byte[] copy = wrapper.getCopy(); 
     System.out.println(new String(copy, response.getCharacterEncoding())); 
     wrapper.setHeader("foo","bar"); 

    } 

    @Override 
    public void destroy() { 

    } 

} 

Implementacja HttpServletResponseCopier. Jedyną zmianą ze źródła jest zastąpienie wszystkich 3 podpisów metody zapisu, a nie tylko jednego.

class HttpServletResponseCopier extends HttpServletResponseWrapper{ 

    private ServletOutputStream outputStream; 
    private PrintWriter writer; 
    private ServletOutputStreamCopier copier; 

    public HttpServletResponseCopier(HttpServletResponse response) throws IOException { 
     super(response); 
    } 

    @Override 
    public ServletOutputStream getOutputStream() throws IOException { 
     if (writer != null) { 
      throw new IllegalStateException("getWriter() has already been called on this response."); 
     } 

     if (outputStream == null) { 
      outputStream = getResponse().getOutputStream(); 
      copier = new ServletOutputStreamCopier(outputStream); 
     } 

     return copier; 
    } 

    @Override 
    public PrintWriter getWriter() throws IOException { 
     if (outputStream != null) { 
      throw new IllegalStateException("getOutputStream() has already been called on this response."); 
     } 

     if (writer == null) { 
      copier = new ServletOutputStreamCopier(getResponse().getOutputStream()); 
      writer = new PrintWriter(new OutputStreamWriter(copier, getResponse().getCharacterEncoding()), true); 
     } 

     return writer; 
    } 

    @Override 
    public void flushBuffer() throws IOException { 
     if (writer != null) { 
      writer.flush(); 
     } else if (outputStream != null) { 
      copier.flush(); 
     } 
    } 

    public byte[] getCopy() { 
     if (copier != null) { 
      return copier.getCopy(); 
     } else { 
      return new byte[0]; 
     } 
    } 

    private class ServletOutputStreamCopier extends ServletOutputStream { 

     private OutputStream outputStream; 
     private ByteArrayOutputStream copy; 

     public ServletOutputStreamCopier(OutputStream outputStream) { 
      this.outputStream = outputStream; 
      this.copy = new ByteArrayOutputStream(1024); 
     } 

     @Override 
     public void write(int b) throws IOException { 
      outputStream.write(b); 
      copy.write(b); 
     } 

     @Override 
     public void write(byte[] b,int off, int len) throws IOException { 
      outputStream.write(b,off,len); 
      copy.write(b,off,len); 
     } 

     @Override 
     public void write(byte[] b) throws IOException { 
      outputStream.write(b); 
      copy.write(b); 
     } 

     public byte[] getCopy() { 
      return copy.toByteArray(); 
     } 

    } 
} 

I wreszcie moja metoda kontroler w rzeczywistej aplikacji

@Secured() 
    def myAction() { 
     def thing = Thing.get(1) //thing can be any domain object really. in this case we created thing 1 in bootstap 

     //throws a 404 
     respond(thing) 
     /* 
     works as expected, output is both rendered 
     and sent to system out, header "foo" is in response 
     /* 
     //render thing as JSON 
} 

Każdy wgląd byłoby mile widziane, jak ja nie rozumiem, dlaczego czynią będzie działać odpowiedzi nie. Dodatkowo jestem otwarty na inne podejścia do rozwiązania tej potrzeby, jeśli to, co próbuję, po prostu nie zadziała w grails. Z góry dziękuję.

+0

Myślę, że to ma coś wspólnego z negocjacji Treść: http://grails.github.io/grails-doc/2.3.x/guide/single.html #contentNegotiation. Spróbuj wywołać 'myAction' z parametrem' format = json' lub dodaj nagłówek żądania 'Accept'. – defectus

+0

Dzięki. Próbowałem tego i nie miało to żadnego wpływu. Mogę dołączyć debugger i zobaczyć, że najmniej wydaje się, że przechodzi on przez metodę respond i do rendera json, wykreślając prawidłowy JSON. Moje (wprawdzie nieświadome) przypuszczenie jest, że problem leży gdzieś dalej w stosie. –

+0

Dlaczego nie użyjesz domyślnych filtrów Grails i wstawisz nowy nagłówek w sekcji 'after()'? –

Odpowiedz

1

Umieściłem cały projekt w grailsach i miałem podobny problem. Musiałem wprowadzić pewne zmiany.

Dla rejestru użyłem metody SpringSecurityUtils.clientRegisterFilter, tak jak robiłem to z aplikacji Bootstrap.groovy. Również zadeklarowałem filtr w resources.groovy

Działa z renderowaniem, ale 404 z odpowiedzią. Tak zmieniłem odpowiedź na:

respond user, [formats:['json']] 

I działało po tym, jak usunąłem twój filtr. Mam 404 za każdym razem, gdy wkładam twój filtr i próbuję znaleźć action.gsp.

Zrobiłem zmiany w ServletOutputStreamCopier, wdrażanie delegatów dla close i flush metod, i to działało dobrze dla renderowania odpowiedzi:

class HttpServletResponseCopier extends HttpServletResponseWrapper { 

    private ServletOutputStream outputStream; 
    private PrintWriter writer; 
    private ServletOutputStreamCopier copier; 

    public HttpServletResponseCopier(HttpServletResponse response) 
      throws IOException { 
     super(response); 
    } 

    @Override 
    public ServletOutputStream getOutputStream() throws IOException { 
     if (writer != null) { 
      throw new IllegalStateException(
        "getWriter() has already been called on this response."); 
     } 

     if (outputStream == null) { 
      outputStream = getResponse().getOutputStream(); 
      copier = new ServletOutputStreamCopier(outputStream); 
     } 

     return copier; 
    } 

    @Override 
    public PrintWriter getWriter() throws IOException { 
     if (outputStream != null) { 
      throw new IllegalStateException(
        "getOutputStream() has already been called on this response."); 
     } 

     if (writer == null) { 
      copier = new ServletOutputStreamCopier(getResponse() 
        .getOutputStream()); 
      writer = new PrintWriter(new OutputStreamWriter(copier, 
        getResponse().getCharacterEncoding()), true); 
     } 

     return writer; 
    } 

    @Override 
    public void flushBuffer() throws IOException { 
     if (writer != null) { 
      writer.flush(); 
     } else if (outputStream != null) { 
      copier.flush(); 
     } 
    } 

    public byte[] getCopy() { 
     if (copier != null) { 
      return copier.getCopy(); 
     } else { 
      return new byte[0]; 
     } 
    } 

    private class ServletOutputStreamCopier extends ServletOutputStream { 

     private OutputStream outputStream; 
     private ByteArrayOutputStream copy; 

     public ServletOutputStreamCopier(OutputStream outputStream) { 
      this.outputStream = outputStream; 
      this.copy = new ByteArrayOutputStream(1024); 
     } 

     @Override 
     public void write(int b) throws IOException { 
      outputStream.write(b); 
      copy.write(b); 
     } 

     @Override 
     public void write(byte[] b, int off, int len) throws IOException { 
      outputStream.write(b, off, len); 
      copy.write(b, off, len); 
     } 

     @Override 
     public void write(byte[] b) throws IOException { 
      outputStream.write(b); 
      copy.write(b); 
     } 

     @Override 
     public void flush() throws IOException { 
      outputStream.flush(); 
      copy.flush(); 
     } 

     @Override 
     public void close() throws IOException { 
      outputStream.close(); 
      copy.close(); 
     } 

     public byte[] getCopy() { 
      return copy.toByteArray(); 
     } 

    } 
} 

nie przechodzą reagować szczegółów implementacji, ale myślę, że uzyskać pewne zamieszanie, ponieważ nie ma sposobu na przepłukanie lub zamknięcie, i ma awarię wywoływania widoku zamiast renderowania json.

Wiem, że jest trochę za późno, ale teraz działa.

resources.groovy

beans = { 

    responseHasher(ResponseHasher){ 

    } 

} 

Boostrap.Groovy

def init = { servletContext -> 

    . 

    SpringSecurityUtils.clientRegisterFilter('responseHasher', SecurityFilterPosition.LAST.order - 1) 

} 

Best, Eder