权限认证(四):OAuth2认证服务器策略配置
- 通过刷新令牌获取访问令牌
- 新建CustomUserDetailsService
@Component("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
return new User("admin", passwordEncoder.encode("1234"),
AuthorityUtils.commaSeparatedStringToAuthorityList("product"));
}
}
- SpringSecurityConfig中添加如下
@Autowired
private UserDetailsService customUserDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService);
}
- AuthorizationServerConfig中添加如下
@Autowired // 刷新令牌
private UserDetailsService customUserDetailsService;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// 密码模式要设置认证管理器
endpoints.authenticationManager(authenticationManager);
// 刷新令牌时需要使用
endpoints.userDetailsService(customUserDetailsService);
}
-
postmam测试
-
使用jdbc管理访问令牌
-
新建数据库和表
-- ----------------------------
-- Table structure for clientdetails
-- ----------------------------
DROP TABLE IF EXISTS `clientdetails`;
CREATE TABLE `clientdetails` (
`appId` varchar(128) NOT NULL,
`resourceIds` varchar(128) DEFAULT NULL,
`appSecret` varchar(128) DEFAULT NULL,
`scope` varchar(128) DEFAULT NULL,
`grantTypes` varchar(128) DEFAULT NULL,
`redirectUrl` varchar(128) DEFAULT NULL,
`authorities` varchar(128) DEFAULT NULL,
`access_token_validity` int(11) DEFAULT NULL,
`refresh_token_validity` int(11) DEFAULT NULL,
`additionalInformation` varchar(4096) DEFAULT NULL,
`autoApproveScopes` varchar(128) DEFAULT NULL,
PRIMARY KEY (`appId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Records of clientdetails
-- ----------------------------
-- ----------------------------
-- Table structure for oauth_access_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_access_token`;
CREATE TABLE `oauth_access_token` (
`token_id` varchar(128) DEFAULT NULL,
`token` blob,
`authentication_id` varchar(128) NOT NULL,
`user_name` varchar(128) DEFAULT NULL,
`client_id` varchar(128) DEFAULT NULL,
`authentication` blob,
`refresh_token` varchar(128) DEFAULT NULL,
PRIMARY KEY (`authentication_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Records of oauth_access_token
-- ----------------------------
-- ----------------------------
-- Table structure for oauth_approvals
-- ----------------------------
DROP TABLE IF EXISTS `oauth_approvals`;
CREATE TABLE `oauth_approvals` (
`userId` varchar(128) DEFAULT NULL,
`clientId` varchar(128) DEFAULT NULL,
`scope` varchar(128) DEFAULT NULL,
`status` varchar(10) DEFAULT NULL,
`expiresAt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`lastModifiedAt` timestamp NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Records of oauth_approvals
-- ----------------------------
-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details` (
`client_id` varchar(128) NOT NULL COMMENT '客户端id(如:mengxuegu_client',
`resource_ids` varchar(128) DEFAULT NULL,
`client_secret` varchar(128) DEFAULT NULL COMMENT '客户端密码(要加密后存储)',
`scope` varchar(128) DEFAULT NULL COMMENT '客户端授权范all,write,read)',
`authorized_grant_types` varchar(128) DEFAULT NULL COMMENT '4种授权类型(多个授权类型,用英文逗号分隔',
`web_server_redirect_uri` varchar(128) DEFAULT NULL COMMENT '获取授权码后的回调地址',
`authorities` varchar(128) DEFAULT NULL COMMENT '授权标识',
`access_token_validity` int(11) DEFAULT NULL,
`refresh_token_validity` int(11) DEFAULT NULL,
`additional_information` varchar(4096) DEFAULT NULL,
`autoapprove` varchar(128) DEFAULT NULL,
PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='客户端(第三方应用)基本信息';
-- ----------------------------
-- Records of oauth_client_details
-- ----------------------------
INSERT INTO `oauth_client_details` (`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) VALUES ('mengxuegu-pc', 'product-server', '$2a$10$VcUHxmgnxBTdB9XNdhoOWujC.lFZ0rO1UizqMAS0GU6WAerFviX.a', 'all,PRODUCT_API', 'authorization_code,password,implicit,client_credentials,refresh_token', 'http://www.mengxuegu.com/', NULL, '50000', NULL, NULL, 'false');
INSERT INTO `oauth_client_details` (`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) VALUES ('client1', '', '$2a$10$AodcXpujNILcZAQ.7cQ0b.Bm4klRumapoFBu7uyQ/ZV9Nu9F5fE3y', 'MEMBER_READ,MEMBER_WRITE', 'authorization_code,refresh_token', 'http://localhost:9001/login', NULL, '50000', NULL, NULL, 'true');
INSERT INTO `oauth_client_details` (`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) VALUES ('client2', '', '$2a$10$AodcXpujNILcZAQ.7cQ0b.Bm4klRumapoFBu7uyQ/ZV9Nu9F5fE3y', 'MEMBER_READ', 'authorization_code,refresh_token', 'http://localhost:9002/login', NULL, '50000', NULL, NULL, 'true');
-- ----------------------------
-- Table structure for oauth_client_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_token`;
CREATE TABLE `oauth_client_token` (
`token_id` varchar(128) DEFAULT NULL,
`token` blob,
`authentication_id` varchar(128) NOT NULL,
`user_name` varchar(128) DEFAULT NULL,
`client_id` varchar(128) DEFAULT NULL,
PRIMARY KEY (`authentication_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Records of oauth_client_token
-- ----------------------------
-- ----------------------------
-- Table structure for oauth_code
-- ----------------------------
DROP TABLE IF EXISTS `oauth_code`;
CREATE TABLE `oauth_code` (
`code` varchar(128) DEFAULT NULL,
`authentication` blob
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Records of oauth_code
-- ----------------------------
-- ----------------------------
-- Table structure for oauth_refresh_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_refresh_token`;
CREATE TABLE `oauth_refresh_token` (
`token_id` varchar(128) DEFAULT NULL,
`token` blob,
`authentication` blob
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
- pom.xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
- yml
spring:
# 数据源配置
datasource:
username: root
password: 123456
url: jdbc:mysql://192.168.1.102:3306/demo?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8
#mysql8版本以上驱动包指定新的驱动类
driver-class-name: com.mysql.cj.jdbc.Driver
# 数据源其他配置, 在 DruidConfig配置类中手动绑定
initialSize: 8
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
- 配置类
@Configuration
public class TokenConfig {
// jdbc管理token
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource dataSource(){
return new DruidDataSource();
}
@Bean
public TokenStore tokenStore() {
// jdbc管理令牌
return new JdbcTokenStore(dataSource());
}
}
# AuthorizationServerConfig类中添加如下
@Autowired // token管理方式,在TokenConfig类中已对添加到容器中了
private TokenStore tokenStore;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// 令牌的管理方式
endpoints.tokenStore(tokenStore);
}
-
启动项目使用密码模式测试
-
数据库中产生1条数据,测试通过
-
jdbc管理授权码
# AuthorizationServerConfig配置类中添加如下
@Autowired
private DataSource dataSource;
@Bean // 授权码管理策略
public AuthorizationCodeServices jdbcAuthorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// 授权码管理策略 会产生的授权码放到 oauth_code 表中,如果这个授权码已经使用了,则对应这个表中的数据就会被删除
endpoints.authorizationCodeServices(jdbcAuthorizationCodeServices());
}
# 之前的客户端信息储存在内存中,将其注释掉
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("mengxuegu-pc") // 客户端id
.secret(passwordEncoder.encode("mengxuegu-secret")) // 客户端密码,需加密
.resourceIds("product-server") // 资源id,可理解为微服务名称,表示可访问哪些微服务
.authorizedGrantTypes("authorization_code", "password", "implicit", "client_credentials", "refresh_token") // 认证模式
.scopes("all") // 授权范围标识,哪部分资源可访问(all只是标识,不是说所有资源)
.autoApprove(false) // false 跳到一个授权页面手动点击授权,true不需要手动点授权,直接响应一个授权码
.redirectUris("http://www.mengxuegu.com/") // 客户端回调地址
.accessTokenValiditySeconds(60*60*8) //访问令牌有效时长 默认为12小时
.refreshTokenValiditySeconds(60*60*24*60) // 刷新令牌有效时长,默认是30天
;
}
- 未获取授权码前查看数据库
- 获取授权码
# 浏览器访问http://localhost:8090/auth/oauth/authorize?client_id=mengxuegu-pc&response_type=code
# 输入用户名和密码:admin + 1234
-
获取授权码后查看数据库
-
使用授权码后,数据库自动删除授权码
-
配置访问令牌和刷新令牌的有效时长
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 内存方式管理客户端信息
clients.inMemory()
.withClient("mengxuegu-pc") // 客户端id
.secret(passwordEncoder.encode("mengxuegu-secret")) // 客户端密码,需加密
.resourceIds("product-server") // 资源id,可理解为微服务名称,表示可访问哪些微服务
.authorizedGrantTypes("authorization_code", "password", "implicit", "client_credentials", "refresh_token") // 认证模式
.scopes("all") // 授权范围标识,哪部分资源可访问(all只是标识,不是说所有资源)
.autoApprove(false) // false 跳到一个授权页面手动点击授权,true不需要手动点授权,直接响应一个授权码
.redirectUris("http://www.mengxuegu.com/") // 客户端回调地址
.accessTokenValiditySeconds(60*60*8) //访问令牌有效时长 默认为12小时
.refreshTokenValiditySeconds(60*60*24*60) // 刷新令牌有效时长,默认是30天
;
}
oauth_client_details表字段详情
client_id :客户端ID
resource_ids :可访问的资源服务器ID, 不写则不校验.
client_secret :客户端密码,此处不能是明文,需要加密$2a$10$VcUHxmgnxBTdB9XNdhoOWujC.lFZ0rO1UizqMAS0GU6WAerFviX.a
scope :客户端授权范围, 不指定默认不校验范围
authorized_grant_types :客户端授权类型,支持多个使用逗号分隔authorization_code,password,implicit,client_credentials,refresh_token
web_server_redirect_uri :服务器回调地址
autoapprove :false 显示授权点击页,true不显示自动授权
使用jdbc管理第三方应用
# AuthorizationServerConfig类中添加如下
@Autowired
private DataSource dataSource;
@Bean
public ClientDetailsService jdbcClientDetailsService() {
return new JdbcClientDetailsService(dataSource);
}
/**
* 配置被允许访问此认证服务器的客户端信息
* 1.内存方式
* 2. 数据库方式
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// jdbc管理客户端
clients.withClientDetails(jdbcClientDetailsService());
}
-
使用postman测试
-
令牌端点详情
/oauth/authorize :申请授权码 code, 涉及的类 AuthorizationEndpoint
/oauth/token :获取令牌 token, 涉及的类 TokenEndpoint
/oauth/check_token :用于资源服务器请求端点来检查令牌是否有效, 涉及的类 CheckTokenEndpoint
/oauth/confirm_access :用户确认授权提交, 涉及的类 WhitelabelApprovalEndpoint
/oauth/error :授权服务错误信息, 涉及的类 WhitelabelErrorEndpoint
/oauth/token_key :提供公有密匙的端点,使用 JWT 令牌时会使用 , 涉及的类 TokenKeyEndpoint
开放端点
/**
* 令牌端点的安全配置
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
// 所有人可访问 /oauth/token_key 后面要获取公钥, 默认拒绝访问
security.tokenKeyAccess("permitAll()");
// 认证后可访问 /oauth/check_token , 默认拒绝访问
security.checkTokenAccess("isAuthenticated()");
}
- 先获取一个令牌用于测试
- 未开放时测试
- 开放后测试