文档章节

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

码农小胖哥
 码农小胖哥
发布于 10/23 18:20
字数 1373
阅读 78
收藏 3

logout.png

1. 前言

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

2. 我们使用 Spring Security 登录后都做了什么

这个问题我们必须搞清楚!一般登录后,服务端会给用户发一个凭证。常见有以下的两种:

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

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

2. 退出登录需要我们做什么

  1. 当前的用户登录状态失效。这就需要我们清除服务端的用户状态。
  2. 退出登录接口并不是 permitAll, 只有携带对应用户的凭证才退出。
  3. 将退出结果返回给请求方。
  4. 退出登录后用户可以通过重新登录来认证该用户。

3. Spring Security 中的退出登录

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

3.1 LogoutFilter

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

  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 提供了一些直接配置来满足你的需要。如下:

  • 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

© 著作权归作者所有

码农小胖哥

码农小胖哥

粉丝 53
博文 100
码字总数 116348
作品 1
郑州
程序员
私信 提问
Spring Security 实战干货: 登录后返回 JWT Token

前言 欢迎阅读 Spring Security 实战干货 系列文章,上一文 我们实现了 JWT 工具。本篇我们将一起探讨如何将 JWT 与 Spring Security 结合起来,在认证成功后不再跳转到指定页面而是直接返回...

码农小胖哥
10/28
1K
3
Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密...

小致Daddy
2018/08/03
23.5K
1
Spring Security 实战干货:玩转自定义登录

前言 前面的关于 Spring Security 相关的文章只是一个预热。为了接下来更好的实战,如果你错过了请从 Spring Security 实战系列 开始。安全访问的第一步就是认证(),认证的第一步就是登录。...

码农小胖哥
10/18
68
0
Spring Security 实战干货:自定义配置类入口WebSecurityConfigurerAdapter

前言 今天我们要进一步的的学习如何自定义配置 Spring Security 我们已经多次提到了 ,而且我们知道 Spring Boot 中的自动配置实际上是通过自动配置包下的 总配置类上导入的 Spring Boot We...

码农小胖哥
10/16
108
0
Spring Security 实战干货:必须掌握的一些内置 Filter

前言 上一文我们使用 Spring Security 实现了各种登录聚合的场面。其中我们是通过在 之前一个自定义的过滤器实现的。我怎么知道自定义过滤器要加在 之前。我在这个系列开篇说了 Spring Secur...

码农小胖哥
10/22
110
0

没有更多内容

加载失败,请刷新页面

加载更多

你知道多少this,new,bind,call,apply?那我告诉你

那么什么是this,new,bind,call,apply呢?这些你都用过吗?掌握这些内容都是基础中的基础了。如果你不了解,那还不赶快去复习复习,上网查阅资料啥的! 通过call,apply,bind可以改变thi...

达达前端小酒馆
今天
5
0
设计模式之命令模式

命令模式的类图 其中的角色有: Client 客户端。只依赖于调用者Invoker、接收者Receiver、以及Command(网上找的图片这里没有画出来),不用关注接收者如何执行命令,只需要告诉调用者需要执行...

陈年之后是青葱
今天
7
0
2. 彤哥说netty系列之IO的五种模型

你好,我是彤哥,本篇是netty系列的第二篇。 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识。 简介 本文将介绍linux中的五种IO模型,同时也会介绍阻塞/非阻塞与同步/异步的区别。 何...

彤哥读源码
今天
5
0
OSChina 周四乱弹 —— 喵的波粒二象性

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @ 小小编辑推荐:《水墨兰亭》- 李志辉 《水墨兰亭》- 李志辉 手机党少年们想听歌,请使劲儿戳(这里) @巴拉迪维 :卧室里采光要足够好,这样...

小小编辑
今天
37
1
前后端分离接口规范

最近在开发,遇到前后端关于Boolean类型的参数传参和接收的问题: 场景:后台会根据用户是否出车/是否出司机(Boolean类型)来决定后端的业务逻辑(比如费用的计算),前端使用JSON字符串类型...

code-ortaerc
昨天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部