本文介绍关于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 会话超时处理逻辑 - 西门夜说 - 博客园