【问题标题】:Spring Security on GCP App Engine hangs 30+ seconds for each request then errorsGCP App Engine 上的 Spring Security 为每个请求挂起 30 多秒,然后出错
【发布时间】:2020-07-17 09:47:23
【问题描述】:

我有一个简单的 Spring Boot 2.3.1 应用程序,它使用 Spring Security 来管理用户登录/注销(使用最新的依赖项)。用户/角色域模型存储的后端存储是 Postgres(我正在使用 JPA 来访问它们)。在本地一切正常,但是当我通过 gcloud app deploy 部署到 GAE 时,站点上的每个页面——甚至是 /login——页面都尝试加载 30 秒,然后出现以下 500 错误消息:

Error: Server Error
The server encountered an error and could not complete your request.

Please try again in 30 seconds.

使用gcloud app logs tail -s default 查看 GAE 错误日志会显示所有正常行为。与 Postgres 的数据库连接很好,没有 WARN 或 ERROR 块。我几乎想知道它是否比我的代码拦截请求然后停止更高级,比如 ELB?

这是我的pom.xml

   <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>ch.qos.logback</groupId>
                    <artifactId>logback-classic</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
        <dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
            <version>3.4.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>nz.net.ultraq.thymeleaf</groupId>
            <artifactId>thymeleaf-layout-dialect</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.google.cloud.sql</groupId>
            <artifactId>postgres-socket-factory</artifactId>
            <version>1.0.16</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>ch.qos.logback</groupId>
                    <artifactId>logback-classic</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR6</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>

以及带有 Spring Security 覆盖的 @Configuration 类:


@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Autowired
    private MemberService memberService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        String authByUsernameQuery = "select m.username as principal, name from member m" +
                " inner join member_role mr on m.id = mr.member_id" +
                " inner join role r on mr.role_id = r.id" +
                " where m.username=?";

        auth.jdbcAuthentication().dataSource(dataSource)
                .usersByUsernameQuery("select username as principal, password as credentials, true from member where username=?")

                .authoritiesByUsernameQuery(authByUsernameQuery)
                .passwordEncoder(passwordEncoder()).rolePrefix("ROLE_");
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager customAuthenticationManager() throws Exception {
        return authenticationManager();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();

        // force https -- does this need more work somehow?
        // Still doesn't work in GAE when commented out
        http.requiresChannel()
                .anyRequest().requiresSecure();

        http.authorizeRequests().antMatchers("/register", "/login", "/status").permitAll()
                .antMatchers("/index").hasAnyRole("USER, ADMIN")
                .and().formLogin().loginPage("/login").permitAll()
                .defaultSuccessUrl("/")
                // smart logout from spring (no need for explicit endpoint)
                .and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login");
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(memberService).passwordEncoder(passwordEncoder());
    }
}

下面是带有登录方法的控制器示例,但由于停顿,我无法访问此允许的页面:

@Controller
public class MemberController {

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

    @Autowired
    private MemberService memberService;

    @Autowired
    private AppConfig appConfig;

    @RequestMapping("/status")
    @GetMapping
    public String check() {
        return "OK";
    }

    @GetMapping("/login")
    public String showLoginForm(){
        return "views/loginForm";
    }

因此,那些端点——以及我尝试过的所有其他端点——都会挂起,直到出现错误。项目 UI 页面使用 Thymeleaf 和资源/中的标准设置:

我现在被阻止了。我进行了一个更简单的实验来部署另一个没有 Spring Security 的 Java 应用程序,它工作得很好。然后我将 Spring Security 添加到这个更简单的项目中,它会起作用,因为我会立即收到 Spring Security 的默认登录屏幕的提示。然而我的代码一切都挂了,我从来没有成功加载过一个页面。我觉得它一定是我还没有为 HTTPS/SSL/Cert 添加的东西,但我不知道是什么。

有什么想法吗?提前致谢。

【问题讨论】:

    标签: java spring-boot google-cloud-platform spring-security


    【解决方案1】:

    固定。

    超时与 Spring Security 或 SSL 无关。 GCP 实例 F1 的默认大小是 256MB RAM,这导致了足够的资源紧张,一旦我包含 Spring Security 和一些其他依赖项,应用程序在尝试处理请求时崩溃。没有像您通常看到的那样明确的OutOfMemoryError,而是我一遍又一遍地在日志中找到了 Spring 欢迎文本。我将实例大小提高到 F4,效果很好。

    这让我陷入了困境,但至少安全性看起来不错。

    【讨论】:

    • 为你加油!谢谢你拯救了我的夜晚:-)
    猜你喜欢
    • 2022-01-25
    • 1970-01-01
    • 1970-01-01
    • 2017-07-28
    • 2013-10-06
    • 2011-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多