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

Spring Security 实战干货:实现自定义退出登录的更多相关文章

  1. Spring Security 实战干货:图解用户是如何登录的

    1. 前言 欢迎阅读Spring Security 实战干货系列文章,在集成Spring Security安全框架的时候我们最先处理的可能就是根据我们项目的实际需要来定制注册登录了,尤其是Http登录 ...

  2. Spring Security 实战干货:OAuth2登录获取Token的核心逻辑

    1. 前言 在上一篇Spring Security 实战干货:OAuth2授权回调的核心认证流程中,我们讲了当第三方同意授权后会调用redirectUri发送回执给我们的服务器.我们的服务器拿到一个中 ...

  3. Spring Security 实战干货:AuthenticationManager的初始化细节

    1. 前言 今天有个同学告诉我,在Security Learning项目的day11分支中出现了一个问题,验证码登录和其它登录不兼容了,出现了No Provider异常.还有这事?我赶紧跑了一遍还真是 ...

  4. Spring Security 实战干货:使用 JWT 认证访问接口

    (转载)原文链接:https://my.oschina.net/10000000000/blog/3127268 1. 前言 欢迎阅读Spring Security 实战干货系列.之前我讲解了如何编写 ...

  5. Spring Security 实战干货: 简单的认识 OAuth2.0 协议

    1.前言 欢迎阅读 Spring Security 实战干货 系列文章 .OAuth2.0 是近几年比较流行的授权机制,对于普通用户来说可能每天你都在用它,我们经常使用的第三方登录大都基于 OAuth ...

  6. Spring Security 实战干货:如何实现不同的接口不同的安全策略

    1. 前言 欢迎阅读 Spring Security 实战干货 系列文章 .最近有开发小伙伴提了一个有趣的问题.他正在做一个项目,涉及两种风格,一种是给小程序出接口,安全上使用无状态的JWT Toke ...

  7. Spring Security 实战干货:图解Spring Security中的Servlet过滤器体系

    1. 前言 我在Spring Security 实战干货:内置 Filter 全解析对Spring Security的内置过滤器进行了罗列,但是Spring Security真正的过滤器体系才是我们了 ...

  8. Spring Security 实战干货:理解AuthenticationManager

    1. 前言 我们上一篇介绍了UsernamePasswordAuthenticationFilter的工作流程,留下了一个小小的伏笔,作为一个Servlet Filter应该存在一个doFilter实 ...

  9. Spring Security 实战干货:OAuth2第三方授权初体验

    1. 前言 Spring Security实战干货系列 现在很多项目都有第三方登录或者第三方授权的需求,而最成熟的方案就是OAuth2.0授权协议.Spring Security也整合了OAuth2. ...

随机推荐

  1. boot camp要求独立的fat分区

    先在windows把u盘用diskpart clean掉分区表 然后在苹果里面,给u盘重新分区,分区表不要选择GUID,选择主引导记录,格式选择fat,OK

  2. ‘100%’wuxiao

    有时候设置控件的width:’100%‘无效,  群友解释, 因为父控件的大小要靠自空间撑起来,确定    ??????

  3. integrator.setTimeout 设置一个超时时间,超过这个时间之后,扫描的 Activity 将会被 finish 。

    integrator.setTimeout 设置一个超时时间,超过这个时间之后,扫描的 Activity 将会被 finish . +++++++++++++++++++ 经查,没有这个功能

  4. hostapd阅读(openwrt)-4

    接下来,咱们来看看hostapd的源码目录之hostapd,今天我们先分析整体功能,然后从main.c开始注释 hostapd下代码主要作用有:配置解析,环境初始化,控制接口建立,AP接口管理模块. ...

  5. MacOS下Lucene学习

    学于黑马和传智播客联合做的教学项目 感谢 黑马官网 传智播客官网 微信搜索"艺术行者",关注并回复关键词"lucene"获取视频和教程资料! b站在线视频 全文 ...

  6. MacOS下ElasticSearch学习(第二天)

    ElasticSearch第二天 学于黑马和传智播客联合做的教学项目 感谢 黑马官网 传智播客官网 微信搜索"艺术行者",关注并回复关键词"elasticsearch&q ...

  7. Python os.closerange() 方法

    概述 os.closerange() 方法用于关闭所有文件描述符 fd,从 fd_low (包含) 到 fd_high (不包含), 错误会忽略.高佣联盟 www.cgewang.com 语法 clo ...

  8. HTML 统一资源定位器(Uniform Resource Locators)

    HTML 统一资源定位器(Uniform Resource Locators) URL 是一个网页地址.高佣联盟 www.cgewang.com URL可以由字母组成,如"runoob.co ...

  9. MyBatis-Plus使用(2)-CRUD接口

    参考文档:https://mybatis.plus/guide/crud-interface.html MyBatis-Plus自带的CRUD方法分为Mapper层和Service层,大多数功能是重叠 ...

  10. 动态修改HttpServletRequest的Post请求参数

    需求场景: 公司对APP调用的后台接口有个公用格式如下,外层包含了一些设备.版本.签名信息,主要的业务参数是在body里,外层信息都是在网关解决,验证签名后,在转发body到后台服务. { " ...