# 三个方面
- 客户端信息
- token 存储
- 授权(用户)RBAC 存储
参考项目 https://github.com/topsale/spring-boot-samples/tree/master/spring-security-oauth2 (opens new window)
# OAuth2 需要的 mysql 表
CREATE DATABASE IF NOT EXISTS `oauth2` DEFAULT CHARACTER SET utf8 ;
USE `oauth2` ;
-- -----------------------------------------------------
-- Table `oauth2`.`clientdetails`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `oauth2`.`clientdetails` (
`appId` VARCHAR(128) NOT NULL,
`resourceIds` VARCHAR(256) NULL DEFAULT NULL,
`appSecret` VARCHAR(256) NULL DEFAULT NULL,
`scope` VARCHAR(256) NULL DEFAULT NULL,
`grantTypes` VARCHAR(256) NULL DEFAULT NULL,
`redirectUrl` VARCHAR(256) NULL DEFAULT NULL,
`authorities` VARCHAR(256) NULL DEFAULT NULL,
`access_token_validity` INT(11) NULL DEFAULT NULL,
`refresh_token_validity` INT(11) NULL DEFAULT NULL,
`additionalInformation` VARCHAR(4096) NULL DEFAULT NULL,
`autoApproveScopes` VARCHAR(256) NULL DEFAULT NULL,
PRIMARY KEY (`appId`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
-- -----------------------------------------------------
-- Table `oatuh2`.`oauth_access_token`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_access_token` (
`token_id` VARCHAR(256) NULL DEFAULT NULL,
`token` BLOB NULL DEFAULT NULL,
`authentication_id` VARCHAR(128) NOT NULL,
`user_name` VARCHAR(256) NULL DEFAULT NULL,
`client_id` VARCHAR(256) NULL DEFAULT NULL,
`authentication` BLOB NULL DEFAULT NULL,
`refresh_token` VARCHAR(256) NULL DEFAULT NULL,
PRIMARY KEY (`authentication_id`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
-- -----------------------------------------------------
-- Table `oatuh2`.`oauth_approvals`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_approvals` (
`userId` VARCHAR(256) NULL DEFAULT NULL,
`clientId` VARCHAR(256) NULL DEFAULT NULL,
`scope` VARCHAR(256) NULL DEFAULT NULL,
`status` VARCHAR(10) NULL DEFAULT NULL,
`expiresAt` DATETIME NULL DEFAULT NULL,
`lastModifiedAt` DATETIME NULL DEFAULT NULL)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
-- -----------------------------------------------------
-- Table `oatuh2`.`oauth_client_details`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_client_details` (
`client_id` VARCHAR(128) NOT NULL,
`resource_ids` VARCHAR(256) NULL DEFAULT NULL,
`client_secret` VARCHAR(256) NULL DEFAULT NULL,
`scope` VARCHAR(256) NULL DEFAULT NULL,
`authorized_grant_types` VARCHAR(256) NULL DEFAULT NULL,
`web_server_redirect_uri` VARCHAR(256) NULL DEFAULT NULL,
`authorities` VARCHAR(256) NULL DEFAULT NULL,
`access_token_validity` INT(11) NULL DEFAULT NULL,
`refresh_token_validity` INT(11) NULL DEFAULT NULL,
`additional_information` VARCHAR(4096) NULL DEFAULT NULL,
`autoapprove` VARCHAR(256) NULL DEFAULT NULL,
PRIMARY KEY (`client_id`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
-- -----------------------------------------------------
-- Table `oatuh2`.`oauth_client_token`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_client_token` (
`token_id` VARCHAR(256) NULL DEFAULT NULL,
`token` BLOB NULL DEFAULT NULL,
`authentication_id` VARCHAR(128) NOT NULL,
`user_name` VARCHAR(256) NULL DEFAULT NULL,
`client_id` VARCHAR(256) NULL DEFAULT NULL,
PRIMARY KEY (`authentication_id`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
-- -----------------------------------------------------
-- Table `oatuh2`.`oauth_code`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_code` (
`code` VARCHAR(256) NULL DEFAULT NULL,
`authentication` BLOB NULL DEFAULT NULL)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
-- -----------------------------------------------------
-- Table `oatuh2`.`oauth_refresh_token`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_refresh_token` (
`token_id` VARCHAR(256) NULL DEFAULT NULL,
`token` BLOB NULL DEFAULT NULL,
`authentication` BLOB NULL DEFAULT NULL)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
# 说明
- oauth_access_token 生成的token
- oauth_client_details 客户端信息
- oauth_code 授权码
- oauth_refresh_token 刷新token
# oauth_client_detail表说明
| 字段名 | 字段约束 | 详细描述 | 范例 |
|---|---|---|---|
| client_id | 主键,必须唯一,不能为空 | 用于唯一标识每一个客户端(client);注册时必须填写(也可以服务端自动生成),这个字段是必须的,实际应用也有叫app_key | OaH1heR2E4eGnBr87Br8FHaUFrA2Q0kE8HqZgpdg8Sw |
| resource_ids | 不能为空,用逗号分隔 | 客户端能访问的资源id集合,注册客户端时,根据实际需要可选择资源id,也可以根据不同的额注册流程,赋予对应的额资源id | order-resource,pay-resource |
| client_secret | 必须填写 | 注册填写或者服务端自动生成,实际应用也有叫app_secret, 必须要有前缀代表加密方式 | {bcrypt}gY/Hauph1tqvVWiH4atxteSH8sRX03IDXRIQi03DVTFGzKfz8ZtGi |
| scope | 不能为空,用逗号分隔 | 指定client的权限范围,比如读写权限,比如移动端还是web端权限 | read,write / web,mobile |
| authorized_grant_types | 不能为空 | 可选值 授权码模式:authorization_code,密码模式:password,刷新token: refresh_token, 隐式模式: implicit: 客户端模式: client_credentials。支持多个用逗号分隔 | password,refresh_token |
| web_server_redirect_uri | 可为空 | 客户端重定向uri,authorization_code和implicit需要该值进行校验,注册时填写, | <http://baidu.com> |
| authorities | 可为空 | 指定用户的权限范围,如果授权的过程需要用户登陆,该字段不生效,implicit和client_credentials需要 | ROLE_ADMIN,ROLE_USER |
| access_token_validity | 可空 | 设置access_token的有效时间(秒),默认(606012,12小时) | 3600 |
| refresh_token_validity | 可空 | 设置refresh_token有效期(秒),默认(606024*30, 30天) | 7200 |
| additional_information | 可空 | 值必须是json格式 | {"key", "value"} |
| autoapprove | false/true/read/write | 默认false,适用于authorization_code模式,设置用户是否自动approval操作,设置true跳过用户确认授权操作页面,直接跳到redirect_uri | false |
# RBAC 需要的表
/*
SQLyog v12.2.6 (64 bit)
MySQL - 8.0.15 : Database - oauth2
*********************************************************************
*/
/*!40101 SET NAMES utf8 */;
/*!40101 SET SQL_MODE=''*/;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`oauth2` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `oauth2`;
/*Table structure for table `tb_permission` */
DROP TABLE IF EXISTS `tb_permission`;
CREATE TABLE `tb_permission` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`parent_id` bigint(20) DEFAULT NULL COMMENT '父权限',
`name` varchar(64) NOT NULL COMMENT '权限名称',
`enname` varchar(64) NOT NULL COMMENT '权限英文名称',
`url` varchar(255) NOT NULL COMMENT '授权路径',
`description` varchar(200) DEFAULT NULL COMMENT '备注',
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=49 DEFAULT CHARSET=utf8 COMMENT='权限表';
/*Data for the table `tb_permission` */
insert into `tb_permission`(`id`,`parent_id`,`name`,`enname`,`url`,`description`,`created`,`updated`) values
(37,0,'系统管理','System','/',NULL,'2019-04-04 23:22:54','2019-04-04 23:22:56'),
(38,37,'用户管理','SystemUser','/users/',NULL,'2019-04-04 23:25:31','2019-04-04 23:25:33'),
(39,38,'查看用户','SystemUserView','/users/view/**',NULL,'2019-04-04 15:30:30','2019-04-04 15:30:43'),
(40,38,'新增用户','SystemUserInsert','/users/insert/**',NULL,'2019-04-04 15:30:31','2019-04-04 15:30:44'),
(41,38,'编辑用户','SystemUserUpdate','/users/update/**',NULL,'2019-04-04 15:30:32','2019-04-04 15:30:45'),
(42,38,'删除用户','SystemUserDelete','/users/delete/**',NULL,'2019-04-04 15:30:48','2019-04-04 15:30:45'),
(44,37,'内容管理','SystemContent','/contents/',NULL,'2019-04-06 18:23:58','2019-04-06 18:24:00'),
(45,44,'查看内容','SystemContentView','/contents/view/**',NULL,'2019-04-06 23:49:39','2019-04-06 23:49:41'),
(46,44,'新增内容','SystemContentInsert','/contents/insert/**',NULL,'2019-04-06 23:51:00','2019-04-06 23:51:02'),
(47,44,'编辑内容','SystemContentUpdate','/contents/update/**',NULL,'2019-04-06 23:51:04','2019-04-06 23:51:06'),
(48,44,'删除内容','SystemContentDelete','/contents/delete/**',NULL,'2019-04-06 23:51:08','2019-04-06 23:51:10');
/*Table structure for table `tb_role` */
DROP TABLE IF EXISTS `tb_role`;
CREATE TABLE `tb_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`parent_id` bigint(20) DEFAULT NULL COMMENT '父角色',
`name` varchar(64) NOT NULL COMMENT '角色名称',
`enname` varchar(64) NOT NULL COMMENT '角色英文名称',
`description` varchar(200) DEFAULT NULL COMMENT '备注',
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8 COMMENT='角色表';
/*Data for the table `tb_role` */
insert into `tb_role`(`id`,`parent_id`,`name`,`enname`,`description`,`created`,`updated`) values
(37,0,'超级管理员','admin',NULL,'2019-04-04 23:22:03','2019-04-04 23:22:05');
/*Table structure for table `tb_role_permission` */
DROP TABLE IF EXISTS `tb_role_permission`;
CREATE TABLE `tb_role_permission` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`role_id` bigint(20) NOT NULL COMMENT '角色 ID',
`permission_id` bigint(20) NOT NULL COMMENT '权限 ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=48 DEFAULT CHARSET=utf8 COMMENT='角色权限表';
/*Data for the table `tb_role_permission` */
insert into `tb_role_permission`(`id`,`role_id`,`permission_id`) values
(37,37,37),
(38,37,38),
(39,37,39),
(40,37,40),
(41,37,41),
(42,37,42),
(43,37,44),
(44,37,45),
(45,37,46),
(46,37,47),
(47,37,48);
/*Table structure for table `tb_user` */
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(64) NOT NULL COMMENT '密码,加密存储',
`phone` varchar(20) DEFAULT NULL COMMENT '注册手机号',
`email` varchar(50) DEFAULT NULL COMMENT '注册邮箱',
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`) USING BTREE,
UNIQUE KEY `phone` (`phone`) USING BTREE,
UNIQUE KEY `email` (`email`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8 COMMENT='用户表';
/*Data for the table `tb_user` */
insert into `tb_user`(`id`,`username`,`password`,`phone`,`email`,`created`,`updated`) values
(37,'admin','$2a$10$9ZhDOBp.sRKat4l14ygu/.LscxrMUcDAfeVOEPiYwbcRkoB09gCmi','15888888888','lee.lusifer@gmail.com','2019-04-04 23:21:27','2019-04-04 23:21:29');
/*Table structure for table `tb_user_role` */
DROP TABLE IF EXISTS `tb_user_role`;
CREATE TABLE `tb_user_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL COMMENT '用户 ID',
`role_id` bigint(20) NOT NULL COMMENT '角色 ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8 COMMENT='用户角色表';
/*Data for the table `tb_user_role` */
insert into `tb_user_role`(`id`,`user_id`,`role_id`) values
(37,37,37);
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
# AuthorizationServerConfiguration
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Bean
@Primary
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
// 配置数据源(注意,我使用的是 HikariCP 连接池),以上注解是指定数据源,否则会有冲突
return DataSourceBuilder.create().build();
}
@Bean
public TokenStore tokenStore() {
// 基于 JDBC 实现,令牌保存到数据
return new JdbcTokenStore(dataSource());
}
@Bean
public ClientDetailsService jdbcClientDetails() {
// 基于 JDBC 实现,需要事先在数据库配置客户端信息
return new JdbcClientDetailsService(dataSource());
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// 设置令牌
endpoints.tokenStore(tokenStore());
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 读取客户端配置
clients.withClientDetails(jdbcClientDetails());
}
}
# WebSecurityConfiguration
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Bean
public BCryptPasswordEncoder passwordEncoder() {
// 设置默认的加密方式
return new BCryptPasswordEncoder();
}
@Bean
@Override
public UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 使用自定义认证与授权
auth.userDetailsService(userDetailsService());
}
@Override
public void configure(WebSecurity web) throws Exception {
// 将 check_token 暴露出去,否则资源服务器访问时报 403 错误
web.ignoring().antMatchers("/oauth/check_token");
}
}
# application.yml
spring:
application:
name: oauth2-server
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://192.168.141.128:3307/oauth2?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
hikari:
minimum-idle: 5
idle-timeout: 600000
maximum-pool-size: 10
auto-commit: true
pool-name: MyHikariCP
max-lifetime: 1800000
connection-timeout: 30000
connection-test-query: SELECT 1
server:
port: 8080
mybatis:
type-aliases-package: com.funtl.oauth2.server.domain
mapper-locations: classpath:mapper/*.xml