Spring Security OAuth2.0(三)-----基于Redis存储和JDBC存储

news2024/11/20 8:32:26

问题

令牌往哪里存?
客户端信息入库
第三方应用优化

1.令牌往哪里存?

在我们配置授权码模式的时候,有两个东西当时存在了内存中:

  • InMemoryAuthorizationCodeServices 这个表授权码存在内存中。
  • InMemoryTokenStore 表示生成的令牌存在内存中。
    授权码用过一次就会失效,存在内存中没什么问题,但是令牌,我们实际上还有其他的存储方案。

我们所使用的 InMemoryTokenStore 实现了 TokenStore 接口,我们来看下 TokenStore 接口的实现类:
在这里插入图片描述

可以看到,我们有多种方式来存储 access_token。

  1. InMemoryTokenStore,这是我们之前使用的,也是系统默认的,就是将 access_token 存到内存中,单机应用这个没有问题,但是在分布式环境下不推荐。
  2. JdbcTokenStore,看名字就知道,这种方式令牌会被保存到数据中,这样就可以方便的和其他应用共享令牌信息。
  3. JwtTokenStore,这个其实不是存储,因为使用了 jwt 之后,在生成的 jwt 中就有用户的所有信息,服务端不需要保存,这也是无状态登录,关于 OAuth2 结合 JWT 的用法,松哥本系列未来的文章中,也会详细介绍,这里就不再多说。
  4. RedisTokenStore,这个很明显就是将 access_token 存到 redis 中。
    JwkTokenStore,将 access_token 保存到 JSON Web Key。
    虽然这里支持的方案比较多,但是我们常用的实际上主要是两个,RedisTokenStore 和 JwtTokenStore,JwtTokenStore 的比较复杂,我会在后面专门写文章来单独介绍,这里先来跟大家演示存入 RedisTokenStore。

首先我们启动一个 Redis 服务,然后给 auth-server 添加 Redis 依赖:

        <!--redis依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

依赖添加成功后,在 application.properties 中添加 redis 配置:

spring.redis.host=
spring.redis.port=6379
spring.redis.password=

配置完成后,我们修改 TokenStore 的实例,如下:

package com.xql.authorization_server.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

@Configuration
public class AccessTokenConfig {

    @Autowired
    RedisConnectionFactory redisConnectionFactory;

    @Bean
    TokenStore tokenStore() {
        return new RedisTokenStore(redisConnectionFactory);
    }
}

然后分别启动项目,走一遍第三方登录流程,然后我们发现,派发的 access_token 在 redis 中也有一份:
在这里插入图片描述

2.客户端信息入库

所以我们要将客户端信息存入数据库中。

客户端信息入库涉及到的接口主要是 ClientDetailsService,这个接口主要有两个实现类,如下:
在这里插入图片描述
InMemoryClientDetailsService 就不多说了,这是存在内存中的。如果要存入数据库,很明显是 JdbcClientDetailsService,我们来大概看下 JdbcClientDetailsService 的源码,就能分析出数据库的结构了:

CREATE TABLE `oauth_client_details` (
  `client_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '客户端ID,唯一标识',
  `client_secret` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '客户端访问秘钥,BCryptPasswordEncoder加密算法加密',
  `resource_ids` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '可访问资源id(英文逗号分隔)',
  `scope` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '授权范围(英文逗号分隔)',
  `authorized_grant_types` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '授权类型(英文逗号分隔)',
  `web_server_redirect_uri` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '重定向uri',
  `authorities` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '@PreAuthorize("hasAuthority(''admin'')")可以在方法上标志 用户或者说client 需要说明样的权限\r\n\n\n指定客户端所拥有的Spring Security的权限值\r\n(英文逗号分隔)',
  `access_token_validity` int NOT NULL COMMENT '令牌有效期(单位:秒)',
  `refresh_token_validity` int NOT NULL COMMENT '刷新令牌有效期(单位:秒)',
  `additional_information` varchar(4096) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '预留字段,在Oauth的流程中没有实际的使用(JSON格式数据)',
  `autoapprove` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '设置用户是否自动Approval操作, 默认值为 ''false''\r\n可选值包括 ''true'',''false'', ''read'',''write''.\r\n该字段只适用于grant_type="authorization_code"的情况,当用户登录成功后,若该值为''true''或支持的scope值,则会跳过用户Approve的页面, 直接授权',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`client_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC;

接下来我们将一开始定义的客户端的关键信息存入数据库中,如下:

在这里插入图片描述

xql	$2a$10$SMP8P9hRmdTFzMfZBXksuuDNBm7AV9q1SFomvqc9FR38e/MMR7XiC	res1	all	authorization_code,password, client_credentials, implicit,refresh_token	http://localhost:8089/goods/index		7200	259200		false	2023-05-06 01:14:19	2023-05-06 06:14:30

既然用到了数据库,依赖当然也要提供相应的支持,我们给 authorization_server 添加如下依赖:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

然后在 application.properties 中配置一下数据库的连接信息:

spring.datasource.river-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url= jdbc:mysql://xxxx:3307/oauth?useUnicode=true&characterEncoding=utf8
spring.datasource.username= root
spring.datasource.password= xxxx

这里的配置多了最后一条。这是因为我们一会要创建自己的 ClientDetailsService,而系统已经创建了 ClientDetailsService,加了最后一条就允许我们自己的实例覆盖系统默认的实例。

    @Resource
    private DataSource dataSource;

    @Bean
    public ClientDetailsService detailsService(){
        return new JdbcClientDetailsService(dataSource);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(detailsService());
        //内存配置的方式配置用户信息
        //内存方式
//        clients.inMemory()
//                //内存模拟client_id
//                .withClient("xql")
//                //客户端 秘钥 以及加密方式BCryptPasswordEncoder
//                .secret(new BCryptPasswordEncoder().encode("xql123"))
//                //客户端拥有的资源列表
//                .resourceIds("res1")
//                //该client允许的授权类型
//                .authorizedGrantTypes("authorization_code",
//                        "password", "client_credentials", "implicit",
//                        "refresh_token")
//                //允许的授权范围
//                .scopes("all")
//                //跳转到授权页面
//                .autoApprove(false)
//                //回调地址
//                .redirectUris("http://localhost:8089/goods/index");
//                .redirectUris("http://localhost:8089/goods/implicit.jsp");
//                .redirectUris("http://localhost:8089/goods/login");
        //继续注册其他客户端
        // .and()
        // .withClient()
        // 加载自定义的客户端管理服务
//         clients.withClientDetails(clientDetailsService);
    }

修改后的 AuthorizationServerTokenServices 实例如下:

    @Autowired
    private ClientDetailsService clientDetailsService;
    /**
     * 这个 Bean 主要用来配置 Token 的一些基本信息,
     * 例如 Token 是否支持刷新、Token 的存储位置、Token 的有效期以及刷新 Token 的有效期等等。
     * Token 有效期这个好理解,刷新 Token 的有效期我说一下,当 Token 快要过期的时候,
     * 我们需要获取一个新的 Token,在获取新的 Token 时候,需要有一个凭证信息,
     * 这个凭证信息不是旧的 Token,而是另外一个 refresh_token,这个 refresh_token 也是有有效期的。
     */
    @Bean
    public AuthorizationServerTokenServices authorizationServerTokenServices() {
        DefaultTokenServices services = new DefaultTokenServices();
        //客户端详情服务
        services.setClientDetailsService(clientDetailsService);
        //允许令牌自动刷新
        services.setSupportRefreshToken(true);
        //令牌存储策略-内存
        services.setTokenStore(tokenStore);
        // 令牌默认有效期2小时
        services.setAccessTokenValiditySeconds(60 * 60 * 2);
        // 刷新令牌默认有效期3天
        services.setRefreshTokenValiditySeconds(60 * 60 * 24 * 3);
        return services;
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/495584.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

open3D

一、说明 对于点云 处理&#xff0c;这里介绍哦pen3d&#xff0c;该软件和opencv同样是interl公司的产品。 Open3D 是一个开源库&#xff0c;支持快速开发处理 3D 数据的软件。 Open3D 前端在 C 和 Python 中公开了一组精心挑选的数据结构和算法。后端经过高度优化&#xff0c;…

Spring Boot处理CORS跨域请求的三种方法

1 前言 Springboot跨域问题&#xff0c;是当前主流web开发人员都绕不开的难题。但我们首先要明确以下几点 跨域只存在于浏览器端&#xff0c;不存在于安卓/ios/Node.js/python/ java等其它环境跨域请求能发出去&#xff0c;服务端能收到请求并正常返回结果&#xff0c;只是结…

05-权限分配 尚筹网

权限控制 权限控制机制的本质就是“用钥匙开锁”。 在实现权限控制之前&#xff0c;这里先完成给Admin分配Role和给Role分配Auth的功能。 一、给Admin分配Role 目标 ​ 通过前端页面操作&#xff0c;将Admin与Role之间的关系保存到数据库 思路 ​ 给下面的按钮&#xff…

【ED合集】事件检测的文章

1 CorED: Incorporating Type-level and Instance-level Correlationsfor Fine-grained Event Detection 论文来源&#xff1a;SIGIR 2022(CCF A类会议) 论文链接&#xff1a;https://dl.acm.org/doi/pdf/10.1145/3477495.3531956 代码链接&#xff1a;GitHub - JiaweiSheng…

抖音小程序|基于天气API实现天气预报功能

文章目录 一、前言包含了功能UI展示 二、开发前的准备三、开发步骤1.app.js 配置2.pages/index.js 演示二维码源码在百度网盘下载 一、前言 参考老版iPhone自带的天气预报APP。目前只有一个界面UI, 后续会更新出更多功能; 包含了功能 - 实况预报 - 未来48小时 - 未来一周的天…

动态gif图片如何在线做?轻松实现图片在线生成gif

常见的jpg、png格式的静态图片想要变成gif格式的动态图片时&#xff0c;要怎么办呢&#xff1f;有没有什么简单实用的gif制作工具呢&#xff1f; 一、什么工具能够在线制作gif&#xff1f; GIF中文网作为一款专业的gif制作&#xff08;https://www.gif.cn/&#xff09;工具&a…

Golang - slice 内部实现原理解析

Golang - slice 内部实现原理解析 一.Go中的数组和slice的关系 1.数组 在几乎所有的计算机语言中&#xff0c;数组的实现都是一段连续的内存空间&#xff0c;Go语言数组的实现也是如此&#xff0c;但是Go语言中的数组和C语言中数组还是有所不同的 C语言数组变量是指向数组第…

鸿蒙Hi3861学习七-Huawei LiteOS(信号量)

一、简介 信号量&#xff08;Semaphore&#xff09;是一种实现任务间通信的机制&#xff0c;实现任务之间同步或临界资源的互斥访问。常用于协助一组相互竞争的任务来访问临界资源。 在多任务系统中&#xff0c;各任务之间需要同步或互斥实现临界资源的保护&#xff0c;信号量功…

阿里工作7年,肝到P8就靠这份学习笔记了,已助14个朋友拿到offer

​ 在阿里工作了7年&#xff0c;工作压力大&#xff0c;节奏快&#xff0c;但是从技术上确实得到了成长&#xff0c;尤其是当你维护与大促相关的系统的时候&#xff0c;熬到P8也费了不少心思。 技术的更新迭代越来越快&#xff0c;程序员或许是这个过程中最为挣扎的一波人。每…

第0章 学习之前的准备

突然想写点关于linux的东西&#xff0c;一是将自己几十年来零碎的知识作以串联&#xff0c;二是能为正在学习路上的新手作些指引。而恰好作者的孩子是一位初一的学生&#xff0c;我写的这些东西也正是我手把手教授他的&#xff0c;现在分享出来并且命名为《linux中学教程》&…

记一次SpringBoot应用性能调优过程

背景 使用SpringBoot、MyBatis-Plus开发一个接口转发的能&#xff0c;将第三方接口注册到平台中&#xff0c;由平台对外提供统一的地址&#xff0c;平台转发时记录接口的转发日志信息。开发完成后使用Jmeter进行性能测试&#xff0c;使用100个线程、持续压测180秒&#xff0c;…

Java中池化技术探讨

背景&#xff1a;在日常开发中&#xff0c;除了考虑IO操作、线程上下文切换、GC的影响性能外。还通过池化技术提高性能通过循环复用资源&#xff0c;降低资源创建和销毁带来的开销和损失&#xff0c;从而提高性能&#xff0c;例如对象池、内存池、线程池、连接池 一、对象池&a…

软件测试 - 测试用例设计方法之等价类划分和边界值分析

1. 等价类划分法 1.1 基本理论 等价类划分法是通过科学的方法找到具有共同特性的测试输入的集合&#xff0c;避免进行穷举测试&#xff0c;大大减少了测试用例的数量&#xff0c;从而提高测试效率。等价类划分法的典型应用场景就是输入框&#xff0c;适用于较少数量输入框的场…

晶振概述及工作原理

晶振在电路板中随处可见&#xff0c;只要用到处理器的地方就必定有晶振的存在&#xff0c;即使没有外部晶振&#xff0c;芯片内部也有晶振。 晶振概述 晶振一般指晶体振荡器。晶体振荡器是指从一块石英晶体上按一定方位角切下薄片&#xff08;简称为晶片&#xff09;&#xf…

虚拟服务器基础架构解决方案:用最小的工作量实现最大的价值

虚拟服务器基础架构解决方案&#xff1a;用最小的工作量实现最大的价值 一切皆可虚拟化&#xff01;包括服务器在内。NetApp 虚拟服务器基础架构解决方案有助于加快数据访问速度、构建创新服务并简化部署&#xff0c;从而实现最大价值。 为什么选择 NetApp 的虚拟服务器基础架…

pytorch矩阵乘法总结

1. element-wise&#xff08;*&#xff09; 按元素相乘&#xff0c;支持广播&#xff0c;等价于torch.mul() a torch.tensor([[1, 2], [3, 4]]) b torch.tensor([[2, 3], [4, 5]]) c a*b # 等价于torch.mul(a,b) # tensor([[ 2, 6], # [12, 20]]) a * torch.tenso…

详解C++类对象(上篇)——超详细

目录 一&#xff0c;面向对象&面向过程的认识(简单了解即可&#xff0c;逐步认识&#xff09; 二&#xff0c; 类 2.1 类的引入 2.2 类的定义 1. struct 2. class 类的两种定义方式&#xff1a; 2.3 封装&类的访问限定符 1. 封装概念 2. 类的访问限定符 …

低代码如何不写代码创建表单和维护表单

工作表新建与修改 新建工作表的流程包含 新建工作表/编辑公祖表为工作表添加字段&#xff0c;例如“员工档案”表中有姓名、性别、年龄等字段为字段设置属性工作表布局工作表预览、保存、关闭 1、新建工作表/修改工作表 新建工作表 修改工作表 2、为工作表添加字段 添加字段 左…

关于C语言的一些杂记2

文章目录 sizeof运算符内容关于基本概念的问题关于一些语句的理解和分号的注意字符的理解关于输出格式的扩展 本文内容摘自C技能树一些优秀的博主 sizeof运算符内容 关于基本概念的问题 sizeof是C语言的关键字&#xff0c;它用来计算变量&#xff08;或数据类型&#xff09;在…

2.Hive创建数据库

1.数据库操作 1.1 创建数据库 create database test comment Just for test location /abcd with dbproperties(aaabbb); comment后面指的是注释&#xff1b;location后面是数据库存放路径&#xff1b;dbproperties代表了数据库的属性 ps.避免要创建的数据库已经存在错误&…