问题
1.令牌往哪里存?
- InMemoryAuthorizationCodeServices 这个表授权码存在内存中。
- InMemoryTokenStore 表示生成的令牌存在内存中。
授权码用过一次就会失效,存在内存中没什么问题,但是令牌,我们实际上还有其他的存储方案。
我们所使用的 InMemoryTokenStore 实现了 TokenStore 接口,我们来看下 TokenStore 接口的实现类:
- InMemoryTokenStore,这是我们之前使用的,也是系统默认的,就是将 access_token 存到内存中,单机应用这个没有问题,但是在分布式环境下不推荐。
- JdbcTokenStore,看名字就知道,这种方式令牌会被保存到数据中,这样就可以方便的和其他应用共享令牌信息。
- JwtTokenStore,这个其实不是存储,因为使用了 jwt 之后,在生成的 jwt 中就有用户的所有信息,服务端不需要保存,这也是无状态登录,关于 OAuth2 结合 JWT 的用法,松哥本系列未来的文章中,也会详细介绍,这里就不再多说。
- 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=
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 需要说明样的权限rnnn指定客户端所拥有的Spring Security的权限值rn(英文逗号分隔)',
`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''rn可选值包括 ''true'',''false'', ''read'',''write''.rn该字段只适用于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;
}
原文地址:https://blog.csdn.net/qq_42264638/article/details/130519064
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_46042.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!