Grails spring security,servlet过滤器,并回复

时间:2015-08-18 18:10:27

标签: grails spring-security

我需要扩展spring安全性来散列http响应内容并将结果放在标头内。我的方法是创建一个servlet过滤器,读取响应并放置适当的标头。过滤器通过单独的插件注册spring security。实施主要来自here

当最终应用程序在控制器中使用“render”将JSON输出到客户端时,整个设置工作正常。但是,如果通过“响应”格式化相同的数据,则将404返回给客户端。我无法解释其中的差异。

作为参考,一切都是grails版本2.3.11和spring security core版本2.0-RC4

通过我的插件的doWithSpring注册过滤器

responseHasher(ResponseHasher)

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

我的过滤器实现

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

    }

}

HttpServletResponseCopier实现。来自源的唯一变化是覆盖写入的所有3个方法签名而不仅仅是一个。

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

    }
}

最后我的控制器方法在实际应用中

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

任何见解都会受到赞赏,因为我不明白为什么渲染会起作用而反应不会。另外,如果我正在尝试的东西在grails中不起作用,我愿意采用其他方法来解决这个问题。提前谢谢。

1 个答案:

答案 0 :(得分:1)

我将所有项目都放在grails中,并遇到了类似的问题。我不得不做出一些改变。

对于注册,我使用SpringSecurityUtils.clientRegisterFilter方法,就像我在Bootstrap.groovy应用程序中所做的那样。 另外,我在resources.groovy

中声明了过滤器

它适用于渲染,但404响应。所以改变了回应:

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

我删除了你的过滤器后它运行了。每当我放置你的过滤器并且它试图找到action.gsp时,我得到404。

我在ServletOutputStreamCopier中进行了更改,实现了closeflush方法的代理,并且它在渲染和响应方面运行良好:

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

    }
}

我没有通过回复实施细节,但我认为它有一些混乱,因为它没有办法刷新或关闭,并且有一个后退来调用视图而不是渲染json。

我知道这有点晚了,但现在它正在发挥作用。

resources.groovy

beans = {

    responseHasher(ResponseHasher){

    }

}

Boostrap.groovy

def init = { servletContext ->

    .

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

}

最佳, Eder的