You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot+Spring Security启用HTTPS后无法暴露HTTP端点的问题

问题描述

我的Spring Boot应用集成了Spring Security,配置了自定义登录页与验证系统,整体运行在HTTPS环境中。为避免给Prometheus配置TLS支持,我想暴露某个HTTP端点供它采集指标。

我尝试了几种配置HTTP/HTTPS双端口的方案,但访问http://localhost:8081/greeting时出现解析HTTP请求头的错误,而https://localhost:8443/greeting可以正常访问。修改Security配置类后问题仍未解决,还出现了从8443到8080的重定向,求解决方案。

错误堆栈信息

2022-10-07 17:21:01.190  INFO 14776 --- [nio-8081-exec-2] o.apache.coyote.http11.Http11Processor   : Error parsing HTTP request header
 Note: further occurrences of HTTP request parsing errors will be logged at DEBUG level.

java.lang.IllegalArgumentException: Invalid character found in method name [0x160x030x010x020x000x010x000x010xfc0x030x030xedv0x87l0xf9G0xb80xf10xae}0xd00x130x1e0xe10x0az0x810xc50xee0xd220xb10xf10xb2O%k0x92Ipd0x95 ]. HTTP method names must be tokens
    at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:419) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:271) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:890) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1743) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
    at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]

现有配置代码

Security配置类

package com.andrekreou.iot.authentication.security;

import com.andrekreou.iot.authentication.user.ApplicationUserService;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@AllArgsConstructor
@EnableWebSecurity
public class ApplicationSecurityConfig {

    private final ApplicationUserService applicationUserService;

    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    @Bean
    protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .requiresChannel()
                    .antMatchers("/greeting")
                    .requiresInsecure()
                .and()
                .authorizeRequests()
                    .antMatchers("/api/v*/registration/**","/register*","/login","/registration","/registration-complete","/greeting").permitAll()
                    .anyRequest()
                    .authenticated()
                    .and()
                .formLogin()
                    .loginPage("/login")
                    .usernameParameter("email")
                    .permitAll()
                    .defaultSuccessUrl("/",true)
                .and()
                .logout()
                    .logoutUrl("/logout")
                    .clearAuthentication(true)
                    .invalidateHttpSession(true)
                    .deleteCookies("JSESSIONID","Idea-2e8e7cee")
                    .logoutSuccessUrl("/login");

        return http.build();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider provider =
                new DaoAuthenticationProvider();
        provider.setPasswordEncoder(bCryptPasswordEncoder);
        provider.setUserDetailsService(applicationUserService);
        return provider;
    }
}

主类

package com.andrekreou.iot;

import io.micrometer.core.aop.TimedAspect;
import io.micrometer.core.instrument.MeterRegistry;
import org.apache.catalina.connector.Connector;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@SpringBootApplication
@EnableJpaRepositories
public class IotApplication {

    public static void main(String[] args) {
        SpringApplication.run(IotApplication.class, args);
    }

    @Bean
    public TimedAspect timedAspect(MeterRegistry registry) {
        return new TimedAspect(registry);
    }

    @Bean
    public ServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
        tomcat.addAdditionalTomcatConnectors(createStandardConnector());
        return tomcat;
    }

    private Connector createStandardConnector() {
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        connector.setPort(httpPort);
        return connector;
    }

    @Value("${server.http.port}")
    private int httpPort;
}

配置文件(application.properties)

#Server properties for HTTPS configuration
server.ssl.enabled=true
server.ssl.key-store-type=PKCS12
server.ssl.key-store=classpath:local-ssl.p12
server.ssl.key-store-password=Puredrummer1
server.ssl.key-password=Puredrummer1
server.servlet.context-path=/
server.ssl.key-alias=local_ssl
server.port=8443
server.http.port=8081

测试控制器

package com.andrekreou.iot.control.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Sample {

    @GetMapping(value = "/greeting")
    public String greeting() {
        return "I am working with both HTTP and HTTPS";
    }
}

更新说明

修改Security配置类后,错误仍未解决,且出现从8443到8080的重定向。


解决方案

1. 错误根源分析

请求解析错误是因为用HTTPS协议访问了HTTP端口8081——请求里的0x16是TLS握手标志位,Tomcat的HTTP连接器收到HTTPS请求自然解析失败。

重定向到8080的问题,是Spring Security的requiresChannel规则与Tomcat连接器配置不匹配导致的。

2. 修复步骤

步骤1:修正Tomcat连接器配置

确保HTTP连接器明确为HTTP模式,不继承主端口的SSL配置:

private Connector createStandardConnector() {
    Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
    // 明确标记为HTTP协议
    connector.setScheme("http");
    connector.setSecure(false);
    connector.setPort(httpPort);
    // 设置重定向到HTTPS端口,避免地址错误
    connector.setRedirectPort(8443);
    return connector;
}

步骤2:调整Spring Security通道规则

根据需求选择以下两种配置之一:

  • 需求1:仅/greeting允许HTTP,其余强制HTTPS
@Bean
protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
            .csrf().disable()
            .requiresChannel(channel -> channel
                    .antMatchers("/greeting").requiresInsecure()
                    .anyRequest().requiresSecure()
            )
            .authorizeRequests()
                    .antMatchers("/api/v*/registration/**","/register*","/login","/registration","/registration-complete","/greeting").permitAll()
                    .anyRequest().authenticated()
                    .and()
            .formLogin()
                    .loginPage("/login")
                    .usernameParameter("email")
                    .permitAll()
                    .defaultSuccessUrl("/",true)
            .and()
            .logout()
                    .logoutUrl("/logout")
                    .clearAuthentication(true)
                    .invalidateHttpSession(true)
                    .deleteCookies("JSESSIONID","Idea-2e8e7cee")
                    .logoutSuccessUrl("/login");

    return http.build();
}
  • 需求2:/greeting同时支持HTTP和HTTPS,其余强制HTTPS
    移除requiresInsecure()规则,改为仅对非/greeting的请求强制HTTPS:
.requiresChannel(channel -> channel
        .requestMatchers(r -> !"/greeting".equals(r.getRequestURI()))
        .requiresSecure()
)

步骤3:验证配置

启动应用后:

  1. 访问http://localhost:8081/greeting,应正常返回内容;
  2. 访问https://localhost:8443/greeting,根据配置要么正常返回,要么重定向到HTTP;
  3. 访问登录页等其他页面,确认仍强制使用HTTPS。

内容的提问来源于stack exchange,提问作者Alex_Pap

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 17:20:31