创建一个Spring Boot Starter风格的Basic认证SDK
文章目录
- 前言
- 设计思路
- SDK实现步骤
- 1. 创建SDK Maven项目(sdk目录)
- 2. 实现配置类
- 3. 实现认证逻辑
- 4. 实现拦截器
- 5. 实现自动配置
- 6. 创建spring.factories文件
- 使用方集成步骤
- 1. 引入SDK依赖
- 2. 配置Application属性
- 3. 创建测试接口
- 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文件:
<?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:
/*** 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:
/*** 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:
/*** 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;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception {// 从请求头中获取Authorization信息String authorizationHeader = request.getHeader("Authorization");// 提取AppID和AppSecretString[] 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:
/*** 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;}@Beanpublic AppAuthService appAuthService() {return new AppAuthService(properties);}@Beanpublic AppAuthInterceptor appAuthInterceptor(AppAuthService appAuthService) {return new AppAuthInterceptor(appAuthService);}@Overridepublic 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文件:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.javabasicsdk.config.AppAuthAutoConfiguration
使用方集成步骤
1. 引入SDK依赖
在使用方的项目中添加SDK依赖:
<!--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:
# App认证配置
app:auth:enabled: trueinclude-patterns:- /test/**# 配置允许访问的AppID和AppSecretclients:appid1: "appsecret1"
3. 创建测试接口
创建测试ControllerTestBasicController.java:
/*** TestController* @author senfel* @version 1.0* @date 2025/9/12 16:33*/
@RestController
@RequestMapping("/test")
public class TestBasicController {@GetMappingpublic 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,只有凭证有效的请求才能访问受保护的接口。这种设计提供了开箱即用的体验,同时保持了足够的灵活性,可以通过配置调整保护的范围和允许的凭证。