Spring Security 实战干货:实现自定义退出登陆

logout.png

1. 前言

上一篇对 Spring Security 全部内置的 Filter 进行了介绍。今天咱们来实战如何安全退出应用程序。html

2. 咱们使用 Spring Security 登陆后都作了什么

这个问题咱们必须搞清楚!通常登陆后,服务端会给用户发一个凭证。常见有如下的两种:前端

  • 基于 Session 客户端会存 cookie 来保存一个 sessionId ,服务端存一个 Sessionjava

  • 基于 token 客户端存一个 token 串,服务端会在缓存中存一个用来校验此 token 的信息。spring

2. 退出登陆须要咱们作什么

  1. 当前的用户登陆状态失效。这就须要咱们清除服务端的用户状态。
  2. 退出登陆接口并非 permitAll, 只有携带对应用户的凭证才退出。
  3. 将退出结果返回给请求方。
  4. 退出登陆后用户能够经过从新登陆来认证该用户。

3. Spring Security 中的退出登陆

接下来咱们来分析并实战 如何定制退出登陆逻辑。首先咱们要了解 LogoutFilter编程

3.1 LogoutFilter

经过 Spring Security 实战干货:内置 Filter 全解析 咱们知道退出登陆逻辑是由过滤器 LogoutFilter 来执行的。 它持有三个接口类型的属性:json

  1. RequestMatcher logoutRequestMatcher 这个用来拦截退出请求的 URL
  2. LogoutHandler handler 用来处理退出的具体逻辑
  3. LogoutSuccessHandler logoutSuccessHandler 退出成功后执行的逻辑

咱们经过对以上三个接口的实现就能实现咱们自定义的退出逻辑。后端

3.2 LogoutConfigurer

咱们通常不会直接操做 LogoutFilter ,而是经过 LogoutConfigurer 来配置 LogoutFilter。 你能够经过 HttpSecurity#logout() 方法来初始化一个 LogoutConfigurer 。 接下来咱们来实战操做一下。缓存

3.2.1 实现自定义退出登陆请求URL

LogoutConfigurer 提供了 logoutRequestMatcher(RequestMatcher logoutRequestMatcher)logoutUrl(Sring logoutUrl) 两种方式来定义退出登陆请求的 URL 。它们做用是相同的,你选择其中一种方式便可。安全

3.2.2 处理具体的逻辑

默认状况下 Spring Security 是基于 Session 的。LogoutConfigurer 提供了一些直接配置来知足你的须要。以下:cookie

  • clearAuthentication(boolean clearAuthentication) 是否在退出时清除当前用户的认证信息
  • deleteCookies(String... cookieNamesToClear) 删除指定的 cookies
  • invalidateHttpSession(boolean invalidateHttpSession) 是否移除 HttpSession

若是上面知足不了你的须要就须要你来定制 LogoutHandler 了。

3.2.3 退出成功逻辑

  • logoutSuccessUrl(String logoutSuccessUrl) 退出成功后会被重定向到此 URL你能够写一个Controller 来完成最终返回,可是须要支持 GET 请求和 匿名访问 。 经过 setDefaultTargetUrl 方法注入到 LogoutSuccessHandler
  • defaultLogoutSuccessHandlerFor(LogoutSuccessHandler handler, RequestMatcher preferredMatcher) 用来构造默认的 LogoutSuccessHandler 咱们能够经过添加多个来实现从不一样 URL 退出执行不一样的逻辑。
  • LogoutSuccessHandler logoutSuccessHandler 退出成功后执行的逻辑的抽象根本接口。

3.3 Spring Security 退出登陆实战

如今先后端分离比较多,退出后返回json。 并且只有用户在线才能退出登陆。不然不能进行退出操做。咱们采用实现 LogoutHandlerLogoutSuccessHandler 接口这种编程的方式来配置 。退出请求的 url 依然经过 LogoutConfigurer#logoutUrl(String logoutUrl)来定义。

3.3.1 自定义 LogoutHandler

默认状况下清除认证信息 (invalidateHttpSession),和Session 失效(invalidateHttpSession) 已经由内置的SecurityContextLogoutHandler 来完成。咱们自定义的 LogoutHandler 会在SecurityContextLogoutHandler 来执行。

@Slf4j
 public class CustomLogoutHandler implements LogoutHandler {
     @Override
     public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
         User user = (User) authentication.getPrincipal();
         String username = user.getUsername();
         log.info("username: {}  is offline now", username);
     }
 }

以上是咱们实现的 LogoutHandler 。 咱们能够从 logout 方法的 authentication 变量中 获取当前用户信息。你能够经过这个来实现你具体想要的业务。好比记录用户下线退出时间、IP 等等。

3.3.2 自定义 LogoutSuccessHandler

若是咱们实现了自定义的 LogoutSuccessHandler 就没必要要设置 LogoutConfigurer#logoutSuccessUrl(String logoutSuccessUrl) 了。该处理器处理后会响应给前端。你能够转发到其它控制器。重定向到登陆页面,也能够自行实现其它 MediaType ,能够是 json 或者页面

@Slf4j
  public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {
      @Override
      public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
          User user = (User) authentication.getPrincipal();
          String username = user.getUsername();
          log.info("username: {}  is offline now", username);
  
  
          responseJsonWriter(response, RestBody.ok("退出成功"));
      }
  
      private static void responseJsonWriter(HttpServletResponse response, Rest rest) throws IOException {
          response.setStatus(HttpServletResponse.SC_OK);
          response.setCharacterEncoding("utf-8");
          response.setContentType(MediaType.APPLICATION_JSON_VALUE);
          ObjectMapper objectMapper = new ObjectMapper();
          String resBody = objectMapper.writeValueAsString(rest);
          PrintWriter printWriter = response.getWriter();
          printWriter.print(resBody);
          printWriter.flush();
          printWriter.close();
      }
  }

3.3.4 自定义退出的 Spring Security 配置

为了方便调试我 注释掉了咱们 实现的自定义登陆,你能够经过 http:localhost:8080/login 来登陆,而后经过 http:localhost:8080/logout 测试退出。

@Override
          protected void configure(HttpSecurity http) throws Exception {
              http.csrf().disable()
                      .cors()
                      .and()
                      .authorizeRequests().anyRequest().authenticated()
                      .and()
  //                    .addFilterBefore(preLoginFilter, UsernamePasswordAuthenticationFilter.class)
                      // 登陆
                      .formLogin().loginProcessingUrl(LOGIN_PROCESSING_URL).successForwardUrl("/login/success").failureForwardUrl("/login/failure")
                      .and().logout().addLogoutHandler(new CustomLogoutHandler()).logoutSuccessHandler(new CustomLogoutSuccessHandler());
  
          }

4. 总结

本篇 咱们实现了 在 Spring Security 下的自定义退出逻辑。相对比较简单,你能够根据你的业务须要来实现你的退出逻辑。有什么疑问能够经过 关注公众号:Felordcn 来私信提问 。相关DEMO代码也能够经过关注后回复 ss04 获取。

关注公众号:Felordcn获取更多资讯

我的博客:https://felord.cn

相关文章
相关标签/搜索