Skip to content

OAuth 2.0 ​

这篇解决什么 ​

要把管理端登录态发给其它系统,或给第三方换一张带 scope 的令牌。读完能新增客户端、走授权码 / 密码换票、在开放接口上挂 @ss.hasScope,以及在令牌页把会话踢掉。

默认端口 48080,管理端前缀 /admin-api。账号密码登录也走同一套访问令牌,不是另一套 JWT。

示意图:请求先过过滤器解析 Token,再进 system 模块的授权与令牌接口。

名称说明仓库路径
开放授权@RequestMapping("/system/oauth2"),发码、换票、校验ruoyi-office/yudao-module-system/.../controller/admin/oauth2/OAuth2OpenController.java
客户端@RequestMapping("/system/oauth2-client")同目录 OAuth2ClientController.java
令牌页@RequestMapping("/system/oauth2-token"),只列未过期同目录 OAuth2TokenController.java
用户信息@ss.hasScope('user.read') / user.write同目录 OAuth2UserController.java
应用页菜单「应用管理」,路由 /system/oauth2/oauth2/applicationruoyi-office-vben/apps/web-antd/src/views/system/oauth2/client/
令牌页菜单「令牌管理」,路由 /system/oauth2/token同目录 token/
SSO 页/auth/sso-login.../views/_core/authentication/sso-login.vue

授权模式 ​

枚举在 OAuth2GrantTypeEnum。换票走 POST /system/oauth2/token;简化模式不走这个接口,只走授权页的 response_type=token。

示意图:机器用客户端模式;人且第三方必须授权码;人且自家系统可用密码或授权码。

名称说明仓库路径
authorization_code授权码。第三方先拿 code,再在服务端换票OAuth2GrantTypeEnum.AUTHORIZATION_CODE
password密码。客户端直接拿账号密码换票PASSWORD
client_credentials客户端。机器调用,用户编号写成 0CLIENT_CREDENTIALS
implicit简化。授权完成后前端直接拿到 TokenIMPLICIT
refresh_token用刷新令牌换新的访问令牌REFRESH_TOKEN

换票时必须带客户端身份:Authorization: Basic base64(client_id:client_secret),或表单字段 client_id + client_secret。解析在 HttpUtils.obtainBasicAuthorization。

Token 接口不接受 implicit

grant_type=implicit 会直接 400。简化模式只允许授权页 response_type=token,由 approveOrDeny 拼重定向 URL。

内部登录与外部授权 ​

管理后台自己登录,不走密码模式。AdminAuthServiceImpl 登录成功后,用内置 client_id = default 调 createAccessToken。其它系统要来拿用户,走授权码 SSO。

示意图:内部登录和外部授权最后都落到 system_oauth2_access_token。

名称说明仓库路径
登录@RequestMapping("/system/auth"),POST /login,@PermitAll.../controller/admin/auth/AuthController.java
默认客户端OAuth2ClientConstants.CLIENT_ID_DEFAULT = "default"yudao-module-system-api/.../enums/oauth2/OAuth2ClientConstants.java
过期时间读该客户端的 accessTokenValiditySeconds / refreshTokenValiditySecondsOAuth2ClientDO

default 的访问令牌默认 1800 秒,刷新令牌 2592000 秒。改客户端配置即改全站登录时效。

为什么登录也复用 OAuth 令牌

登出、改密、令牌页删除都会同时清库和 Redis,会话立刻作废。无状态 JWT 做不到这一点。

应用与令牌 ​

先在应用管理里登记客户端,再让接入方用它的 clientId / secret 换票。令牌管理只展示未过期的访问令牌,删除会走 AdminAuthService.logout。

OAuth2 应用管理

本系统截图:系统管理 → OAuth 2.0 → 应用管理。内置 default 供管理端登录;其它行是接入方客户端。

OAuth2 令牌管理

本系统截图:系统管理 → OAuth 2.0 → 令牌管理。删除一行等于踢掉该会话。

名称说明仓库路径
客户端字段authorizedGrantTypes、scopes、redirectUris、autoApproveScopes.../dal/dataobject/oauth2/OAuth2ClientDO.java
客户端缓存@Cacheable(cacheNames = "oauth_client", key = "#clientId").../service/oauth2/OAuth2ClientServiceImpl.java
权限标识system:oauth2-client:create 等OAuth2ClientController
令牌删除system:oauth2-token:deleteOAuth2TokenController
字典授权类型 system_oauth2_grant_type应用表单 client/data.ts

validOAuthClientFromCache 会核状态、密钥、授权类型、scope 是否越权,以及回调地址是否匹配。

回调地址校验两套规则

授权时 redirect_uri 按前缀匹配 redirectUris(startWithAny)。换票时必须和授权码上记下的地址完全相等。多写一个斜杠就会 400。

改客户端后缓存会 @CacheEvict。OAuth2ClientDO 标了 @TenantIgnore,客户端配置跨租户共用。

授权码 SSO ​

接入方把用户送到 /auth/sso-login,带上 response_type=code、client_id、redirect_uri、scope、可选 state。未登录会先落到 /auth/login,登完再回到授权页。

示意图:授权页只返回重定向 URL,不让 Axios 跟 302。接入方服务端再拿 code 换票。

名称说明仓库路径
授权信息GET /system/oauth2/authorize?clientId=OAuth2OpenController#authorize
申请授权POST /system/oauth2/authorize,scope 是 JSON 对象approveOrDeny
换票POST /system/oauth2/tokenpostAccessToken
授权码表 system_oauth2_code,用一次即删OAuth2CodeServiceImpl
批准记录表 system_oauth2_approve,默认 30 天OAuth2ApproveServiceImpl
前端调用getAuthorize / authorize.../api/system/oauth2/open/index.ts

SSO 页若 URL 已带 scope,会先 auto_approve=true 试一次。全部命中 autoApproveScopes,或用户 30 天内同意过,就不再弹勾选。否则展示「访问你的个人信息」「修改你的个人信息」,对应 user.read / user.write。

用户点「同意授权」后,后端发码并返回

text
{redirect_uri}?code={授权码}&state={state}

接入方服务端再换票。code、redirect_uri、state 必须和发码时一致,client_id 也必须对上。

http
POST /admin-api/system/oauth2/token
Authorization: Basic base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=...&redirect_uri=...&state=...

成功体是 CommonResult,data 里有 accessToken、refreshToken、expiresTime。后续请求:

http
Authorization: Bearer {accessToken}

自动授权和手动授权

auto_approve=true 只检查历史批准和客户端 autoApproveScopes,失败返回空 URL,前端停在勾选页。false 才按用户勾选写入 system_oauth2_approve。

开放给第三方的用户接口用 scope,不用菜单权限:

java
@GetMapping("/get")
@PreAuthorize("@ss.hasScope('user.read')")
public CommonResult<OAuth2UserInfoRespVO> getUserInfo() { /* ... */ }

@PutMapping("/update")
@PreAuthorize("@ss.hasScope('user.write')")
public CommonResult<Boolean> updateUserInfo(@Valid @RequestBody OAuth2UserUpdateReqVO reqVO) { /* ... */ }

hasScope 读的是 LoginUser.scopes,来自令牌,不是角色菜单。

密码模式 ​

自家 App 或内部页面可以直接拿账号密码换票。第三方不要用:它会见到明文密码。

示意图:密码模式没有授权码,也没有 SSO 勾选页。

http
POST /admin-api/system/oauth2/token
Authorization: Basic base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencoded

grant_type=password&username=admin&password=...&scope=user.read user.write

OAuth2GrantServiceImpl#grantPassword 调 AdminAuthService.authenticate,用户类型固定管理端。刷新用 grant_type=refresh_token;作废用 DELETE /system/oauth2/token?token=。

客户端模式给机器用:grant_type=client_credentials,生成的令牌 userId = 0。

Token 校验 ​

每个 /admin-api/**、/app-api/** 请求都会进 TokenAuthenticationFilter。过滤器先看网关透传的 login-user 头;没有再从 Authorization 取 Bearer。

示意图:热路径只读 Redis。Key 是令牌本身,租户在 JSON 里。

名称说明仓库路径
过滤器校验 Token,写入 LoginUserruoyi-office/yudao-framework/yudao-spring-boot-starter-security/.../filter/TokenAuthenticationFilter.java
Redis Keyoauth2_access_token:%s,TTL 等于剩余秒数.../dal/redis/RedisKeyConstants.java
RedisDAOStringRedisTemplate + JsonUtils.../dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java
回源先访问令牌表,没有再把未过期刷新令牌当访问令牌OAuth2TokenServiceImpl#getAccessToken
请求头Authorization: Bearer {accessToken}SecurityProperties.tokenHeader

积木报表、WebSocket 往往只能带一个 token。刷新令牌未过期时,getAccessToken 会把它当成访问令牌用,避免再传 refresh_token。

本地联调可开模拟 Token。yudao.security.mock-enable: true 时,Bearer test1 表示用户 1。生产必须关掉。

删令牌要库和 Redis 一起清

removeAccessToken 会删访问令牌、刷新令牌,并 oauth2AccessTokenRedisDAO.delete。只删表或只删 Key,过滤器仍可能放行。

表与关键类 ​

五张表。客户端是配置;批准是用户勾选记录;授权码短命;访问令牌和刷新令牌带租户。

按本仓库生成的 ER:client_id 存的是客户端编号字符串,不是表主键 id。

名称说明仓库路径
system_oauth2_client客户端。@TenantIgnoreOAuth2ClientDO
system_oauth2_access_token访问令牌。userInfo 给 LoginUserOAuth2AccessTokenDO
system_oauth2_refresh_token刷新令牌,有效期更长OAuth2RefreshTokenDO
system_oauth2_code授权码,5 分钟、一次性OAuth2CodeDO
system_oauth2_approve用户是否接受某个 scopeOAuth2ApproveDO
授予按模式发码或发令牌OAuth2GrantServiceImpl
令牌创建、刷新、校验、删除OAuth2TokenServiceImpl

@TableName("system_oauth2_access_token") 的 scopes、userInfo 用 JacksonTypeHandler。userType 区分管理端和会员。客户端模式没有真实用户,不要按 userId 去查员工档案。

校验、刷新、删除也可以走开放接口:POST /system/oauth2/check-token、DELETE /system/oauth2/token,同样要带客户端 Basic。

配置与操作 ​

管理员在 系统管理 → OAuth 2.0 → 应用管理 建客户端:clientId、密钥、授权模式、回调。保存后立刻能换码。令牌页只做查询和踢线,不改客户端。

改回调地址或授权模式要保存客户端,已发出的 Token 仍有效到过期。开启见 系统功能设计。截图见上文应用和令牌列表。

相关篇 ​

联系我们

获取报价、演示和二开方案

微信咨询二维码

微信咨询

17156169080

添加时备注「RuoYi Office」

在线体验商业版