Zuul API Gateway with Spring Session (Redis): Authenticate and Route in same request with Apache module Mellon (SAML)

1 minute read

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.

Updated: