2015-08-18 14 views
8

Ich muss Frühling Sicherheit zu Hash-der HTTP-Response-Inhalt und legen Sie das Ergebnis innerhalb einer Kopfzeile. Mein Ansatz besteht darin, einen Servlet-Filter zu erstellen, der die Antwort liest und den entsprechenden Header platziert. Der Filter ist mit Federsicherheit über ein separates Plugin registriert. Die Implementierung ist weitgehend aus entnommen.Grails Feder Sicherheit, Servlet-Filter und reagieren

Das gesamte Setup funktioniert einwandfrei, wenn die endgültige Anwendung "Rendern" im Controller verwendet, um JSON an den Client auszugeben. Wenn jedoch die gleichen Daten über "Antworten" formatiert werden, wird ein 404 an den Client zurückgegeben. Ich bin nicht in der Lage, den Unterschied zu erklären.

Als Referenz alles ist Grails Version 2.3.11 und Federsicherheits Core-Version 2.0-RC4

Registrieren Sie die Filter über meine Plugins doWithSpring

responseHasher(ResponseHasher) 

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

Meine Filterimplementierung

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() { 

    } 

} 

Die HttpServletResponseCopier Implementierung. Die einzige Änderung von der Quelle besteht darin, alle 3 Methodensignaturen von Write anstelle von nur einer zu überschreiben.

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(); 
     } 

    } 
} 

Und schließlich Methode meines Controller in der tatsächlichen Anwendung

@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 
} 

Einsicht würde geschätzt, wie ich verstehe nicht, warum funktionieren würde machen und reagieren würde nicht. Außerdem bin ich offen für andere Ansätze, um dieses Bedürfnis zu lösen, wenn das, was ich versuche, einfach nicht in Grails funktionieren wird. Danke im Voraus.

+0

Ich denke, es hat etwas mit dem Content-Negotiation zu tun hat: http://grails.github.io/grails-doc/2.3.x/guide/single.html #contentVerhandlungsrecht. Versuchen Sie, 'myAction' mit dem Parameter' format = json' aufzurufen oder fügen Sie einen 'Accept'-Request-Header hinzu. – defectus

+0

Danke. Ich habe es versucht und es hatte keinen Einfluss. Ich kann einen Debugger anhängen und sehe, dass es am wenigsten scheint, es durch die Antwort-Methode und in den Json-Renderer zu machen, indem ich gültigen JSON rausschmeiße. Meine (zugegebenermaßen unwissende) Vermutung ist das Problem ist irgendwo weiter unten im Stapel –

+0

Warum verwenden Sie nicht Standard-Grails-Filter und neue Header in 'After()' Abschnitt? –

Antwort

1

Ich legte das ganze Projekt in die Grals und bekam ein ähnliches Problem. Ich musste einige Änderungen vornehmen.

Für registrieren, habe ich SpringSecurityUtils.clientRegisterFilter Methode, wie ich von einer Bootstrap.groovy Anwendung gemacht wurde. Auch ich erklärte den Filter in resources.groovy

Es funktionierte mit Render, aber 404 mit reagieren. So änderte die Reaktion auf:

Und es funktionierte, nachdem ich Ihren Filter entfernt. Ich habe 404 wann immer ich Ihren Filter einsetzte und es versuchte action.gsp zu finden.

machte ich eine Veränderung in ServletOutputStreamCopier, die Delegierten für close und flush Methoden der Umsetzung, und es funktionierte gut für machen und darauf zu reagieren:

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(); 
     } 

    } 
} 

Ich gehe nicht durch Details der Implementierung reagieren, aber ich denke, es Verwirrt euch, da es keine Möglichkeit zum Leeren oder Schließen gibt, und hat einen Fallback, um die Ansicht aufzurufen, anstatt json zu rendern.

Ich weiß, es ist ein bisschen spät, aber jetzt funktioniert es.

resources.groovy

beans = { 

    responseHasher(ResponseHasher){ 

    } 

} 

Bootstrap.

groovy
def init = { servletContext -> 

    . 

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

} 

Best, Eder

Verwandte Themen