Grails spring security、servlet 过滤器和响应
Posted
技术标签:
【中文标题】Grails spring security、servlet 过滤器和响应【英文标题】:Grails spring security, servlet filters, and respond 【发布时间】:2015-11-11 19:41:38 【问题描述】:我需要扩展 spring security 以散列 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 个 write 方法签名,而不仅仅是一个。
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 中不起作用,我愿意接受其他方法来解决此需求。提前致谢。
【问题讨论】:
我猜这与内容协商有关:grails.github.io/grails-doc/2.3.x/guide/…。尝试使用参数format=json
调用myAction
或添加Accept
请求标头。
谢谢。我试过了,它没有任何影响。我可以附加一个调试器,并看到它似乎至少是通过响应方法进入 json 渲染器,踢出有效的 JSON。我(诚然无知)的猜测是问题出在堆栈中更远的地方
你为什么不使用默认的 Grails 过滤器并将新标题放在 after()
部分?
【参考方案1】:
我将所有项目都放在 grails 中,但遇到了类似的问题。我不得不做出一些改变。
对于注册,我使用SpringSecurityUtils.clientRegisterFilter
方法,就像我在Bootstrap.groovy
应用程序中所做的那样。
另外,我在resources.groovy
中声明了过滤器
它适用于渲染,但 404 响应。于是将响应改为:
respond user, [formats:['json']]
在我删除您的过滤器后它起作用了。每当我放置你的过滤器并且它试图找到 action.gsp 时,我都会得到 404。
我对@987654325@ 进行了更改,实现了close
和flush
方法的委托,并且它在渲染和响应方面工作得很好:
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)
最好, 埃德
【讨论】:
以上是关于Grails spring security、servlet 过滤器和响应的主要内容,如果未能解决你的问题,请参考以下文章
grails-spring-security-rest 插件和悲观锁定
Grails + spring-security-core:用户登录后如何分配角色?