13. Spring Security session管理

本文介绍了Spring Security中的Session管理,包括如何检测Session超时并重定向、并发控制来限制用户的多个登录会话,以及如何防止Session固定攻击。

session管理

目录

 

1.1     检测session超时

1.2     concurrency-control

1.3     session 固定攻击保护

 

       Spring Security通过http元素下的子元素session-management提供了对Http Session管理的支持。

 

1.1     检测session超时

       Spring Security可以在用户使用已经超时的sessionId进行请求时将用户引导到指定的页面。这个可以通过如下配置来实现。

   <security:http>

      ...

      <!-- session管理,invalid-session-url指定使用已经超时的sessionId进行请求需要重定向的页面 -->

      <security:session-management invalid-session-url="/session_timeout.jsp"/>

      ...

   </security:http>

 

       需要注意的是session超时的重定向页面应当是不需要认证的,否则再重定向到session超时页面时会直接转到用户登录页面。此外如果你使用这种方式来检测session超时,当你退出了登录,然后在没有关闭浏览器的情况下又重新进行了登录,Spring Security可能会错误的报告session已经超时。这是因为即使你已经退出登录了,但当你设置session无效时,对应保存session信息的cookie并没有被清除,等下次请求时还是会使用之前的sessionId进行请求。解决办法是显示的定义用户在退出登录时删除对应的保存session信息的cookie。

   <security:http>

      ...

      <!-- 退出登录时删除session对应的cookie -->

      <security:logout delete-cookies="JSESSIONID"/>

      ...

   </security:http>

       此外,Spring Security并不保证这对所有的Servlet容器都有效,到底在你的容器上有没有效,需要你自己进行实验。

 

1.2     concurrency-control

       通常情况下,在你的应用中你可能只希望同一用户在同时登录多次时只能有一个是成功登入你的系统的,通常对应的行为是后一次登录将使前一次登录失效,或者直接限制后一次登录。Spring Security的session-management为我们提供了这种限制。

       首先需要我们在web.xml中定义如下监听器。

   <listener>

   <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>

   </listener>

 

       在session-management元素下有一个concurrency-control元素是用来限制同一用户在应用中同时允许存在的已经通过认证的session数量。这个值默认是1,可以通过concurrency-control元素的max-sessions属性来指定。

   <security:http auto-config="true">

      ...

      <security:session-management>

         <security:concurrency-control max-sessions="1"/>

      </security:session-management>

      ...

   </security:http>

 

       当同一用户同时存在的已经通过认证的session数量超过了max-sessions所指定的值时,Spring Security的默认策略是将先前的设为无效。如果要限制用户再次登录可以设置concurrency-control的error-if-maximum-exceeded的值为true。

 

   <security:http auto-config="true">

      ...

      <security:session-management>

         <security:concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/>

      </security:session-management>

      ...

   </security:http>

 

       设置error-if-maximum-exceeded为true后如果你之前已经登录了,然后想再次登录,那么系统将会拒绝你的登录,同时将重定向到由form-login指定的authentication-failure-url。如果你的再次登录是通过Remember-Me来完成的,那么将不会转到authentication-failure-url,而是返回未授权的错误码401给客户端,如果你还是想重定向一个指定的页面,那么你可以通过session-management的session-authentication-error-url属性来指定,同时需要指定该url为不受Spring Security管理,即通过http元素设置其secure=”none”。

   <security:http security="none" pattern="/none/**" />

   <security:http>

      <security:form-login/>

      <security:logout/>

      <security:intercept-url pattern="/**" access="ROLE_USER"/>

      <!-- session-authentication-error-url必须是不受Spring Security管理的 -->

      <security:session-management session-authentication-error-url="/none/session_authentication_error.jsp">

         <security:concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/>

      </security:session-management>

      <security:remember-me data-source-ref="dataSource"/>

   </security:http>

 

       在上述配置中我们配置了session-authentication-error-url为“/none/session_authentication_error.jsp”,同时我们通过<security:http security="none" pattern="/none/**" />指定了以“/none”开始的所有URL都不受Spring Security控制,这样当用户进行登录以后,再次通过Remember-Me进行自动登录时就会重定向到“/none/session_authentication_error.jsp”了。

       在上述配置中为什么我们需要通过<security:http security="none" pattern="/none/**" />指定我们的session-authentication-error-url不受Spring Security控制呢?把它换成<security:intercept-url pattern="/none/**"access="IS_AUTHENTICATED_ANONYMOUSLY"/>不行吗?这就涉及到之前所介绍的它们两者之间的区别了。前者表示不使用任何Spring Security过滤器,自然也就不需要通过Spring Security的认证了,而后者是会被Spring Security的FilterChain进行过滤的,只是其对应的URL可以匿名访问,即不需要登录就可访问。使用后者时,REMEMBER_ME_FILTER检测到用户没有登录,同时其又提供了Remember-Me的相关信息,这将使得REMEMBER_ME_FILTER进行自动登录,那么在自动登录时由于我们限制了同一用户同一时间只能登录一次,后来者将被拒绝登录,这个时候将重定向到session-authentication-error-url,重定向访问session-authentication-error-url时,经过REMEMBER_ME_FILTER时又会自动登录,这样就形成了一个死循环。所以session-authentication-error-url应当使用<security:http security="none" pattern="/none/**" />设置为不受Spring Security控制,而不是使用<security:intercept-url pattern="/none/**"access="IS_AUTHENTICATED_ANONYMOUSLY"/>。

 

       此外,可以通过expired-url属性指定当用户尝试使用一个由于其再次登录导致session超时的session时所要跳转的页面。同时需要注意设置该URL为不需要进行认证。

   <security:http auto-config="true">

      <security:form-login/>

      <security:logout/>

      <security:intercept-url pattern="/expired.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>

      <security:intercept-url pattern="/**" access="ROLE_USER"/>

      <security:session-management>

         <security:concurrency-control max-sessions="1" expired-url="/expired.jsp" />

      </security:session-management>

   </security:http>

 

1.3     session 固定攻击保护

       session固定是指服务器在给客户端创建session后,在该session过期之前,它们都将通过该session进行通信。session 固定攻击是指恶意攻击者先通过访问应用来创建一个session,然后再让其他用户使用相同的session进行登录(比如通过发送一个包含该sessionId参数的链接),待其他用户成功登录后,攻击者利用原来的sessionId访问系统将和原用户获得同样的权限。Spring Security默认是对session固定攻击采取了保护措施的,它会在用户登录的时候重新为其生成一个新的session。如果你的应用不需要这种保护或者该保护措施与你的某些需求相冲突,你可以通过session-management的session-fixation-protection属性来改变其保护策略。该属性的可选值有如下三个。

l  migrateSession:这是默认值。其表示在用户登录后将新建一个session,同时将原session中的attribute都copy到新的session中。

l  none:表示继续使用原来的session。

l  newSession:表示重新创建一个新的session,但是不copy原session拥有的attribute。

<think>我们之前提到Spring Security默认的过滤器链中包含了`UsernamePasswordAuthenticationFilter`,但在你提供的列表中却没有出现,而是出现了`JwtAuthenticationFilter`(一个自定义过滤器)。原因如下: ### 原因分析: 1. **自定义配置覆盖了默认行为**: - 当你在项目中添加了自定义的安全配置(例如继承`WebSecurityConfigurerAdapter`或使用`@EnableWebSecurity`的配置类)时,可能会覆盖Spring Security的默认配置。 - 默认情况下,如果启用了表单登录(`formLogin()`),则`UsernamePasswordAuthenticationFilter`会被添加到过滤器链中。但如果你禁用了表单登录,或者配置了其他认证方式(如JWT),则该过滤器不会被添加。 2. **JWT认证替代了表单登录**: - 从你打印的过滤器列表中可以看到有一个`org.example.security.JwtAuthenticationFilter`(自定义的JWT过滤器),这通常用于处理基于Token的认证。 - 当使用JWT等自定义认证方式时,开发者通常会**禁用表单登录**(不调用`formLogin()`方法),因此`UsernamePasswordAuthenticationFilter`不会被激活。 3. **其他可能原因**: - 如果配置了HTTP Basic认证(`httpBasic()`),则会添加`BasicAuthenticationFilter`,但不会添加`UsernamePasswordAuthenticationFilter`。 - 如果配置了其他认证机制(如OAuth2、LDAP等),也会改变默认的过滤器链。 ### 验证方法: 检查你的安全配置类(通常命名为`SecurityConfig`),查看是否包含以下情况: ```java @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http // 如果禁用了表单登录,就不会有UsernamePasswordAuthenticationFilter .formLogin().disable() // 或者根本没有调用formLogin() .addFilter(new JwtAuthenticationFilter(authenticationManager())) // 添加了自定义JWT过滤器 ... // 其他配置 } } ``` ### 解决方案: 如果你希望同时保留表单登录和JWT认证(例如同时支持浏览器和API请求),可以显式启用表单登录并调整过滤器的顺序: ```java http .formLogin() // 启用表单登录(会添加UsernamePasswordAuthenticationFilter) .and() .addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); // 在表单登录前添加JWT过滤器 ``` ### 总结: **没有`UsernamePasswordAuthenticationFilter`是因为你的配置中禁用了表单登录(或未启用),而使用了自定义的JWT认证过滤器替代了默认的用户名密码认证流程。** §§相关问题§§ 1. 如何在Spring Security中同时启用表单登录和JWT认证? 2. 自定义过滤器(如JwtAuthenticationFilter)应该放在Spring Security过滤器链的哪个位置? 3. 禁用Spring Security的表单登录后,如何自定义登录接口? 4. 为什么在Spring Security中自定义过滤器时需要关注过滤器的顺序? 5. 除了UsernamePasswordAuthenticationFilter,Spring Security还有哪些内置的认证过滤器?</think>### 为什么没有 `UsernamePasswordAuthenticationFilter`? 在您列出的过滤器链中,没有出现 `UsernamePasswordAuthenticationFilter` 的原因通常有以下几点: #### 1. **自定义安全配置覆盖了默认行为** - 当您在 `SecurityConfig` 类中自定义了安全配置(如使用 `@EnableWebSecurity`),**默认的表单登录会被禁用** - 例如以下配置会移除表单登录相关的过滤器: ```java @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests() .anyRequest().authenticated() .and() // 没有启用 formLogin() .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); } ``` #### 2. **使用了 JWT 认证替代表单登录** - 您的过滤器链中包含自定义的 `org.example.security.JwtAuthenticationFilter` - 这通常表示您**用 JWT 认证替代了默认的表单登录机制** - `UsernamePasswordAuthenticationFilter` 是专用于处理表单登录(`/login` 端点)的,当您使用 JWT 时它会被移除 #### 3. **特定配置禁用了表单登录** - 如果您在配置中显式禁用了表单登录: ```java http.formLogin().disable(); // 明确关闭表单登录 ``` - 这会直接移除 `UsernamePasswordAuthenticationFilter` 和相关的 `DefaultLoginPageGeneratingFilter` #### 4. **认证机制的选择** - 当您配置了其他认证方式时(如 HTTP Basic 认证),Spring Security 会**自动调整过滤器链**: ```java http.httpBasic(); // 启用HTTP Basic认证 → 添加BasicAuthenticationFilter ``` - 此时 `UsernamePasswordAuthenticationFilter` 会被 `BasicAuthenticationFilter` 替代 ### 验证方法: 1. **检查安全配置类** 查看是否调用了 `formLogin()` 方法。如果没有调用或调用了 `formLogin().disable()`,则不会生成该过滤器。 2. **查看自定义过滤器的位置** 您的 `JwtAuthenticationFilter` 很可能被配置在 `UsernamePasswordAuthenticationFilter` 的位置: ```java http.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); ``` 这种配置会用 JWT 过滤器完全替代表单登录过滤器。 ### 总结: **`UsernamePasswordAuthenticationFilter` 缺失是因为您使用了 JWT 认证方案,覆盖了 Spring Security 的默认表单登录行为。这是自定义安全配置时的正常现象,表明您的认证流程已转向基于 Token 的机制。**
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值