返回顶部
首页 > 资讯 > 精选 >SpringBoot Security如何实现单点登出并清除所有token
  • 109
分享到

SpringBoot Security如何实现单点登出并清除所有token

2023-07-05 00:07:23 109人浏览 安东尼
摘要

今天小编给大家分享一下SpringBoot Security如何实现单点登出并清除所有token的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获

今天小编给大家分享一下SpringBoot Security如何实现单点登出并清除所有token的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

需求

  • A、B、C 系统通过 sso 服务实现登录

  • A、B、C 系统分别获取 Atoken、Btoken、Ctoken 三个 token

  • 其中某一个系统主动登出后,其他两个系统也登出

  • 至此全部 Atoken、Btoken、Ctoken 失效

记录token

pom 文件引入依赖

<dependency>   <groupId>org.springframework.boot</groupId>   <artifactId>spring-boot-starter-data-Redis</artifactId></dependency><dependency>   <groupId>cn.hutool</groupId>   <artifactId>hutool-all</artifactId>   <version>5.7.13</version></dependency>

token 存储类 实现 AuthJdbcTokenStore

  • TokenStore 继承 JdbcTokenStore

  • 使用登录用户的用户名 username 做 Redis 的 key

  • 因为用户登录的系统会有多个,所以 value 使用 Redis 的列表类型来存储 token

  • 设置有效时间,保证不少于 list 里 token 的最大有效时间

@Componentpublic class AuthJdbcTokenStore extends JdbcTokenStore {    public static final String USER_HAVE_TOKEN = "user-tokens:";    @Resource    RedisTemplate redisTemplate;    public AuthJdbcTokenStore(DataSource connectionFactory) {        super(connectionFactory);    }    @Override    public void storeAccessToken(OAuth3AccessToken token, OAuth3Authentication authentication) {        super.storeAccessToken(token, authentication);        if (Optional.ofNullable(authentication.getUserAuthentication()).isPresent()) {            User user = (User) authentication.getUserAuthentication().getPrincipal();            String userTokensKey = USER_HAVE_TOKEN + user.getUsername();            String tokenValue = token.getValue();            redisTemplate.opsForList().leftPush(userTokensKey, tokenValue);            Long seconds = redisTemplate.opsForValue().getOperations().getExpire(userTokensKey);            Long tokenExpTime = getExpTime(tokenValue);            Long expTime = seconds < tokenExpTime ? tokenExpTime : seconds;            redisTemplate.expire(userTokensKey, expTime, TimeUnit.SECONDS);        }    }    private long getExpTime(String accessToken) {        Jwt jwt = JWTUtil.parseToken(accessToken);        cn.hutool.JSON.jsONObject jsonObject = jwt.getPayload().getClaimsJson();        long nowTime = Instant.now().getEpochSecond();        long expEndTime = jsonObject.getLong("exp");        long expTime = (expEndTime - nowTime);        return expTime;    }}

oauth_access_token 使用 JdbcTokenStore 存储 token 需要新增表

CREATE TABLE `oauth_access_token` (  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,  `token_id` varchar(255) DEFAULT NULL,  `token` blob,  `authentication_id` varchar(255) DEFAULT NULL,  `user_name` varchar(255) DEFAULT NULL,  `client_id` varchar(255) DEFAULT NULL,  `authentication` blob,  `refresh_token` varchar(255) DEFAULT NULL,  UNIQUE KEY `authentication_id` (`authentication_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

AuthorizationServerConfigurerAdapter 使用 AuthJdbcTokenStore 做 token 存储

  • 引入 DataSource,因为 JdbcTokenStore 的构造方法必须传入 DataSource

  • 创建按 TokenStore,用 AuthJdbcTokenStore 实现

  • tokenServices 添加 TokenStore

  • endpoints 添加 tokenServices

@Configuration@EnableAuthorizationServerpublic class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {    @Autowired    private DataSource dataSource;...    @Bean    public TokenStore tokenStore() {        JdbcTokenStore tokenStore = new AuthJdbcTokenStore(dataSource);        return tokenStore;    }    @Override    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {        DefaultTokenServices tokenServices = new DefaultTokenServices();        tokenServices.setTokenStore(tokenStore());        endpoints                .authenticationManager(authenticationManager)                .tokenServices(tokenServices)                .accessTokenConverter(converter)        ;    }...}

清除token

  • 继承 SimpleUrlLoGoutSuccesshandler

  • 获取用户名 userName

  • 获取登录时存储在 Redis 的 token 列表

  • token 字符串转换成 OAuth3AccessToken

  • 使用 tokenStore 删除 token

@Componentpublic class AuthLogoutSuccessHandler1 extends SimpleUrlLogoutSuccessHandler {    String USER_HAVE_TOKEN = AuthJdbcTokenStore.USER_HAVE_TOKEN;    @Resource    RedisTemplate redisTemplate;    @Resource    TokenStore tokenStore;    @Override    public void onLogoutSuccess(httpservletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {        if (!Objects.isNull(authentication)) {            String userName = authentication.getName();            String userTokensKey = USER_HAVE_TOKEN + userName;            Long size = redisTemplate.opsForList().size(userTokensKey);            List<String> list = redisTemplate.opsForList().range(userTokensKey, 0, size);            for (String tokenValue : list) {                OAuth3AccessToken token = tokenStore.readAccessToken(tokenValue);                if (Objects.nonNull(token)) {                    tokenStore.removeAccessToken(token);                }            }            redisTemplate.delete(userTokensKey);            super.handle(request, response, authentication);        }    }}

解决登出时长过长

场景:项目运行一段时间后,发现登出时间越来越慢

问题:通过 debug 发现耗时主要在删除 token 那一段

tokenStore.removeAccessToken(token);

原因:随着时间推移,token 越来越多,token 存储表 oauth_access_token 变得异常的大,所以删除效率非常差

解决办法:使用其他 TokenStore,或者清除 oauth_access_token 的表数据

以上就是“SpringBoot Security如何实现单点登出并清除所有token”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网精选频道。

--结束END--

本文标题: SpringBoot Security如何实现单点登出并清除所有token

本文链接: https://lsjlt.com/news/348472.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作