微服务使用openfeign调用单点的会话失效问题

发布时间 2023-05-19 19:57:33作者: 白嫖老郭

项目Springcloud,认证中心方式实现SSO使用开源框架Sa-Token

本身的单独访问每个客户端服务的单点就没有问题。然后单点通过Fegin调用就不好使了!

主要使用的Sa-Token的微服务单点功能
使用的依赖如下
  <!--SA-Token SSO-->
    <dependencyManagement>
        <dependencies>
            <!-- Sa-Token 权限认证, 在线文档:https://sa-token.cc -->
            <dependency>
                <groupId>cn.dev33</groupId>
                <artifactId>sa-token-spring-boot-starter</artifactId>
                <version>1.33.0</version>
            </dependency>
            <!-- Sa-Token 插件:整合SSO -->
            <dependency>
                <groupId>cn.dev33</groupId>
                <artifactId>sa-token-sso</artifactId>
                <version>1.33.0</version>
            </dependency>

            <!-- Sa-Token 整合redis (使用jackson序列化方式) -->
            <dependency>
                <groupId>cn.dev33</groupId>
                <artifactId>sa-token-dao-redis-jackson</artifactId>
                <version>1.33.0</version>
            </dependency>

            <!-- Sa-Token插件:权限缓存与业务缓存分离 -->
            <dependency>
                <groupId>cn.dev33</groupId>
                <artifactId>sa-token-alone-redis</artifactId>
                <version>1.33.0</version>
            </dependency>

        </dependencies>
    </dependencyManagement>

问题就是A服务通过Fegin调用B服务,然后 StpUtil.isLogin();是False

统一的认证处理拦截器

/**
 * @description: SA-Token 认证中心登录状态校验拦截器
 * @author: GuoTong
 * @createTime: 2022-10-05 15:40
 * @since JDK 1.8 OR 11
 **/
public class AuthenticationInterceptor implements HandlerInterceptor {
    /**
     * Description:  主要流程:
     * <p>
     * 1.从 http 请求头中取出 token,
     * 2.判断是否映射到方法
     * 3.检查是否有SkipTokenByJWT注解注释,有则跳过认证
     * 4.检查有没有需要用户登录的注解NeedTokenByJWT,有则需要取出并验证
     * 5.认证通过则可以访问,不通过会报相关错误信息
     */

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        // 如果不是映射到方法直接通过
        if (!(object instanceof HandlerMethod)) {
            return true;
        }
        String requestURI = httpServletRequest.getRequestURI();
        // 判断是否是认证中心对外认证接口
        if (requestURI.contains("/sso")) {
            return true;
        }
        //  获取当前会话是否已经登录,返回true=已登录,false=未登录 ||只能获取本服务的登录状态 StpUtil.isLogin
        boolean login = StpUtil.isLogin();
        if (!login) {
            // 跳转认证中心 httpServletResponse.sendRedirect("/sso/login?back=" + httpServletRequest.getRequestURL());
            throw new NotLoginException(ContextCommonMsg.LOGIN_STATUS_EXPIRE);
        }
        return true;
    }
}

明明都已经登录了Sa-Token集成的OSSServer服务器

但是Fegin调用过程中,发现请求老是被拦截去重定向到SSO-server的登录页

按下F12看看Sa-token在浏览器上做了啥。已经登录的系统Sa-token会放一个Cookie,且所有集成于Sa-token的应用,访问的时候都会放这个玩意。所有集成Sa-token的SSo-Server的客户端的Cookie完全一致,达到单点登录目的。

但是Fegin调用为什么不通,难道这个Cookie没有带过去。

于是就拦截Fegin发送请求的Request,鞋带上Cookie在Request·的Header里

 @Bean
    public RequestInterceptor requestInterceptor() {
        return template -> {
            // 获取请求进入的数据
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

            if (requestAttributes != null) {
                // fegin调用前的原始请求
                HttpServletRequest request = requestAttributes.getRequest();
                if (request != null) {
                    String cookie = request.getHeader("Cookie");
                    // 添加cookie
                    template.header("Cookie", cookie);
                    // 添加Content-Type
                    String contentType = request.getContentType();
                    template.header("Content-Type", contentType);
                }
            }

        };
    }

本身的单独访问每个客户端服务的单点就没有问题。然后单点通过Fegin调用就好使了,;

未登录访问任何一个微服务系统都会被重定向到SSO服务器的登录页

image

登录完成后会重定向回来:Fegin调用不会走到SO服务器的登录页,获取到接口数据了

image

但是任意一个系统点了注销,所有系统都会被注销