关于spring:一个诡异的登录问题

41次阅读

共计 3661 个字符,预计需要花费 10 分钟才能阅读完成。

美妙周末,从解 BUG 开始!

周五原本想早点上班,临了有一个简略的需要忽然提上来,心想着整完了就走,没想到一下折腾了 1 个多小时才搞定,欢快的周末就从加班中开启了。回到家里把这件事复盘一下,小伙伴们看看是否可能从中 GET 到一些未知的货色。

需要是这样的:

我的项目是 Spring Boot 我的项目,里边对申请进行了划分,有的申请是 HTTP 协定,有的申请是 HTTPS 协定,我的项目规定,有一些申请必须是 HTTPS 协定,例如 /https 接口,该接口必须应用 HTTPS 协定拜访,如果用户应用了 HTTP 协定拜访,那么会主动产生申请重定向,重定向到 HTTPS 协定上;同时也有一些申请必须是 HTTP 协定,例如 /http 接口,该接口必须应用 HTTP 协定拜访,如果用户应用了 HTTPS 协定拜访,那么会主动产生申请重定向,重定向到 HTTP 协定上。对于一些没有明确规定的接口,当用户拜访 HTTP 协定时,不须要主动跳转到 HTTPS 协定上,即用户如果应用 HTTP 协定就是 HTTP 协定,用户如果应用 HTTPS 协定就是 HTTPS 协定。

这个工作切实是小 case,因为我的项目自身曾经反对 HTTPS 了,我只须要再增加一个 HTTP 监听的端口即可 (Spring Boot 中配置 Https),增加如下配置:

@Configuration
public class TomcatConfig {
    @Bean
    TomcatServletWebServerFactory tomcatServletWebServerFactory() {TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        factory.addAdditionalTomcatConnectors(createTomcatConnector());
        return factory;
    }
    private Connector createTomcatConnector() {
        Connector connector = new
                Connector("org.apache.coyote.http11.Http11NioProtocol");
        connector.setScheme("http");
        connector.setPort(8080);
        return connector;
    }
}

增加实现后,我的项目启动日志如下:

能够看到,我的项目曾经同时反对 HTTPSHTTP 了,两者别离在不同的端口上监听。

接下来利用 Spring Security 中的 HTTPS 校验转发性能对申请进行辨别:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.
        // 省略其余
                .requiresChannel()
                .antMatchers("/https").requiresSecure()
                .antMatchers("/http").requiresInsecure()
                .and()
                .csrf().disable();
    }
}

功败垂成,so easy!

配置实现后,启动我的项目,如下两个地址都能够拜访到登录页面:

  • http://localhost:8080/login
  • https://localhost:8444/login

能够应用任意一个地址登录。

如果应用了 HTTP 协定登录,登录胜利后,如果间接拜访 http://localhost:8080/http 申请,能够间接拜访到,没有任何问题;如果登录胜利后拜访 http://localhost:8080/https 申请,则会主动重定向到 https://localhost:8444/https,所有看起来都很完满。

仿佛能够上班了。

别急,我再用 HTTPS 登录测试了,关上 https://localhost:8444/login 页面,登录胜利,申请 https://localhost:8444/https 地址没有问题,申请 https://localhost:8444/http,傻眼了。

当我应用 HTTPS 登录胜利后,申请 https://localhost:8444/http 地址时,按理说会重定向到 http://localhost:8080/http,后果并没有,而是重定向到登录页面,这是咋回事?更为诡异的是,当初在登录页面,无论我怎么做,都登录失败。

看来 965 到底是空中楼阁,还是持续解决问题吧。

那就从登录开始,好端端的为什么忽然就无奈登录了呢?

先革除浏览器缓存试试?咦,革除浏览器缓存后登录胜利了!

通过屡次尝试后,我总结进去了如下法则:

如果应用 HTTP 协定登录,登录胜利后,HTTP 协定和 HTTPS 协定之间相互重定向没有任何问题。如果应用了 HTTPS 协定登录,登录胜利后,HTTPS 协定重定向到 HTTP 协定时,须要从新登录,并且在登录页面总是登录失败,须要革除浏览器缓存能力登录胜利。

先找到法则这个很重要,有的小伙伴微信问松哥问题时候,喜爱说, 这个货色它一会能够一会又不行 ,诚实说,这个问题提的十分业余!所有看似无规律的 BUG 背地都是有法则的,找到法则才是解决 BUG 的第一步。

在整个过程中,最为诡异的是从 HTTPS 重定向到 HTTP 之后,无论怎么样都登录不了,服务端重启也没用,只能革除浏览器缓存,这个十分奇怪,我感觉就先从这个中央动手 DEBUG。

那就 DEBUG,浏览器发送登录申请,服务端我把 Spring Security 登录流程走了一遍,貌似没问题,登录胜利后重定向到 http://localhost:8080/,这也是失常的,持续 DEBUG,重定向到 http://localhost:8080/ 地址时,呈现了一点点意外,该申请在 Spring Security 过滤器链的最初一个环节 FilterSecurityInterceptor 中执行时候抛出异样了,异样起因是因为检查用户身份,发现这是个匿名用户!(一文搞定 Spring Security 异样解决机制!)

不对呀,一开始曾经登录胜利了,怎么会是匿名用户呢?Spring Security 在登录胜利后,会将用户信息保留在 SecurityContextHolder 中(在 Spring Security 中,我就想从子线程获取用户登录信息,怎么办?),是不是没保留?从新查看登录过程,发现登录胜利后是保留了用户信息的。然而当登录胜利后再次发送申请却说我没登录,还剩一种可能,是不是前端申请的问题,JSESSIONID 拿错了?或者没拿?

浏览器 F12 查看前端申请,发现登录胜利后,重定向到 http://localhost:8080/ 地址时,果然没有携带 Cookie!

当初的问题是为什么它就不携带 Cookie 呢?

一瞬间脑子里闪过了诸多可能性,是不是浏览器 SameSite 机制导致的?是不是。。。最初思维定格在 Cookie 的 Secure 标记上。

如果申请是 HTTPS,则服务端响应的 Cookie 中含有 Secure 标记:

这个标记示意该 Cookie 只能够在平安环境下(HTTPS)传输,如果申请是 HTTP 协定,则不会携带该 Cookie。这样就能解释通为什么登录胜利后重定向时不携带 Cookie 了。

新的问题来了,我应用的是 HTTP 协定登录,为什么 Cookie 中有 Secure 标记呢?答复这个问题,咱们要残缺的梳理一遍登录过程。

首先咱们应用 HTTPS 协定登录,登录胜利后,返回的 Cookie 中含有 Secure 标记,接下来咱们拜访 https://localhost:8444/http,该申请重定向到 http://localhost:8080/http,重定向的申请是 HTTP 申请,而 Cookie 只能够在 HTTPS 环境下传输,所以不会携带 Cookie,服务端认为这是一个匿名申请,所以要求重定向到登录页面,回到登录页面持续登录,此时发动的登录是 HTTP 申请,即端口是 8080,因为 Cookie 并不会辨别端口号,所以应用 8080 登录胜利后,应用的还是之前 8444 生成的 Cookie,然而 8080 又无奈在发送申请时,主动携带该 Cookie,所以看到的就是总是登录失败,当革除浏览器缓存后,8444 的 Cookie 就被革除了,8080 再次登录就能够生成本人的没有 Secure 标记的 Cookie,此时所有又恢复正常了。

这里边其实次要波及到两个知识点:

  1. 含有 Secure 标记的 Cookie 只能够在平安环境下(HTTPS)传输。
  2. Cookie 是不辨别端口号的,如果 Cookie 名雷同,会主动笼罩,并且读取的是雷同的数据。所以 8080 和 8444 并不会主动应用两个 Cookie。

至此,总算搞清楚这个诡异的登录问题了。那么接下来的解决方案就很容易了。

还是那句话,所有看似无规律的 BUG 都是有法则的,找到法则才有解决问题的可能性!

正文完
 0