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ę.
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
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. –
Dlaczego nie użyjesz domyślnych filtrów Grails i wstawisz nowy nagłówek w sekcji 'after()'? –