创建一个Spring Boot Starter风格的Basic认证SDK

文章目录

    • 前言
    • 设计思路
    • SDK实现步骤
      • [1. 创建SDK Maven项目(sdk目录)](#1. 创建SDK Maven项目(sdk目录))
      • [2. 实现配置类](#2. 实现配置类)
      • [3. 实现认证逻辑](#3. 实现认证逻辑)
      • [4. 实现拦截器](#4. 实现拦截器)
      • [5. 实现自动配置](#5. 实现自动配置)
      • [6. 创建spring.factories文件](#6. 创建spring.factories文件)
    • 使用方集成步骤
      • [1. 引入SDK依赖](#1. 引入SDK依赖)
      • [2. 配置Application属性](#2. 配置Application属性)
      • [3. 创建测试接口](#3. 创建测试接口)
      • [4. 测试接口访问](#4. 测试接口访问)
    • SDK扩展功能
    • 总结

前言

Spring Boot作为当前最流行的Java开发框架之一,其Starter机制为开发者提供了极大的便利,使得集成各种功能变得更加简单。为了帮助开发者快速实现基于AppID和AppSecret的应用认证功能,我们设计并实现了这个认证SDK。该SDK遵循Spring Boot Starter的最佳实践,可以无缝集成到Spring Boot应用中,提供开箱即用的Basic认证功能。

设计思路

创建一个Spring Boot Starter风格的SDK,它会:

1.自动配置认证所需的组件

2.提供简单的配置方式管理多个AppID/AppSecret

3.通过拦截器自动保护指定接口

4.提供统一的错误响应处理

下面是整个认证流程的示意图:

SDK实现步骤

1. 创建SDK Maven项目(sdk目录)

创建sdk/pom.xml文件:

bash 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>java-basic-sdk</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>java-basic-sdk</name>
    <description>java-basic-sdk</description>
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.6.13</spring-boot.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2. 实现配置类

创建配置属性类AppAuthProperties.java:

bash 复制代码
/**
 * AppAuthProperties
 * @author senfel
 * @version 1.0
 * @date 2025/9/12 16:17
 */
@ConfigurationProperties(prefix = "app.auth")
public class AppAuthProperties {
    /**
     * 是否启用App认证
     */
    private boolean enabled = true;

    /**
     * 需要认证的路径模式(默认保护/test接口)
     */
    private String[] includePatterns = new String[]{"/test/**"};

    /**
     * 排除的路径模式
     */
    private String[] excludePatterns = new String[]{};

    /**
     * 存储有效的AppID和AppSecret键值对
     */
    private Map<String, String> clients = new HashMap<>();

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public String[] getIncludePatterns() {
        return includePatterns;
    }

    public void setIncludePatterns(String[] includePatterns) {
        this.includePatterns = includePatterns;
    }

    public String[] getExcludePatterns() {
        return excludePatterns;
    }

    public void setExcludePatterns(String[] excludePatterns) {
        this.excludePatterns = excludePatterns;
    }

    public Map<String, String> getClients() {
        return clients;
    }

    public void setClients(Map<String, String> clients) {
        this.clients = clients;
    }
}

3. 实现认证逻辑

创建认证服务类AppAuthService.java:

bash 复制代码
/**
 * AppAuthService
 * @author senfel
 * @version 1.0
 * @date 2025/9/12 16:19
 */
public class AppAuthService {
    private final AppAuthProperties properties;

    public AppAuthService(AppAuthProperties properties) {
        this.properties = properties;
    }

    /**
     * 验证AppID和AppSecret是否有效
     */
    public boolean validateCredentials(String appId, String appSecret) {
        if (!StringUtils.hasText(appId) || !StringUtils.hasText(appSecret)) {
            return false;
        }

        // 检查配置中是否存在该AppID且AppSecret匹配
        return properties.getClients().containsKey(appId) &&
                properties.getClients().get(appId).equals(appSecret);
    }

    /**
     * 从HTTP Basic认证头中提取凭证
     */
    public String[] extractCredentials(String authorizationHeader) {
        if (!StringUtils.hasText(authorizationHeader) ||
                !authorizationHeader.startsWith("Basic ")) {
            return null;
        }

        try {
            String base64Credentials = authorizationHeader.substring(6);
            String credentials = new String(Base64.getDecoder().decode(base64Credentials));
            return credentials.split(":", 2);
        } catch (Exception e) {
            return null;
        }
    }
}

4. 实现拦截器

创建认证拦截器AppAuthInterceptor.java:

bash 复制代码
/**
 * AppAuthInterceptor
 * @author senfel
 * @version 1.0
 * @date 2025/9/12 16:20
 */
public class AppAuthInterceptor implements HandlerInterceptor {
    private final AppAuthService appAuthService;

    public AppAuthInterceptor(AppAuthService appAuthService) {
        this.appAuthService = appAuthService;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        // 从请求头中获取Authorization信息
        String authorizationHeader = request.getHeader("Authorization");

        // 提取AppID和AppSecret
        String[] credentials = appAuthService.extractCredentials(authorizationHeader);

        if (credentials == null || credentials.length != 2) {
            sendErrorResponse(response, "缺少有效的Authorization请求头,请使用Basic认证格式");
            return false;
        }

        String appId = credentials[0];
        String appSecret = credentials[1];

        // 验证凭证
        if (!appAuthService.validateCredentials(appId, appSecret)) {
            sendErrorResponse(response, "无效的AppID或AppSecret");
            return false;
        }

        return true;
    }

    private void sendErrorResponse(HttpServletResponse response, String message)
            throws Exception {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write("{\"code\": 401, \"message\": \"" + message + "\"}");
    }
}

5. 实现自动配置

创建自动配置类AppAuthAutoConfiguration.java:

bash 复制代码
/**
 * AppAuthAutoConfiguration
 * @author senfel
 * @version 1.0
 * @date 2025/9/12 16:22
 */
@Configuration
@ConditionalOnWebApplication
@EnableConfigurationProperties(AppAuthProperties.class)
@ConditionalOnProperty(prefix = "app.auth", name = "enabled", havingValue = "true", matchIfMissing = true)
public class AppAuthAutoConfiguration implements WebMvcConfigurer {
    private final AppAuthProperties properties;

    public AppAuthAutoConfiguration(AppAuthProperties properties) {
        this.properties = properties;
    }

    @Bean
    public AppAuthService appAuthService() {
        return new AppAuthService(properties);
    }

    @Bean
    public AppAuthInterceptor appAuthInterceptor(AppAuthService appAuthService) {
        return new AppAuthInterceptor(appAuthService);
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(appAuthInterceptor(appAuthService()))
                .addPathPatterns(properties.getIncludePatterns())
                .excludePathPatterns(properties.getExcludePatterns());
    }
}

6. 创建spring.factories文件

创建sdk/src/main/resources/META-INF/spring.factories文件:

bash 复制代码
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.javabasicsdk.config.AppAuthAutoConfiguration

使用方集成步骤

1. 引入SDK依赖

在使用方的项目中添加SDK依赖:

bash 复制代码
<!--java-basic-sdk-->
<dependency>
    <groupId>com.example</groupId>
    <artifactId>java-basic-sdk</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

2. 配置Application属性

在使用方的application.yml中配置允许访问的AppID和AppSecret:

bash 复制代码
# App认证配置
app:
  auth:
    enabled: true
    include-patterns:
      - /test/**
    # 配置允许访问的AppID和AppSecret
    clients:
      appid1: "appsecret1"

3. 创建测试接口

创建测试ControllerTestBasicController.java:

bash 复制代码
/**
 * TestController
 * @author senfel
 * @version 1.0
 * @date 2025/9/12 16:33
 */
@RestController
@RequestMapping("/test")
public class TestBasicController {

    @GetMapping
    public String test() {
        return "认证成功,可以访问此接口";
    }

    @GetMapping("/sub")
    public String testSub() {
        return "认证成功,可以访问子接口";
    }
}

4. 测试接口访问

不带basic请求头,会提示认证失败:

带错误的appId/appSecret,会提示验证失败:

正确的认证信息可以访问:

SDK扩展功能

如果想进一步增强SDK的功能,可以考虑:

1.添加日志记录:记录认证成功和失败的请求

2.限流功能:为每个AppID添加请求频率限制

3.动态配置:支持从数据库或配置中心动态加载AppID/AppSecret

4.管理接口:提供管理接口用于动态添加/删除AppID和AppSecret

5.多种认证方式:除了Basic认证,支持自定义请求头等方式

总结

通过以上实现,创建一个Spring Boot Starter风格的AppID/AppSecret认证SDK,使用方只需:

1.引入SDK依赖

2.在application.yml中配置允许访问的AppID和AppSecret

3.创建需要保护的接口(如/test)

SDK会自动拦截配置的接口路径,验证请求中的AppID和AppSecret,只有凭证有效的请求才能访问受保护的接口。这种设计提供了开箱即用的体验,同时保持了足够的灵活性,可以通过配置调整保护的范围和允许的凭证。

相关推荐
码农小伙2 小时前
通俗易懂地讲解JAVA的BIO、NIO、AIO
java·nio
不要再敲了3 小时前
JDBC从入门到面试:全面掌握Java数据库连接技术
java·数据库·面试
潇I洒4 小时前
若依4.8.1打包war后在Tomcat无法运行,404报错的一个解决方法
java·tomcat·ruoyi·若依·404
方圆想当图灵4 小时前
如何让百万 QPS 下的服务更高效?
分布式·后端
凤山老林4 小时前
SpringBoot 轻量级一站式日志可视化与JVM监控
jvm·spring boot·后端
凡梦千华4 小时前
Django时区感知
后端·python·django
Funcy4 小时前
XxlJob 源码分析05:执行器注册流程
java
Boop_wu4 小时前
[数据结构] 队列 (Queue)
java·jvm·算法
无敌的神原秋人4 小时前
关于Redis不同序列化压缩性能的对比
java·redis·缓存