Spring Zuul API Gateway with Spring Session / Redis Authenticate and Route in same Request

I followed Justin Taylor's posts on different pages so this is his solution. It makes me sense to have solution with source code here:

  1. Make Spring Session commit eagerly - since spring-session v1.0 there is annotation property @EnableRedisHttpSession(redisFlushMode = RedisFlushMode.IMMEDIATE) which saves session data into Redis immediately. Documentation here.
  2. Simple Zuul filter for adding session into current request's header:
@Component
public class SessionSavingZuulPreFilter extends ZuulFilter {

    @Autowired
    private SessionRepository repository;

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public Object run() {
        RequestContext context = RequestContext.getCurrentContext();

        HttpSession httpSession = context.getRequest().getSession();
        Session session = repository.getSession(httpSession.getId());

        context.addZuulRequestHeader("Cookie", "SESSION=" + httpSession.getId());

        log.info("ZuulPreFilter session proxy: {}", session.getId());

        return null;
    }

}

Once more - this is not my solution - credentials go to Justin Taylor.


I am so sorry about the delayed response here, one of the great things about South Africa is our great telecoms hehe, I have had no internet at home for a while and my source code for this is on my home pc.

Yes Steve is on the right track. There are two issues that you need to be resolve here:

  1. Spring session only commits the authenticated session to redis on response to the initial incoming request. So the first step is to follow that link steve provided to ensure spring session commits to redis whenever the session changes.

  2. Zuul doesn't propagate this newly authenticated session on the initial routing. So what you need to do is to use a zuul pre filter (lots of examples around) that gets the authenticated session id and then adds it to the zuul request to the resource behind the gateway. You will see a setter method on the zuul request to set the session id.

If you don't do this, you will need to do two calls, one to authenticate and get a valid session id which would be in redis from spring session, and then the subsequent call with your authenticated session id.

I did battle with this for a while, but when I got it working it was spot on. I extended this solution to not only work for http basic, but added in a jwt token implementation.

Hopefully this helps, as soon as I am connected at home I can post the source.

Good Luck! Justin


My APIGateway (Zuul) is proxied by Apache Httpd and protected by Mellon module (SAML 2.0). After a successfully authentication on the identity provider, mellon module inject correctly some headers read into the SAML response, but the first request fails with a 403 status code.

I'm also using SpringSecurity, to solve the problem I'm using a simple filter added on the security filter chain that ensure the correct creation of SecurityContext:

@Component
public class MellonFilter extends OncePerRequestFilter {

    private final Logger log = LoggerFactory.getLogger(MellonFilter.class);


    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {

       String mellonId=req.getHeader("mellon-nameid");

        if(mellonId==null||mellonId.isEmpty())
            ;//do filterchain
        else {

            UserWithRoles userWithRoles = new UserWithRoles();
            userWithRoles.setUsername(mellonId);
            SilUserDetails details = new SilUserDetails(userWithRoles);

            SilAuthenticationPrincipal silPrincipal = null;
            Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();

            authorities.add(new SimpleGrantedAuthority("Some roles");

            silPrincipal = new SilAuthenticationPrincipal(details, true, authorities);
            SecurityContextHolder.clearContext();
            SecurityContextHolder.getContext().setAuthentication(silPrincipal);
        }
        filterChain.doFilter(req,httpServletResponse);
    }

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        if(SecurityContextHolder.getContext().getAuthentication()!=null&&SecurityContextHolder.getContext().getAuthentication() instanceof SilAuthenticationPrincipal)
            return true;
        return false;

    }
}

Then I need a ZuulFilter to save the session (on Redis) and to propagate the actual session id:

public class ZuulSessionCookieFilter extends ZuulFilter {

    private final Logger log = LoggerFactory.getLogger(ZuulSessionCookieFilter.class);

    @Autowired
    private SessionRepository repository;

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {

        return true;
    }

    @Override
    public Object run() throws ZuulException {

        RequestContext context = RequestContext.getCurrentContext();

        HttpSession httpSession = context.getRequest().getSession();
        httpSession.setAttribute(
                HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
                SecurityContextHolder.getContext()
        );
        Session session = repository.findById(httpSession.getId());
        context.addZuulRequestHeader("cookie", "SESSION=" + base64Encode(httpSession.getId()));
        log.debug("ZuulPreFilter session proxy: {} and {}", session.getId(),httpSession.getId());

        return null;
    }

    private static String base64Encode(String value) {
        byte[] encodedCookieBytes = Base64.getEncoder().encode(value.getBytes());
        return new String(encodedCookieBytes);
    }
}

I hope this solution will be helpful to everyone.