2015-08-18 4 views
8

Ho bisogno di estendere la sicurezza di primavera per cancellare il contenuto della risposta http e posizionare il risultato all'interno di un'intestazione. Il mio approccio è quello di creare un filtro servlet che legge la risposta e posiziona l'intestazione appropriata. Il filtro è registrato con sicurezza Spring tramite un plugin separato. L'implementazione è in gran parte presa da .Grails sicurezza molla, filtri servlet e risposta

L'intera configurazione funziona perfettamente quando l'applicazione finale utilizza "rendering" nel controller per inviare JSON al client. Tuttavia, se gli stessi dati vengono formattati tramite "rispondi", un 404 viene restituito al client. Sono in perdita per spiegare la differenza.

Per riferimento tutto è graal versione 2.3.11 e la molla del nucleo di sicurezza versione 2.0-RC4

Registrare il filtro tramite doWithSpring di mio plug

responseHasher(ResponseHasher) 

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

mia implementazione filtro

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

    } 

} 

Il Implementazione HttpServletResponseCopier. L'unico cambiamento dalla fonte è quello di scavalcare tutte e 3 le firme dei metodi di scrittura anziché solo quella.

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

    } 
} 

E infine il mio metodo di controllo nell'applicazione effettiva

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

Tutta la comprensione sarebbe apprezzata come io non capisco perché rende avrebbe funzionato e rispondere no. Inoltre, sono aperto ad altri approcci per risolvere questo bisogno se quello che sto tentando non funzionerà solo con i graal. Grazie in anticipo.

+0

Credo che abbia qualcosa a che fare con il contenuto negoziale: http://grails.github.io/grails-doc/2.3.x/guide/single.html #contentNegotiation. Prova a chiamare 'myAction' con parametro' format = json' o aggiungi un'intestazione di richiesta 'Accept'. – defectus

+0

Grazie. Ci ho provato e non ha avuto alcun effetto. Posso collegare un debugger e vedere che sembra che stia facendo il metodo di risposta e nel renderer json, eliminando JSON valido. La mia (presumibilmente ignorante) ipotesi è che il problema è da qualche parte più in basso nello stack –

+0

Perché non usi i filtri predefiniti di Grails e metti una nuova intestazione nella sezione 'after()'? –

risposta

1

Ho inserito tutto il progetto in Grails e ho riscontrato un problema simile. Ho dovuto apportare alcune modifiche.

Per il registro, ho utilizzato il metodo SpringSecurityUtils.clientRegisterFilter, come stavo facendo da un'applicazione Bootstrap.groovy. Inoltre, ho dichiarato il filtro in resources.groovy

Ha funzionato con il rendering, ma 404 con risposta. Così cambiato la risposta a:

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

E ha funzionato dopo aver rimosso il filtro. Ho ottenuto 404 ogni volta che ho messo il filtro e stava cercando di trovare action.gsp.

Ho fatto un cambiamento in ServletOutputStreamCopier, in attuazione dei delegati per close e flush metodi, e ha funzionato bene per rendere e rispondere:

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

    } 
} 

non sono andato attraverso respond dettagli di implementazione, ma penso che ottenere una certa confusione in quanto non ha un modo per eseguire il flush o la chiusura e ha una fallback per chiamare la vista invece di rendere json.

So che è un po 'in ritardo, ma ora funziona.

resources.groovy

beans = { 

    responseHasher(ResponseHasher){ 

    } 

} 

bootstarp.Groovy

def init = { servletContext -> 

    . 

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

} 

migliore, Eder