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.
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
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 –
Warum verwenden Sie nicht Standard-Grails-Filter und neue Header in 'After()' Abschnitt? –