授权服务模块基于 Spring Authorization Server,提供完整的 OAuth2/OIDC 授权服务器功能。

架构概述

授权服务遵循标准的 OAuth2 授权码流程,同时扩展了多种认证方式:

  • 认证层:支持授权码、密码、客户端凭证、短信验证码、图形验证码、小程序等多种认证方式

  • Token 层:支持 JWT 和 Opaque Token,可自定义 Token 生成和校验逻辑

  • 客户端管理:通过 RegisteredClientRepository 管理 OAuth2 客户端配置,支持数据库持久化

  • 用户管理:通过 UserInfo 接口自定义用户信息模型

  • 授权服务:支持 Redis 和内存两种 OAuth2AuthorizationService 后端实现

核心概念

  • RegisteredClient:OAuth2 客户端配置,包含客户端ID、密钥、授权类型、重定向URI等

  • UserInfo:用户信息接口,自定义用户对象需实现此接口

  • CustomService:自定义验证服务接口,用于自定义认证逻辑

  • CaptchaVerificationService:验证码校验服务接口

  • IOauth2ClientService:OAuth2 客户端服务接口,用于客户端的增删改查

  • Oauth2Client:客户端实体接口,定义了客户端配置的字段规范

  • Oauth2ClientConvertor:客户端实体转换器,实现 Oauth2Client 与 RegisteredClient 的相互转换

支持的授权类型

授权类型

说明

authorization_code

授权码模式(OAuth2 标准流程)

client_credentials

客户端凭证模式(服务间调用)

refresh_token

刷新令牌(延长会话)

password

密码模式(自定义扩展)

custom

自定义模式

sms

短信验证码登录(自定义扩展)

mini_program

小程序登录(自定义扩展)

授权类型通过 ThinkBridgeOAuth2AuthorizationServerConfiguration 中注册的 AuthenticationConverter 实现,每种扩展授权类型对应一个转换器,在 Token 端点自动匹配。

配置属性

授权服务器配置

所有授权服务器配置前缀为 hink.auth.server:

属性

默认值

说明

think.auth.server.enabled

true

启用授权服务器

think.auth.server.init-client

false

启动时从配置初始化客户端

think.auth.server.captcha.enable

true

启用验证码功能

think.auth.server.captcha.close

false

关闭验证码校验

think.auth.server.sms.enable

true

启用短信验证码登录

think.auth.server.custom.enable

true

启用自定义认证

think.auth.server.mini-program.enable

true

启用小程序登录

JWK 密钥配置

JWK 密钥配置前缀为 hink.auth.jwk-set:

属性

默认值

说明

think.auth.jwk-set.json

JWK 密钥配置(JSON 格式),用于配置 JWT 签名密钥

Token 自定义

框架提供 ThinkbridgeOAuth2TokenCustomizer 自动将 UserInfo 中的扩展信息注入到 JWT Token 的 Claims 中。

扩展信息的前缀通过 hink.auth.userinfo-ext-prefix 配置(默认值参考 UserInfo 接口常量),生成的 Token 中将包含 {prefix}{key} 格式的自定义 Claims。

授权服务后端

框架提供两种 OAuth2AuthorizationService 实现:

  • RedisOAuth2AuthorizationService:基于 Redis 的授权信息存储,支持集群部署,自动管理 Token 过期

  • InMemoryOAuth2AuthorizationService:基于内存的授权信息存储,适用于单机开发环境

当引入 spring-data-redis 依赖并配置 Redis 连接后,自动使用 Redis 实现。

Gradle 引用

// 授权服务基础包
implementation "com.luamas.thinkbridge:authorization-server-spring-boot-starter"
// 资源保护
implementation "com.luamas.thinkbridge:authorization-resource-spring-boot-starter"

完整代码如下:

plugins {
  id 'java'
  id 'org.springframework.boot' version '4.1.1'
  id 'io.spring.dependency-management' version '1.1.7'
}

group = 'com.thinkbridge.demo'
version = '0.0.1-SNAPSHOT'

java {
  sourceCompatibility = '25'
}

repositories {
  mavenCentral()
}

ext {
  springCloudVersion = "2025.1.3"
  thinkBridgeVersion = '2025.1.2'
}

configurations {
  compileOnly {
    extendsFrom annotationProcessor
  }
}

dependencies {
  implementation 'com.luamas.thinkbridge:authorization-server-spring-boot-starter'
  implementation 'com.luamas.thinkbridge:authorization-resource-spring-boot-starter'
  implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
  implementation 'org.springframework.boot:spring-boot-starter-web'
  implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
  implementation "org.springframework.boot:spring-boot-starter-actuator"

  compileOnly 'org.projectlombok:lombok'
  annotationProcessor 'org.projectlombok:lombok'
  testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

dependencyManagement {
  resolutionStrategy {
    cacheChangingModulesFor(0, 'seconds')
  }

  imports {
    mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    mavenBom "com.luamas.thinkbridge:thinkbridge-bom:${thinkBridgeVersion}"
  }
}

test {
  useJUnitPlatform()
}

客户端仓库

授权服务器支持将 OAuth2 客户端信息存储到数据库中,通过实现 RegisteredClientRepository 接口来自定义客户端信息的持久化方式。

接口说明

Oauth2Client 接口定义了客户端实体需要实现的字段规范,包含客户端ID、密钥、授权类型、重定向URI、Token 设置等核心字段。

Oauth2ClientConvertor 工具类负责 Oauth2Client 与 RegisteredClient 之间的相互转换,包括:

  • toRegisteredClient():将 Oauth2Client 实体转换为 RegisteredClient 对象,用于向 Spring Authorization Server 注册客户端

  • fromRegisteredClient():将 RegisteredClient 转换为 Oauth2Client 实体,用于持久化存储

转换过程中自动处理授权类型、认证方法、Token 设置等复杂对象的序列化/反序列化。

实现步骤

1. 自定义客户端仓库

实现 RegisteredClientRepository 接口,从数据库加载客户端信息:

package com.thinkbridge.demo.auth.thinkbridge;

import com.luamas.thinkbridge.common.security.oauth2.IRegisteredClientService;
import com.luamas.thinkbridge.common.security.oauth2.Oauth2Client;
import com.luamas.thinkbridge.common.security.oauth2.Oauth2ClientConvertor;
import com.thinkbridge.demo.auth.service.IOauth2ClientService;
import lombok.RequiredArgsConstructor;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;

import java.util.Collection;
import java.util.function.Function;

@RequiredArgsConstructor
public class MyRegisteredClientRepository implements RegisteredClientRepository, IRegisteredClientService<String> {
    private final IOauth2ClientService myOauth2ClientService;
    @Override
    public void save(RegisteredClient registeredClient) {
        MyOauth2Client myOauth2Client = this.myOauth2ClientService.lambdaQuery().eq(Oauth2Client::getClientId, registeredClient.getClientId()).one();
        MyOauth2Client newOauth2Client = new MyOauth2Client();
        newOauth2Client = Oauth2ClientConvertor.fromRegisteredClient(registeredClient, newOauth2Client);
        if (myOauth2Client != null) {
            newOauth2Client.setId(myOauth2Client.getId());
        }
        this.myOauth2ClientService.saveOrUpdate(newOauth2Client);
    }

    @Override
    public RegisteredClient findById(String id) {
        Oauth2Client myOauth2Client = this.myOauth2ClientService.getById(id);
        return Oauth2ClientConvertor.toRegisteredClient(myOauth2Client);
    }

    @Override
    public RegisteredClient findByClientId(String clientId) {
        Oauth2Client myOauth2Client = this.myOauth2ClientService.lambdaQuery().eq(Oauth2Client::getClientId, clientId).one();
        return Oauth2ClientConvertor.toRegisteredClient(myOauth2Client);
    }


    @Override
    public boolean removeById(String id) {
        return myOauth2ClientService.removeById(id);
    }

    @Override
    public boolean removeByIds(Collection<String> ids) {
        return myOauth2ClientService.removeByIds(ids);
    }
}

2. 客户端服务接口

IOauth2ClientService 接口继承 mybatis-plus 的 IService 接口:

package com.thinkbridge.demo.auth.service;

import com.baomidou.mybatisplus.spring.service.IService;
import com.thinkbridge.demo.auth.thinkbridge.MyOauth2Client;

public interface IOauth2ClientService extends IService<MyOauth2Client> {

}

3. 客户端服务实现

IOauth2ClientService 的实现类:

package com.thinkbridge.demo.auth.service;

import com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;
import com.thinkbridge.demo.auth.thinkbridge.MyOauth2Client;
import com.thinkbridge.demo.auth.mapper.MyOauth2ClientMapper;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class Oauth2ClientServiceImp extends ServiceImpl<MyOauth2ClientMapper, MyOauth2Client> implements IOauth2ClientService {
    private static final PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();

    @Override
    public boolean save(MyOauth2Client myOauth2Client) {
        if (!myOauth2Client.getClientSecret().startsWith("{")) {
            myOauth2Client.setClientSecret(passwordEncoder.encode(myOauth2Client.getClientSecret()));
        }
        return super.save(myOauth2Client);
    }
}

4. 客户端实体类

MyOauth2Client 实体类需要实现 com.luamas.thinkbridge.common.security.oauth2.Oauth2Client 接口:

package com.thinkbridge.demo.auth.thinkbridge;


import com.baomidou.mybatisplus.annotation.*;
import com.luamas.thinkbridge.common.security.oauth2.Oauth2Client;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;


@Getter
@Setter
@ToString
@TableName("sys_oauth2_client")
public class MyOauth2Client implements Oauth2Client<String>, Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    /
     * ID
     */
    @TableId(value = "id", type = IdType.AUTO)
    private String id;

    /
     * 客户端ID
     */
    private String clientId;

    /
     * 颁发时间
     */
    private LocalDateTime clientIdIssuedAt;

    /
     * 客户端密钥
     */
    private String clientSecret;

    /
     * 密钥过期时间
     */
    private LocalDateTime clientSecretExpiresAt;

    /
     * 客户端名称
     */
    private String clientName;

    /
     * 客户端认证方式,逗号分隔
     */
    private String clientAuthenticationMethods;

    /
     * 授权方式,逗号分隔
     */
    private String authorizationGrantTypes;

    /
     * 重定向url,逗号分隔
     */
    private String redirectUris;

    /
     * 退出后的重定向url,逗号分隔
     */
    private String postLogoutRedirectUris;

    /
     * 授权范围,逗号分隔
     */
    private String scopes;

    /
     * 客户端配置,json字符串
     */
    private String clientSettings;

    /
     * token配置,json字符串
     */
    private String tokenSettings;

    /
     * 创建人
     */
    @TableField(value="create_by",fill = FieldFill.INSERT)
    private String createBy;

    /
     * 创建时间
     */
    @TableField(value="create_time",fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    /
     * 更新人
     */
    @TableField(value="update_by",fill = FieldFill.INSERT_UPDATE)
    private String updateBy;

    /
     * 更新时间
     */
    @TableField(value="update_time",fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;

    /
     * 是否删除 0 否 1 是
     */
    @TableLogic(value = "false", delval = "true")
    @TableField(value="is_delete")
    private Boolean deleted;
}

5. 用户实体类

MyOauth2User 实体类:

package com.thinkbridge.demo.auth.thinkbridge;


import com.baomidou.mybatisplus.annotation.*;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;


@Getter
@Setter
@ToString
@Builder
@TableName(value = "sys_user_info")
public class MyOauth2User implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;


    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    @TableField(value = "status")
    private Integer status; // 账号状态:1启用 2禁用 3预留

    @TableField(value = "user_account")
    private String userAccount; // 用户名,唯一

    @TableField(value = "user_password")
    private String userPassword; // 用户密码

    private String wxUnionId;
    private String wxOpenId;
    //租户id
    private Long tenantId;

    @TableLogic
    private Integer deleted;

    /
     * 创建人
     */
    @TableField(value="create_by",fill = FieldFill.INSERT)
    private String createBy;

    /
     * 创建时间
     */
    @TableField(value="create_time",fill = FieldFill.INSERT)
    private Date createTime;

    /
     * 更新人
     */
    @TableField(value="update_by",fill = FieldFill.INSERT_UPDATE)
    private String updateBy;

    /
     * 更新时间
     */
    @TableField(value="update_time",fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
}
Oauth2ClientConvertor 已内置 Jackson 序列化配置,自动处理 ClientSettings 和 TokenSettings 的 JSON 序列化,确保自定义配置项(如 Token 格式、AccessToken 有效期等)在持久化过程中不丢失。

自定义用户对象

授权服务器支持自定义用户对象,通过实现 com.luamas.thinkbridge.common.security.vo.UserInfo 接口来扩展用户信息字段。

接口说明

UserInfo 接口定义了用户信息的基本契约,实现该接口后,自定义的用户对象将替代默认的用户信息模型。

UserInfo 接口包含以下核心方法:

  • 用户基本信息:获取用户名、密码、权限列表等

  • getExtend():返回扩展信息 Map,这些信息会自动注入到 JWT Token 的 Claims 中

扩展信息注入

UserInfo 的 getExtend() 方法返回的扩展信息,会通过 ThinkbridgeOAuth2TokenCustomizer 自动注入到 JWT Token 的 Claims 中。

注入规则:

  • Claims 的 key 格式为:{prefix}{extendKey}

  • 前缀通过 think.auth.userinfo-ext-prefix 配置(默认值参考 UserInfo 接口常量)

  • 例如:扩展信息包含 { "department": "engineering" },前缀为 ext_,则 JWT 中会增加 ext_department Claim

这种设计使得在不修改 Token 自定义逻辑的情况下,通过 UserInfo 实现即可灵活控制 JWT 中的自定义字段。

实现示例

以下示例展示 com.thinkbridge.demo.auth.thinkbridge.MyUserInfo 类的实现:

package com.thinkbridge.demo.auth.thinkbridge;

import com.luamas.thinkbridge.common.security.vo.UserInfo;
import lombok.Getter;
import lombok.Setter;
import org.springframework.security.core.GrantedAuthority;

import java.util.*;

@Getter
@Setter
public class MyUserInfo implements UserInfo {
    private String userId;
    private String name;
    private Collection<? extends GrantedAuthority> authorities = new HashSet<>();
    private Collection<String> roleIds = new HashSet<>();
    private Map<String, Object> extend = new HashMap<>();

}
自定义用户对象需要实现 UserInfo 接口的所有方法,包括获取用户名、权限列表等核心信息。扩展信息可以通过 getExtend() 返回任意键值对。

自定义验证服务

授权服务器支持自定义验证服务,通过实现 com.luamas.thinkbridge.common.security.authentication.custom.CustomService 接口来扩展认证逻辑。

接口说明

CustomService 接口用于自定义认证流程,当内置的授权类型(如 uthorization_code、client_credentials 等)无法满足需求时,可通过实现此接口添加自定义的认证方式。

CustomService 接口包含以下核心方法:

  • 识别认证类型:区分不同类型的自定义认证请求

  • 执行认证逻辑:验证用户凭证,返回用户信息

  • 返回 UserInfo:认证成功后返回实现了 UserInfo 接口的用户对象

自动注册

ThinkBridgeGlobalAuthenticationConfigurer 会自动检测 Spring 容器中是否存在 CustomService 类型的 Bean:

  • 如果存在,自动注册 CustomAuthenticationProvider

  • 如果不存在,跳过自定义认证逻辑的注册

同时需要启用 think.auth.server.custom.enable=true(默认启用)。

实现示例

以下示例展示 com.thinkbridge.demo.auth.thinkbridge.MyCustomService 类的实现:

package com.thinkbridge.demo.auth.thinkbridge;

import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.luamas.thinkbridge.common.security.authentication.custom.CustomService;
import com.luamas.thinkbridge.common.security.vo.UserInfo;
import com.thinkbridge.demo.auth.mapper.MyOauth2UserMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.util.*;

@Service
@RequiredArgsConstructor
@Slf4j
@ConditionalOnProperty(prefix = "demo.jwk.set.aes", name = "key", matchIfMissing = false)
public class MyCustomService implements CustomService {
    private final MyOauth2UserMapper myOauth2UserMapper;
    @Value("${demo.jwk.set.aes.key}")
    private String secretKey;

    public static void main(String[] args) {
        AES aes = SecureUtil.aes("gJ0cD3bH0dK0hD2a".getBytes(StandardCharsets.UTF_8));// 创建AES解密器
        System.out.println(aes.encryptHex("admin_7771"));
    }

    @Override
    public UserInfo verify(Object principal, Object credentials) {
        AES aes = SecureUtil.aes(secretKey.getBytes(StandardCharsets.UTF_8));// 创建AES解密器
        String username = null;
        Long tenantId = null;
        if (principal != null && principal instanceof String) {
            tenantId = Long.valueOf(principal.toString());
        }
//        if (tenantId == null) {
//            throw new BadCredentialsException("tenantId不能为空");
//        }
        if (credentials instanceof String) {
            try {
                username = aes.decryptStr(credentials.toString());
            } catch (Exception e) {
                log.warn("秘钥错误:{},{}", tenantId, username);
                throw new BadCredentialsException("秘钥错误");
            }
        }
        if (StringUtils.isBlank(username)) {
            log.info("秘钥内容错误:{},{}", tenantId, username);
            throw new BadCredentialsException("秘钥内容错误");
        }
        MyOauth2User myOauth2User  = myOauth2UserMapper.selectOne(new LambdaQueryWrapper<MyOauth2User>().eq(MyOauth2User::getUserAccount, username).ne(MyOauth2User::getStatus, 3));
        if(myOauth2User == null){
            log.info("用户错误:{},{}", tenantId, username);
            throw new BadCredentialsException("用户错误");
        }
        if(tenantId!= null && !tenantId.equals(myOauth2User.getTenantId())){
            log.info("sid越权:{},{}", tenantId, username);
            throw new BadCredentialsException("tenantId越权");
        }
        Map<String, Object> map = new HashMap<>();
        map.put("uid", myOauth2User.getId());
        map.put("tenant_id", myOauth2User.getTenantId());
        map.put("name", myOauth2User.getUserAccount());
        MyUserInfo myUserInfo = new MyUserInfo();
        myUserInfo.setUserId(myOauth2User.getId().toString());
        myUserInfo.setName(myOauth2User.getUserAccount());
        myUserInfo.setAuthorities(new ArrayList<>());
        myUserInfo.setRoleIds(new ArrayList<>());
        myUserInfo.setExtend(map);
        return myUserInfo;
    }
}
自定义验证服务适用于需要集成第三方认证、短信验证码登录、微信扫码登录等场景。框架会根据 token 端点请求中的 grant_type 参数自动匹配对应的认证处理器。

验证码校验服务

授权服务器支持在登录流程中集成验证码校验,通过实现 CaptchaVerificationService 接口来自定义验证码校验逻辑。

接口说明

CaptchaVerificationService 接口定义了验证码校验的契约,实现该接口并将其注册为 Spring Bean,框架会自动在登录流程中调用。

CaptchaVerificationService 包含以下核心方法:

  • supports():判断是否支持当前验证码类型,支持多种验证码类型共存的场景

  • verify():执行验证码校验逻辑,返回校验结果

自动注册机制

验证码认证的自动注册逻辑如下:

  1. ThinkBridgeGlobalAuthenticationConfigurer 检测容器中是否存在 PasswordVerificationService Bean

  2. 如果存在,进一步检测 CaptchaVerificationService 类型的 Bean 列表

  3. 如果至少存在一个,自动注册 CaptchaAuthenticationProvider

  4. 整个流程受 think.auth.server.captcha.enable=true(默认启用)控制

通过 think.auth.server.captcha.close=true 可关闭验证码校验(仍启用验证码功能,但跳过校验步骤)。

实现示例

以下示例展示了如何实现自定义的验证码校验服务,只需校验 type 符合自己定义的 type 即可:

package com.thinkbridge.demo.auth.thinkbridge;

import com.luamas.thinkbridge.common.base.exception.*;
import com.luamas.thinkbridge.common.captcha.common.CaptchaManger;
import com.luamas.thinkbridge.common.captcha.common.dto.CaptchaVerificationDto;
import com.luamas.thinkbridge.common.security.authentication.captcha.CaptchaVerificationService;
import org.springframework.stereotype.Component;
import tools.jackson.databind.ObjectMapper;

@Component
public class CaptchaVerificationServiceImpl implements CaptchaVerificationService {
    private final ObjectMapper objectMapper;
    private final CaptchaManger captchaManger;
    public CaptchaVerificationServiceImpl(ObjectMapper objectMapper, CaptchaManger captchaManger) {
        this.objectMapper = objectMapper;
        this.captchaManger = captchaManger;
    }
    /
     * 检查
     * @param captcha
     * @return
     */
    @Override
    public boolean check(String captcha) {
        try {
            CaptchaVerificationDto<?, ?> dto = objectMapper.readValue(captcha, CaptchaVerificationDto.class);
            captchaManger.verify(dto);
        } catch (BusinessException e) {
            throw UnauthorizedException.of(e.getCode(), e.getMessage(), e.getArgs());
        } catch (Exception e) {
            throw InternalException.of(BaseResultCode.INTERNAL_ERROR);
        }
        return true;
    }

    /
     * 是否支持
     * @param type
     * @return
     */
    @Override
    public boolean supports(String type) {
        return captchaManger.supports(type);
    }
}
可以支持多种验证码类型(如图形验证码、滑动验证码、点击验证码等),在 supports() 方法中根据 type 进行匹配。

配置

授权服务器支持通过 think.auth.server.* 配置属性对认证流程进行精细化控制。

配置文件示例

以下为授权服务器的完整 application.yml 配置示例:

think:
  auth:
    server:
      enabled: true                          # 启用授权服务器(默认 true)
      init-client: false                     # 是否根据配置初始化客户端(默认 false)
      captcha:
        enable: true                         # 启用验证码(默认 true)
        close: false                         # 关闭验证码校验(默认 false)
      sms:
        enable: true                         # 启用短信验证码登录(默认 true)
      custom:
        enable: true                         # 启用自定义认证(默认 true)
      mini-program:
        enable: true                         # 启用小程序登录(默认 true)
    jwk-set:
      json: '{"kty":"oct","kid":"my-key-id","k":"my-base64-encoded-key"}'  # JWK 密钥配置(可选)

授权服务器配置

AuthorizationServerConfig

示例项目中的 AuthorizationServerConfig 配置类包含以下核心 Bean:

RegisteredClientRepository

自定义的 MyRegisteredClientRepository 实现,将 OAuth2 客户端配置持久化到数据库:

@Bean
public MyRegisteredClientRepository myRegisteredClientRepository(IOauth2ClientService iOauth2ClientService) {
    MyRegisteredClientRepository myJdbcRegisteredClientRepository = new MyRegisteredClientRepository(iOauth2ClientService);
    return myJdbcRegisteredClientRepository;
}
PasswordEncoder

密码编码器配置,支持多种密码编码格式:

@Bean
public PasswordEncoder passwordEncoder() {
    DelegatingPasswordEncoder delegatingPasswordEncoder = (DelegatingPasswordEncoder) PasswordEncoderFactories.createDelegatingPasswordEncoder();
    delegatingPasswordEncoder.setDefaultPasswordEncoderForMatches(NoOpPasswordEncoder.getInstance());
    return delegatingPasswordEncoder;
}

OAuth2 端点

授权服务器注册以下标准 OAuth2 端点:

端点

说明

/oauth2/token

Token 端点,用于获取和刷新 Token

/oauth2/authorize

授权端点,用于授权码流程

/oauth2/jwks

JWK Set 端点,用于获取公钥

/oauth2/revoke

Token 撤销端点

/oauth2/introspect

Token 自省端点

.well-known/oauth-authorization-server

OAuth2 元数据端点

.well-known/openid-configuration

OIDC 配置端点

自动配置

AuthorizationServerAutoConfiguration 自动配置类默认启用( think.auth.server.enabled=true),包含以下核心功能:

OAuth2AuthorizationServerAutoConfiguration

Spring Boot 原生 OAuth2 授权服务器自动配置,提供基础的 OAuth2 端点注册。

ThinkBridgeOAuth2AuthorizationServerConfiguration

自定义 OAuth2 配置,包含以下扩展功能:

  • 扩展授权类型:支持密码模式、验证码模式、短信登录、小程序登录等扩展授权类型

  • AuthenticationConverter:每种扩展授权类型对应一个请求转换器,从 HTTP 请求中提取认证参数

  • AuthenticationProvider:对应的认证处理器,验证认证参数并生成 Token

  • Token 端点:所有扩展授权类型共享 Token 端点,通过 grant_type 参数自动匹配对应的转换器和处理器

ThinkBridgeGlobalAuthenticationConfigurer

全局认证处理器配置,根据配置属性自动注册以下认证提供者:

  • PasswordAuthenticationProvider:用户名密码认证(始终启用)

  • CaptchaAuthenticationProvider:图形验证码认证(需启用 think.auth.server.captcha.enable)

  • SMSAuthenticationProvider:短信验证码认证(需启用 think.auth.server.sms.enable)

  • CustomAuthenticationProvider:自定义认证(需启用 think.auth.server.custom.enable)

  • MiniProgramAuthenticationProvider:小程序登录(需启用 think.auth.server.mini-program.enable)

以上认证提供者仅在对应的 Service Bean 存在时才会注册。

ThinkbridgeOAuth2TokenCustomizer

Token 自定义器,自动将 UserInfo 中 getExtend() 返回的扩展信息注入到 JWT 的 Claims 中。

Claims 的 key 格式为 {userinfoExtPrefix}{extendKey},前缀通过 think.auth.userinfo-ext-prefix 配置。

InitClientFromConfigToRepositoryRuner

启动时初始化客户端 Runner,需满足以下条件:

  • think.auth.server.init-client=true

  • 存在 RegisteredClientRepository Bean

  • 存在 InitAuthorizationService Bean

启动时会从配置文件中读取客户端配置,调用 RegisteredClientRepository.save() 方法将配置持久化到数据库。

完整的配置属性请参考 授权基础配置 文档。