当前位置: 首页 > 知识库问答 >
问题:

Spring Zuul API网关与Spring会话/Redis身份验证和路由在同一请求

彭霄
2023-03-14

在过去的几天里,我一直在到处寻找如何做到这一点,最终决定承认失败并寻求帮助,请!!!

我遵循了Dave Syer博士关于Angular和Spring Security性的教程,特别是将Zuul代理用作api网关,并使用Redis的Spring会话(https://github.com/spring-guides/tut-spring-security-and-angular-js/tree/master/double#_sso_with_oauth2_angular_js_and_spring_security_part_v)

我遇到的问题是,我正在通过网关从具有以下标头的外部应用程序调用资源rest服务:

String plainCreds = "user:password";
byte[] plainCredsBytes = plainCreds.getBytes();
byte[] base64CredsBytes = Base64.getEncoder().encode(plainCredsBytes);
String base64Creds = new String(base64CredsBytes);

HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + base64Creds);

通过zuul进行身份验证,然后路由资源,以便通过redis访问经过身份验证的会话。

问题是会话似乎只在请求响应后才提交网关中的redis。所以发生的是,当我用头调用资源服务时,我可以看到成功的身份验证发生在网关和正在创建的会话中,但是我在资源中得到一个403,因为会话在它被创建后不在redis中通过zuul路由。

但是,如果出现错误,请抓取会话id并将其添加到标头中,然后重试,因为现在我的已验证会话在路由后可用于资源项目。

请有人告诉我如何通过网关获取我的电话,以便在同一请求中进行身份验证和路由,好吗?

谢谢你,贾斯汀

共有3个答案

洪琦
2023-03-14

我的APIGateway(Zuul)由Apache Httpd代理,由Mellon模块(SAML 2.0)保护。在身份提供者上成功进行身份验证后,mellon模块正确地将读取的一些标头注入到SAML响应中,但是第一个请求以403状态代码失败。

我也在使用SpringSecurity,为了解决这个问题,我在安全过滤链上添加了一个简单的过滤器,以确保正确创建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;

    }
}

然后,我需要一个ZumFilter来保存会话(在Redis上)并传播实际会话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);
    }
}

希望这个解决方案对大家有帮助。

池阳伯
2023-03-14

我对这里的延迟回复感到非常抱歉,南非最伟大的事情之一是我们伟大的电信业。呵呵,我已经有一段时间没有在家上网了,我的源代码在我的家用电脑上。

是的,史蒂夫在正确的轨道上。这里有两个问题需要解决:

>

  • Spring会话仅在响应初始传入请求时将经过身份验证的会话提交给redis。因此,第一步是遵循steve提供的链接,以确保每当会话更改时,Spring会话都会提交到redis。

    Zuul不会在初始路由上传播这个新认证的会话。因此,您需要做的是使用zuul预过滤器(周围有很多例子),它获取经过身份验证的会话标识,然后将其添加到网关后面的资源的zuul请求中。您将在zuul请求上看到一个setter方法来设置会话id。

    如果您不这样做,您将需要执行两个调用,一个用于验证并从spring会话获取redis中的有效会话id,然后使用经过验证的会话id执行后续调用。

    我确实和它斗争了一段时间,但当我让它工作的时候,它是当场的。我扩展了这个解决方案,不仅适用于http basic,还添加了jwt令牌实现。

    希望这有帮助,只要我在家里连接好,我就可以发布源代码。

    祝你好运!贾斯汀

  • 顾赞
    2023-03-14

    我关注了贾斯汀·泰勒在不同页面上的帖子,所以这是他的解决方案。它使我有意义的解决方案与源代码在这里:

    1. 使Spring会话急切地提交—从Spring会话v1开始。0有注释属性@EnableRedisHttpSession(redisFlushMode=redisFlushMode.IMMEDIATE),可将会话数据立即保存到Redis中。这里有文档
    2. 用于将会话添加到当前请求头中的简单Zuul筛选器:
    @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;
        }
    
    }
    

    再一次-这不是我的解决方案-证书交给贾斯汀·泰勒。

     类似资料:
    • 我试图通过编写一个简单的REST应用程序来学习Spring Boot,该应用程序将登录用户()并显示当前用户的信息()。我正在使用Redis进行会话。 按预期工作:它返回主体并在浏览器和Redis中设置会话cookie。 然而,随后的请求返回。我错过了什么? : : < code > index controller . Java : :

    • 在身份验证等情况下,与会话相比,使用JWTs有什么优势? 它是作为独立方法使用还是在会话中使用?

    • 我不熟悉spring微服务世界。由于我处于学习阶段,我尝试并实施了以下内容。 > 路由(能够使用Spring云网关进行路由) 负载平衡(Netflix Eureka) 限速和断路器 我只需要一些澄清和建议,说明在这些情况下该怎么做: 因为我已经创建了身份验证/授权作为一个单独的微服务集中。现在我如何实现这样的每个请求必须包含jwt令牌和通过API网关调用其他微服务也应该检查哪个用户有权限访问其他微

    • 我正在使用Vert。后端为x,前端为AngularJS。 Vert. x服务器使用POST和GET方法接收HTTP操作。不知何故,我为每个请求获取不同的会话ID。 以下是来自我的LoginFormHandler类句柄例程的代码片段。 我正在将用户对象放入当前会话中。然后我移动到新页面并向Vert. x服务器发送POST请求。在那个POST处理程序中,我正在尝试获取会话对象: 我没有得到用户。此外,

    • 我一直在搜索net,试图找到任何定义如何在流星和响应路由器4中处理身份验证的地方。 基本上,我希望某些路由只对经过身份验证的用户可用。有相关文件吗? 阿西尔

    • 我想通过Spring Cloud介绍Zuul作为一个API网关在少数服务前面。 我关心的问题: > 网关将位于许多服务的前面 某些服务可能公开不需要身份验证的endpoint null