概念
会话:session,对于无状态的HTTP 实现用户状态可维持的一种解决方案。
-
HTTP的无状态特点使得用户给与在服务器交互的过程中,每个请求间没有关联性,即用户的访问没有身份记录,站点无法为用户提供个性化服务。session的诞生解决这个问题(利用session保存用户身份)
-
服务器通过与用户约定,每个请求都会携带一个id类的信息,使得不用请求间有了关联;id也方便与用户绑定,就可把不同请求归类到同一用户
-
基于此方案,为了使同一个用户的每个请求都携带一个id,需要一个载体去存储这个id,即cookie
-
当用户首次访问系统时,系统为该用户生成一个sessionId,并添加到cookie中,那么在这个用户的会话session期间,每个请求都会自动携带cookie,由此系统可轻易识别出这是哪个用户的请求
-
但是有时候用户可能处于隐私或者安全考虑,会在浏览器中禁掉cookie,这个时候基于cookie实现的sessionId无法正常使用
-
有些系统使用重写URL来兼容禁用cookie的浏览器,但是容易遭到黑客攻击 http://xxx.com;jsessionid=xxxx
-
黑客只需要一次访问系统,将系统生成的sessionId提取并拼凑在URL上,然后将该URL发送给一些取得信任的用户,用户只要在session有效期通过url登录,该sessionId就会绑定到用户的身份,黑客也可享有同样的会话状态,完全不需要用户名和密码,即会话攻击
-
-
-
一个账户能多处同时登录不是一个好的策略,容易被他人劫持session会话后冒充普通用户访问系统
-
SS提供了完善的会话管理功能
-
会话固定攻击
-
会话超时检测
-
会话并发控制
-
防御固定会话攻击
原理很简单,在用户登录之后重新生成新的session即可(这样就避免用户通过钓鱼链接访问登录系统后,因为绑定钓鱼的session导致钓鱼有了普通用户的身份,无需登录即可访问系统;管他什么链接登进来的,重新生成session,这样旧session被替换掉,相当于黑客与被监测用户失联)
-
SS框架的WebSecurityConfigurerAdapter中getHttp方法,使用sessionManagement会话管理器对session进行了配置
-
SS框架支持四中防御会话固定攻击策略
-
none:不做任何变动,登录后沿用旧session
-
newSession:登录之后创建一个新的session
-
migrateSession:登录后创建一个新的session,并将旧的session中的数据复制过来
-
changeSessionId:不创建新的session,使用由Servlet容器提供的会话固定保护
-
-
SS框架默认使用migrateSession策略
-
也可根据需要重写configure方法,进行手动配置策略(WebSecurityConfig方法集成WebSecurityConfigurerAdapter类)
-
-
在SS中,即使没有配置,也不用担心固定会话攻击,因为SS的HTTP防火墙(StrictHttpFirewall类)会拦截不合法的URL,当试图访问携带session的URL时,会被SS框架重定向到提示500的错误页(没登录的时候使用session访问就会直接报错)
//WebSecurityConfigurerAdapter
protected final HttpSecurity getHttp() throws Exception {
if (this.http != null) {
return this.http;
} else {
AuthenticationEventPublisher eventPublisher = this.getAuthenticationEventPublisher();
this.localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
AuthenticationManager authenticationManager = this.authenticationManager();
this.authenticationBuilder.parentAuthenticationManager(authenticationManager);
Map<Class<?>, Object> sharedObjects = this.createSharedObjects();
this.http = new HttpSecurity(this.objectPostProcessor, this.authenticationBuilder, sharedObjects);
if (!this.disableDefaults) {
// 使用sessionManagement方法进行会话管理
((HttpSecurity)((DefaultLoginPageConfigurer)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)this.http.csrf().and()).addFilter(new WebAsyncManagerIntegrationFilter()).exceptionHandling().and()).headers().and()).sessionManagement().and()).securityContext().and()).requestCache().and()).anonymous().and()).servletApi().and()).apply(new DefaultLoginPageConfigurer())).and()).logout();
ClassLoader classLoader = this.context.getClassLoader();
List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
Iterator var6 = defaultHttpConfigurers.iterator();
while(var6.hasNext()) {
AbstractHttpConfigurer configurer = (AbstractHttpConfigurer)var6.next();
this.http.apply(configurer);
}
}
this.configure(this.http);
return this.http;
}
}
自定义会话管理
配置会话管理的类型为
-
none:不做任何变动,登录后沿用旧session
-
newSession:登录之后创建一个新的session
-
migrateSession:登录后创建一个新的session,并将旧的session中的数据复制过来
-
changeSessionId:不创建新的session,使用由Servlet容器提供的会话固定保护
@EnableWebSecurity // 自带@Configuration,此处无需再加@Configutaion注解
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsServiceImpl userDetailsServiceImpl;
/**
* 建立身份认证方式 AuthenticationManagerBuilder创建一个AuthenticationManager用于进行身份认证
* 包括:内存验证、LDAP验证、基于JDBC的验证、添加UserDetailsService、添加AuthenticationProvider
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 使用密文,加密方式使用自定义的passwordEncoder类
auth.userDetailsService(userDetailsServiceImpl).passwordEncoder(myPasswordEncoder());
}
/**
* 开启认证 配置需要认证的url
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) thro