我需要扩展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中不起作用,我愿意采用其他方法来解决这个问题。提前谢谢。
答案 0 :(得分:1)
我将所有项目都放在grails中,并遇到了类似的问题。我不得不做出一些改变。
对于注册,我使用SpringSecurityUtils.clientRegisterFilter
方法,就像我在Bootstrap.groovy
应用程序中所做的那样。
另外,我在resources.groovy
它适用于渲染,但404响应。所以改变了回应:
respond user, [formats:['json']]
我删除了你的过滤器后它运行了。每当我放置你的过滤器并且它试图找到action.gsp时,我得到404。
我在ServletOutputStreamCopier
中进行了更改,实现了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)
}
最佳, Eder的