【问题标题】:Spring security oauth2 - add filter after oauth/token callSpring security oauth2 - 在 oauth/token 调用后添加过滤器
【发布时间】:2020-02-08 09:17:26
【问题描述】:

我正在使用 Spring Boot 2.1.1.RELEASE (spring-security-oauth2-2.3.4.RELEASE)。

我想在TokenEndpoint#postAccessToken 调用之后创建一个优先过滤器。为什么 ?因为在该过滤器中,我想从tokenStore 中获取令牌并将其作为 cookie 添加到响应中。

我希望,这会给我想要的:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
    .(...)
    .addFilterAfter(new MyFilter(), BasicAuthenticationFilter.class);
}

但事实并非如此。我可以看到,BasicAuthenticationFilteroauth/token 上成功验证后被调用,但它没有进入我的MyFilter

oauth/token 调用之后,我应该怎么做才能调用MyFilter


您想从授权服务器或资源服务器设置 cookie? 您的身份验证服务器和资源服务器是否都在同一上下文中?或不同的应用程序。?

我有两个微服务。第一个是授权服务器,它提供 jwt 令牌(由其私钥签名)。第二个微服务是一个资源服务器,它根据授权服务器公钥验证令牌(由 Auth 服务器通过 REST 端点公开)

你想在收到授权服务器的access_token后设置吗? >你想通过设置cookie做什么?

没有。当前端应用程序进行oauth/token 调用时,我希望授权服务器设置一个cookie。这样浏览器负责为每个请求添加一个令牌,而不是我的前端应用程序。这可以保护我免受 XSS 攻击,因为 cookie 将设置为 httpOnlysecure

您的计划是读取 cookie 以获取 access_token 吗?

正确。但这应该由资源服务器完成(还没有这样做)

简单的方法是为相同的功能创建一个 API。它将 access_token 作为请求参数并设置 cookie。

您是否建议在前端应用程序和身份验证/资源服务器之间建立代理微服务?将 jwt 令牌设置为 cookie 并从 cookie 中读取令牌的代理微服务?

【问题讨论】:

  • 简单的方法是为相同的功能创建一个 API。它将 access_token 作为请求参数并设置 cookie。
  • 如果你能解释你的要求,那就太好了。您想从授权服务器或资源服务器设置 cookie?是否要在从授权服务器收到 access_token 后进行设置?您的身份验证服务器和资源服务器是否都在同一上下文中?或不同的应用程序。?你想通过设置cookie做什么?您的计划是读取 cookie 以获取 access_token 吗?如果你用你的方法更新问题,它将帮助大多数访问者。
  • 你是怎么解决这个问题的?我想达到同样的效果吗? @user3529850

标签: java spring spring-boot spring-security spring-security-oauth2


【解决方案1】:

没有。当前端应用程序进行 oauth/token 调用时,我希望授权服务器设置一个 cookie。

您需要在所有过滤器之前添加过滤器,我的意思是过滤器顺序 1,以便请求首先到达并最后调度。

如果不是spring-boot,使用web.xml或java config方式的spring配置会容易得多。由于 spring boot 不依赖 web.xml 所有过滤器都是代理过滤器,除了 DelegatingFilterProxy(springSecurityFilterChain) 在此之前我们不能添加任何过滤器。

  • 实现您的要求的可能方法是使用 order(1) 在 FilterRegistrationBean 中注册一个过滤器。

  • 给过滤器url模式/oauth/token

  • 在您的过滤器中使用 HttpServletResponseWrapper 实现来读取响应并获取 access_token 并根据您的要求设置 cookie。

In Any configuration class register filter into FilterRegistrationBean
@Configuration
public class AppInitializer
{
    @Bean
    public FilterRegistrationBean<AccessTokenAlterFilter> sessionTimeoutFilter()
    {
        FilterRegistrationBean<AccessTokenAlterFilter> registrationBean = new FilterRegistrationBean<>();
        AccessTokenAlterFilter filter = new AccessTokenAlterFilter();

        registrationBean.setFilter(filter);
        registrationBean.addUrlPatterns("/oauth/token");
        registrationBean.setOrder(1); // set precedence
        return registrationBean;
    }
}
Your Filter
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class AccessTokenAlterFilter implements Filter
{

    Logger OUT = LoggerFactory.getLogger(AccessTokenAlterFilter.class);

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
    {
        OUT.info("[[[[[[[[[[[[STARTED]]]]]]]]]]]]]]");

        CharResponseWrapper wrappedResponse = new CharResponseWrapper((HttpServletResponse) response);
        chain.doFilter(request, wrappedResponse);
        byte[] bytes = wrappedResponse.getByteArray();
        String out = new String(bytes);
        OUT.info("Response String: {}", out);

        response.getOutputStream().write(out.getBytes());

        OUT.info("[[[[[[[[[[[[ENDED]]]]]]]]]]]]]]");
    }

    private static class ByteArrayServletStream extends ServletOutputStream
    {
        ByteArrayOutputStream baos;

        ByteArrayServletStream(ByteArrayOutputStream baos)
        {
            this.baos = baos;
        }

        public void write(int param) throws IOException
        {
            baos.write(param);
        }

        @Override
        public boolean isReady()
        {
            return false;
        }

        @Override
        public void setWriteListener(WriteListener listener)
        {}
    }

    private static class ByteArrayPrintWriter
    {
        private ByteArrayOutputStream   baos    = new ByteArrayOutputStream();
        private PrintWriter             pw      = new PrintWriter(baos);
        private ServletOutputStream     sos     = new ByteArrayServletStream(baos);

        public PrintWriter getWriter()
        {
            return pw;
        }

        public ServletOutputStream getStream()
        {
            return sos;
        }

        byte[] toByteArray()
        {
            return baos.toByteArray();
        }
    }

    public class CharResponseWrapper extends HttpServletResponseWrapper
    {
        private ByteArrayPrintWriter    output;
        private boolean                 usingWriter;

        public CharResponseWrapper(HttpServletResponse response)
        {
            super(response);
            usingWriter = false;
            output = new ByteArrayPrintWriter();
        }

        public byte[] getByteArray()
        {
            return output.toByteArray();
        }

        @Override
        public ServletOutputStream getOutputStream() throws IOException
        {
            if (usingWriter)
            {
                super.getOutputStream();
            }
            usingWriter = true;
            return output.getStream();
        }

        @Override
        public PrintWriter getWriter() throws IOException
        {
            if (usingWriter)
            {
                super.getWriter();
            }
            usingWriter = true;
            return output.getWriter();
        }

        public String toString()
        {
            return output.toString();
        }
    }
}

之前的流程将仍然存在,如下所示

您可以控制响应对象并添加cookie。只是显示日志供您参考。

【讨论】:

  • requests /oauth/** 被过滤掉了,这些请求永远不会到达 spring 安全配置 WebSecurityConfigurerAdapter,但只会到达 AuthorizationServerConfigurerAdapter 配置,我们没有添加过滤器的选项。因此尝试.addFilterAfteraddFilterBefore 无法成功将oauth/** 请求放入定义的过滤器。
  • @user3529850 就是那个解决方案!你期待吗?
  • user3529850 很想知道它是否解决了您的问题。我相信它应该。但没有收到您的任何反馈。
  • @ammy 我早就做过了。看看我的 github repo 链接。如果我什么都没改变,那么它可能会对你有所帮助。 github.com/nlpraveennl/springsecurity/tree/…
猜你喜欢
  • 1970-01-01
  • 2013-12-31
  • 2018-07-22
  • 2016-12-04
  • 1970-01-01
  • 2014-09-29
  • 2018-02-23
  • 2018-09-20
  • 2014-08-13
相关资源
最近更新 更多