spring-web项目采用token认证session创建过多导致OOM

本文介绍关于session过多,分问题、解决方案、分析在另写文章。

问题:spring-boot微服务互相调用时,采用的是spring-security来认证,比如A服务调用B服务用feign调用方式。

1. 浏览器访问web项目,传递token,Bearer 0d8c2dfa-5a3f-4fde-935e-96da27712687,此时,web服务会拿着token,去用户服务认证,认证通过,放行。每次访问都会在web服务创建一个新的session,有效期是30分钟,即使前端下次访问的时候,传递上次响应的session,web服务也会创建一个新的,这样在短时间内有大量请求时,就会创建大量的session,并且不会回收,就造成OOM。

下载dump文件,用jprofiler分析如图

可以看到session的数量非常多。

解决方案:

在配置http的时候采用固定的session策略:

http.sessionManagement().sessionFixation().none();

完整代码



import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;

import javax.servlet.http.HttpServletResponse;


@Configuration
@EnableResourceServer
@ConditionalOnProperty(prefix = "spring.security.oauth2", name = "enabled", havingValue = "true", matchIfMissing = true)
public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {

    @Value("${spring.application.name}")
    private String resourceId;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId(resourceId);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .exceptionHandling()
            .authenticationEntryPoint((request, response, authException) -> response
                .sendError(HttpServletResponse.SC_UNAUTHORIZED))
            .and()
            .authorizeRequests()
            // 填写不需要token即可调用的接口的路径
            .antMatchers(
                "/v2/api-docs",
                "/favicon.ico",
                "/webjars/**",
                "/csrf",
                "/swagger**",
                "/swagger-resources/**",
                "/course/coursebase/**",
                "/hystrix**",
                "/hystrix/**",
                "/proxy.stream",
                "/actuator/**",
                "/actuator",
                "/cache",
                "/webservices/**")
            .permitAll()
            .anyRequest()
            .authenticated()
            .and()
            //这里配置也可以,下面单独配置也行
            .sessionManagement()
            .sessionFixation()
            .none()
            .and()
            
            .httpBasic();
        //.maximumSessions(30)   最大的session数量
// 采用固定session
//        http.sessionManagement().sessionFixation().none();
//采用合并session,每次都会创建新的,并且把原来session的属性复制到新的里面,原来的session过期,也会创建大量的session
//        http.sessionManagement().sessionFixation().migrateSession();

    }

}

分析在下一篇文章,介绍session管理过滤器,如何创建。

服务之间调用,也会产生大量的session,B服务采用request域,不创建session。



import java.util.Map;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
import org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter;
import org.springframework.security.oauth2.client.token.AccessTokenRequest;
import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest;

@Configuration
@ConditionalOnProperty(
    name = {"my.scope.request"},
    havingValue = "true"
)
public class LeadingOAuth2ClientConfiguration {
    public LeadingOAuth2ClientConfiguration() {
    }

    @Bean
    public OAuth2ClientContextFilter oauth2ClientContextFilter() {
        OAuth2ClientContextFilter filter = new OAuth2ClientContextFilter();
        return filter;
    }

    @Bean
    @Scope(
        value = "request",
        proxyMode = ScopedProxyMode.INTERFACES
    )
    protected AccessTokenRequest accessTokenRequest(@Value("#{request.parameterMap}") Map<String, String[]> parameters, @Value("#{request.getAttribute('currentUri')}") String currentUri) {
        DefaultAccessTokenRequest request = new DefaultAccessTokenRequest(parameters);
        request.setCurrentUri(currentUri);
        return request;
    }

    @Configuration
    @ConditionalOnProperty(
        name = {"my.scope.request"},
        havingValue = "true"
    )
    protected static class OAuth2ClientContextConfiguration {
        @Resource
        @Qualifier("accessTokenRequest")
        private AccessTokenRequest accessTokenRequest;

        protected OAuth2ClientContextConfiguration() {
        }

        @Bean
        @Scope(
            value = "request",
            proxyMode = ScopedProxyMode.INTERFACES
        )
        public OAuth2ClientContext oauth2ClientContext() {
            return new DefaultOAuth2ClientContext(this.accessTokenRequest);
        }
    }
}

在yml中配置 my.scope.request=true 

session在服务之间共享,服务调用传递session,也可以。

参考文章

这篇session回话写的不错

SpringSecurity(九): 自定义Session 会话超时处理逻辑 - 西门夜说 - 博客园

关于feign调用时,session丢失的解决方案 - 程序员大本营

Spring,无法在Java配置中更改SessionAuthenticationStrategy-Java 学习之路

### 使用 `spring-boot-starter-oauth2-client` 实现 Token 自动刷新 为了在 Spring Boot 应用中使用 `spring-boot-starter-oauth2-client` 来实现 OAuth2.0 token 的自动刷新,需确保应用程序能够处理访问令牌过期的情况并请求新的访问令牌。以下是具体的方法: #### 1. 添加依赖项 首先,在项目的 `pom.xml` 文件中添加必要的 Maven 依赖项以支持 OAuth2 客户端功能。 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-client</artifactId> </dependency> ``` 此操作允许开发者利用内置的支持来进行 OAuth2 流程管理[^2]。 #### 2. 配置 application.properties 或者 application.yml 接着配置文件中的属性来指定授权服务器的信息以及所需的范围(scope),这有助于框架理解何时何地去获取新 tokens。 对于 `application.properties`: ```properties spring.security.oauth2.client.registration.myclient.client-id=your_client_id spring.security.oauth2.client.registration.myclient.client-secret=your_secret_key spring.security.oauth2.client.registration.myclient.authorization-grant-type=authorization_code spring.security.oauth2.client.registration.myclient.scope=read,write spring.security.oauth2.client.provider.myprovider.token-uri=https://example.com/oauth/token ``` 如果采用 YAML 格式,则相应的配置如下所示: ```yaml spring: security: oauth2: client: registration: myclient: client-id: your_client_id client-secret: your_secret_key authorization-grant-type: authorization_code scope: read, write provider: myprovider: token-uri: https://example.com/oauth/token ``` 这些设置定义了客户端 ID、密钥以及其他必要参数以便于与外部身份验证服务交互[^1]。 #### 3. 创建自定义 Token Services 类 (可选) 当默认的行为不满足需求时,可以通过继承 `DefaultTokenServices` 并重写其部分方法来自定义行为,比如调整 refresh access token 的逻辑[^3]。 ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.provider.token.DefaultTokenServices; @Configuration public class CustomTokenConfig { @Bean public DefaultTokenServices customTokenService() { DefaultTokenServices service = new DefaultTokenServices(); // 进行相应定制化修改... return service; } } ``` #### 4. 启用自动刷新机制 Spring Security OAuth 提供了一个叫做 `ReactiveOAuth2AuthorizedClientManager` 接口用于管理和维护已授权客户的会话状态。通过这个接口可以很容易地集成到 WebFlux 或 MVC 控制器里,并且它内部已经包含了对 token 刷新的支持。 要启用自动刷新特性,只需按照上述方式正确设置了所有必需的属性即可;一旦检测到当前使用的 access_token 即将到期,系统将会尝试使用 refresh_token 获取一个新的有效凭证[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值