Java框架快速入门: Spring Security+OAuth2之搭建授权服务器(依赖与表结构)

纲要

  • 微服务工程架构概述
    • 授权服务器(auth-server
    • 资源服务器(todo-service
    • 后端客户端(webclient
    • 前端管理应用(uaa-ui
  • 核心依赖引入
    • spring-security-oauth2
    • spring-security-oauth2-autoconfigure
  • 数据库表结构设计
    • oauth_client_details 表字段详解
    • 客户端标识、密钥、授权范围、授权类型、回调地址、权限、Token 有效期等

微服务工程架构

在实现 OAuth 2.0 认证体系时,传统单体应用被拆分为多个独立微服务。工程目录中除原有模块外,新增了 todo-servicewebclient 两个核心工程,其角色分配如下:

  • 授权服务器:承担 OAuth 2.0 授权服务器与部分资源服务器角色,管理用户、权限及客户端注册信息。
  • 资源服务器todo-service):提供受保护的 REST API(如待办事项列表),供客户端在获得授权后访问。
  • 客户端webclient):以不同的 OAuth 2.0 授权流程(授权码模式、客户端凭证模式、密码模式)与授权服务器交互,获取令牌并访问资源。
  • 前端管理界面uaa-ui):作为前端客户端,通过浏览器与授权服务器交互,实现用户登录与客户端管理。

项目运行端口配置在各自的 application.yml 中,例如授权服务器使用 8080,资源服务器使用 8082webclient 使用其他端口,各服务独立启动。

工程结构树示意:

dir 复制代码
project-root/
├── auth-server/              # 授权服务器 (含资源管理)
│   ├── pom.xml
│   └── src/main/resources/
│       └── application.yml   # server.port=8080
├── todo-service/             # 资源服务器
│   ├── pom.xml
│   └── src/main/resources/
│       └── application.yml   # server.port=8082
├── webclient/                # 后端客户端
│   ├── pom.xml
│   └── src/main/resources/
│       └── application.yml
└── uaa-ui/                   # 前端客户端 (Vue/React)
    ├── package.json
    └── ...

各服务交互流程可用下图表示:
#mermaid-svg-pa4v6Wz2OJlVqsU0{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-pa4v6Wz2OJlVqsU0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .error-icon{fill:#552222;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .marker.cross{stroke:#333333;}#mermaid-svg-pa4v6Wz2OJlVqsU0 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-pa4v6Wz2OJlVqsU0 p{margin:0;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .cluster-label text{fill:#333;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .cluster-label span{color:#333;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .cluster-label span p{background-color:transparent;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .label text,#mermaid-svg-pa4v6Wz2OJlVqsU0 span{fill:#333;color:#333;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .node rect,#mermaid-svg-pa4v6Wz2OJlVqsU0 .node circle,#mermaid-svg-pa4v6Wz2OJlVqsU0 .node ellipse,#mermaid-svg-pa4v6Wz2OJlVqsU0 .node polygon,#mermaid-svg-pa4v6Wz2OJlVqsU0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .rough-node .label text,#mermaid-svg-pa4v6Wz2OJlVqsU0 .node .label text,#mermaid-svg-pa4v6Wz2OJlVqsU0 .image-shape .label,#mermaid-svg-pa4v6Wz2OJlVqsU0 .icon-shape .label{text-anchor:middle;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .rough-node .label,#mermaid-svg-pa4v6Wz2OJlVqsU0 .node .label,#mermaid-svg-pa4v6Wz2OJlVqsU0 .image-shape .label,#mermaid-svg-pa4v6Wz2OJlVqsU0 .icon-shape .label{text-align:center;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .node.clickable{cursor:pointer;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .arrowheadPath{fill:#333333;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-pa4v6Wz2OJlVqsU0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pa4v6Wz2OJlVqsU0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-pa4v6Wz2OJlVqsU0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .cluster text{fill:#333;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .cluster span{color:#333;}#mermaid-svg-pa4v6Wz2OJlVqsU0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-pa4v6Wz2OJlVqsU0 rect.text{fill:none;stroke-width:0;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .icon-shape,#mermaid-svg-pa4v6Wz2OJlVqsU0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .icon-shape p,#mermaid-svg-pa4v6Wz2OJlVqsU0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .icon-shape .label rect,#mermaid-svg-pa4v6Wz2OJlVqsU0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pa4v6Wz2OJlVqsU0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-pa4v6Wz2OJlVqsU0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-pa4v6Wz2OJlVqsU0 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 访问
OAuth2 授权请求
返回授权码/令牌
携带令牌
返回受保护资源
管理用户/客户端
OAuth2 登录
用户
webclient 客户端
授权服务器 auth-server
资源服务器 todo-service
数据库
uaa-ui 前端

添加 OAuth2 依赖

在授权服务器模块(auth-server)的 pom.xml 中引入 Spring Security OAuth2 依赖。目前基于 Spring Security OAuth2 的正式发布版本,使用以下两个核心包:

xml 复制代码
<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security.oauth.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
</dependency>

说明spring-security-oauth2 提供 OAuth2 的核心支持,spring-security-oauth2-autoconfigure 简化自动配置。版本号需与实际 Spring Boot 版本兼容,通常在父 POM 或依赖管理器中统一声明。

刷新 Maven 依赖后,即可开始配置授权服务器。

数据库表结构设计

授权服务器需要持久化客户端信息,因此需在数据库中创建相关表。核心表为 oauth_client_details,它存储每个客户端的注册数据。虽然 Spring Security OAuth2 还支持内存模式,但生产环境推荐使用数据库存储。

建表 SQL 语句如下:

sql 复制代码
CREATE TABLE `oauth_client_details` (
  `client_id` varchar(256) NOT NULL COMMENT '客户端唯一标识',
  `resource_ids` varchar(256) DEFAULT NULL COMMENT '可访问的资源ID集合',
  `client_secret` varchar(256) DEFAULT NULL COMMENT '客户端密钥(加密)',
  `scope` varchar(256) DEFAULT NULL COMMENT '授权范围,如 read,write',
  `authorized_grant_types` varchar(256) DEFAULT NULL COMMENT '授权类型,如 authorization_code,password,client_credentials,refresh_token',
  `web_server_redirect_uri` varchar(256) DEFAULT NULL COMMENT '授权码模式回调地址',
  `authorities` varchar(256) DEFAULT NULL COMMENT '客户端拥有的权限(Spring Security authorities)',
  `access_token_validity` int(11) DEFAULT NULL COMMENT '访问令牌有效期(秒)',
  `refresh_token_validity` int(11) DEFAULT NULL COMMENT '刷新令牌有效期(秒)',
  `additional_information` varchar(4096) DEFAULT NULL COMMENT '附加信息(JSON格式)',
  `autoapprove` varchar(256) DEFAULT NULL COMMENT '自动授权范围,true 或 scope 列表',
  PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

字段含义详解

字段 说明
client_id 客户端的唯一标识,如 web-clientapp-client
resource_ids 客户端可访问的资源服务器 ID 列表(逗号分隔),可用于限制访问特定资源服务,实际项目中更多通过权限控制
client_secret 客户端密钥,需加密存储,用于客户端认证
scope 授权范围,定义客户端能请求的权限粒度,如 readwrite,与用户权限形成两层控制
authorized_grant_types 允许的 OAuth2 授权流程,常见值:authorization_codepasswordclient_credentialsrefresh_tokenimplicit
web_server_redirect_uri 授权码模式下的回调地址,授权服务器将授权码或令牌重定向到此 URL
authorities 客户端自身的权限(而非用户权限),可用于资源服务器访问控制
access_token_validity 访问令牌的过期时间(秒),为空则使用全局默认值
refresh_token_validity 刷新令牌的过期时间(秒),为空则使用全局默认值
additional_information 自定义附加信息,可存放业务需要的额外字段,JSON 格式
autoapprove 如果设为 true 或具体 scope 列表,则用户授权时不会弹出确认页面,直接自动批准

示例插入一条客户端数据:

sql 复制代码
INSERT INTO `oauth_client_details` 
(client_id, client_secret, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, autoapprove)
VALUES 
('web-client', '{noop}secret', 'read,write', 'authorization_code,refresh_token', 'http://localhost:8082/login/oauth2/code/auth-server', 'ROLE_CLIENT', 3600, 7200, 'true');

注:{noop} 表示密钥不加密存储(仅开发测试),生产环境应使用 BCrypt 等加密算法。

后续步骤

本文完成了 OAuth2 授权服务器的初始搭建:明确了工程架构、引入了核心依赖、设计了客户端信息表。

下一篇文章将进入授权服务器的核心配置实现,包括 Spring Security 安全配置、OAuth2 授权服务器端点暴露以及 JWT 令牌的签发。

总结

本文基于实际项目需求,介绍了微服务架构下 OAuth2 授权服务器的前置工作:多服务划分、依赖管理和客户端表结构设计。

这些是后续实现授权码、密码、客户端凭证等授权流程的基础。

相关推荐
土司大王1 小时前
LeetCode hot100——4.寻找两个正序数组的中位数:Java 二分第K小 递归裁剪
java·算法·leetcode
dllxhcjla1 小时前
SpringAI+DeepSeek大模型应用开发
java
落叶上的秋1 小时前
基于spring-ai快速搭建ai-client、mcp服务
spring·ai
小鱼干..1 小时前
青少年ctf url
java
用户8181870627461 小时前
第26章 Kafka vs RocketMQ vs RabbitMQ真实选型对比
java·后端
lv__pf1 小时前
09-深入理解AQS之独占锁ReentrantLock源码分析【TL 并发 09】
java·开发语言
letisgo51 小时前
JAVA 高级进阶18篇《生产级RAG:切分、检索、评估与Graph RAG全链路实战》
java·面试·检索增强·rag·graph rag
随遇而安zx1 小时前
【并发】---Disruptor 原理、源码与实战 深度解析
java·并发·disruptor
Lyyaoo.1 小时前
【二分查找】【困难】寻找两个有序数组的中位数
java·数据结构·算法