当前位置:

首页 > 编程开发 > SpringBoot Security如何实现单点登出并清除所有token

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

需求A、B、C系统通过sso服务实现登录A、B、C系统分别获取Atoken、Btoken、Ctoken三个token其中某一个系统主动登出后,其他两个系统也登出至此全部Atoken、Btoken、Ctoken失效记录tokenpom文件引入依赖Redis数据库依赖hutool:用于解析tokenorg.springframework.bootspring-boot-starter-data-rediscn.hutoolhutool-all5.7.13token存储类实现AuthJdbcTokenStore

本文目录

    需求

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

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

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

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

    记录token

    pom 文件引入依赖

    • Redis数据库依赖

    • hutool:用于解析token

    
       org.springframework.boot
       spring-boot-starter-data-redis
    
    
       cn.hutool
       hutool-all
       5.7.13
    

    token 存储类 实现 AuthJdbcTokenStore

    • TokenStore 继承 JdbcTokenStore

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

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

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

    @Component
    public 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
    @EnableAuthorizationServer
    public 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

    @Component
    public 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 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 的表数据

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    Token与Tokenizer是什么?分词、BPE与上下文长度
    Token与Tokenizer是什么?分词、BPE与上下文长度

    深入解析大模型中的Token与Tokenizer工作机制,通过BPE分词示例说明文字如何转化为数字编号,澄清上下文长度与字数的区别,帮助用户准确评估模型处理长文本的实际能力。

    AI重构企业业务架构:超聚变“智企”范式核心解析
    AI重构企业业务架构:超聚变“智企”范式核心解析

    本文解析超聚变在2026数博会发布的“智企”范式,重点阐述如何通过Token生产平台(Token Factory)与企业业务本体建模,实现从简单AI工具调用到企业应用架构系统性重构的演进。文章详细拆解了智能体编排、数字孪生及生态协同等关键技术路径,为AI时代企业数字化转型提供可落地的参考方案。

    PHP | Work 2005 讲座之 PHP效率,PHP安全,WebServices
    PHP | Work 2005 讲座之 PHP效率,PHP安全,WebServices

    PHP|Work2005讲座三大主题:PHP效率优化、WebService实现与陷阱、安全防御(注入、XSS等),每份幻灯片超46页,附资源链接下载。

    总价值上亿元!360全员每人发1亿Token:周鸿祎想通了一件事
    总价值上亿元!360全员每人发1亿Token:周鸿祎想通了一件事

    360公司向全体员工每人发放1亿Token,总价值近亿元,旨在鼓励员工深度使用AI工具以提升效率。此举绕过了传统审批流程,大幅降低使用门槛。公司同时推出“龙虾教练”功能,通过对话式引导帮助员工轻松训练专属AI助手,进一步推动AI技术的普及与应用。

    硅谷程序员开始卷Token消耗量 脸书母公司Meta把AI使用量写进KPI
    硅谷程序员开始卷Token消耗量 脸书母公司Meta把AI使用量写进KPI

    3月27日,据央视财经,在美国硅谷,最近出现了一种新的“攀比”,比的不是加班时间,而是你用了多少AI的“Token”,也就是词元。这种情况在程序员群体当中尤为显著,在一些公司,员工甚至会在内部排行榜上比拼谁消耗的“Token”更多。 想象一下,硅谷的竞争风向又转了。过去可能比谁下班更晚,现在呢?大家

    Token经济崛起:迅策(03317)有望跻身AI“千亿俱乐部”
    Token经济崛起:迅策(03317)有望跻身AI“千亿俱乐部”

    智通财经APP获悉,AI行业正式从“补贴烧钱”跨入“商业化定价时代”。被市场称为“Token第一股”的迅策(03317)迎来利好!叠加近日国泰君安国际已将目标价大幅上调至245港元/股。迅策有望成为AI“千亿俱乐部”的新成员。 中国AI弯道超车:价格优势构筑竞争力 风向正在转变。日前,人工智能公司A

    海光全球首发“机密Token”:把云端大模型装进“笼子”里
    海光全球首发“机密Token”:把云端大模型装进“笼子”里

    4月8日,海光信息全球首发“机密Token”技术,为云端大模型应用上锁 大模型好用,但数据隐私终究是个“心病”。把核心数据明文送上云端,总让人感觉像把钥匙交给了别人。这个痛点,现在有了新的解法。4月8日,海光信息(688041.SH)发布了一项名为“机密Token”的技术。简单来说,这项技术旨在让用

    Vue3+Vite怎么使用双token实现无感刷新
    Vue3+Vite怎么使用双token实现无感刷新

    一、token登录鉴权jwt:JSONWebToken。是一种认证协议,一般用来校验请求的身份信息和身份权限。由三部分组成:Header、Hayload、Signatureheader:也就是头部信息,是描述这个token的基本信息,json格式{"alg":"HS256",//表示签名的算法,默认是HMACSHA256(写成HS256)"type":"JWT"//表示Token的类型,JWT令牌统一写为JWT}payload:载荷,也是一个JSON对象,用来存放实际需要传递的数据。不建议存放敏感信息,比

    springBoot项目常用目录有哪些
    springBoot项目常用目录有哪些

    springBoot项目常用目录springBoot项目的目录结构及名规范介绍基于SpringBoot开发时的目录结构及命名规范,通过介绍能够为你解决,在实际项目中如何规划目录结构?如何命名目录更规范?各个目录分别代表什么含义?等三个问题。目录说明servicex//项目名|-admin-ui//管理服务前端代码(一般将UI和SERVICE放到一个工程中,便于管理)|-servicex-auth//模块1|-servicex-common//模块2|-servicex-gateway//模块3|-serv

    指南:利用Spring Security框架进行权限管理
    指南:利用Spring Security框架进行权限管理

    在后台管理系统中,通常需要访问权限控制,以限制不同用户对接口的访问能力。如果用户缺乏特定权限,则无法访问某些接口。本文将用waynboot-mall项目举例,给大家介绍常见后管系统如何引入权限控制框架SpringSecurity。大纲如下:waynboot-mall项目地址:https://github.com/wayn111/waynboot-mall一、什么是SpringSecuritySpringSecurity是一个基于Spring框架的开源项目,旨在为Java应用程序提供强大和灵活的安全性解决方

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。