春季安全性oauth2-在oauth /令牌调用之后添加过滤器

时间:2019-10-10 21:13:46

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

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

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

我希望这会给我我想要的东西

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

但事实并非如此。我可以看到,BasicAuthenticationFilter是在oauth/token上成功通过身份验证后调用的,但是它没有输入我的MyFilter

MyFilter通话之后,我该怎么打oauth/token


  

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

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

  

从授权服务器接收到access_token后是否要设置?您想通过设置Cookie来做什么?

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

  

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

正确。但这应该由资源服务器完成(尚未完成)

  

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

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

1 个答案:

答案 0 :(得分:2)

  

不。我希望授权服务器在前端应用程序进行oauth / token调用时设置cookie。

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

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

  • 满足要求的可能方法是在FilterRegistrationBean中将order(1)注册为过滤器。

  • 提供过滤器网址格式/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();
        }
    }
}

先前的流程仍将存在,如下所示
enter image description here

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

enter image description here