物联网平台中的Swagger(二)安全认证与生产实践
企业级物联网平台开发中,API文档的安全性和生产环境部署策略至关重要。本文探讨Swagger在安全认证机制设计、生产环境部署、性能优化等方面的实践经验。
一、安全认证机制设计
1.1 多重安全策略
物联网平台的API文档需要考虑安全性,实现JWT Token和Basic认证的双重保护:
private List<SecurityScheme> securitySchemes() {List<SecurityScheme> securitySchemes = new ArrayList<>();// JWT Token认证securitySchemes.add(new ApiKey("Authorization", "Authorization", "header"));// Basic认证(可选)if (swaggerBasicConfig.isEnable()) {securitySchemes.add(new BasicAuth("basicAuth"));}return securitySchemes;
}private List<SecurityContext> securityContexts() {List<SecurityContext> securityContexts = new ArrayList<>();// JWT Token上下文securityContexts.add(SecurityContext.builder().securityReferences(defaultAuth()).forPaths(PathSelectors.regex(".*v3.*")).build());// Basic认证上下文if (swaggerBasicConfig.isEnable()) {securityContexts.add(SecurityContext.builder().securityReferences(basicAuth()).forPaths(PathSelectors.any()).build());}return securityContexts;
}
1.2 Basic认证过滤器实现
为了保护Swagger文档访问,实现专门的Basic认证过滤器:
@Component
public class SwaggerBasicAuthFilter implements Filter {@Autowiredprivate SwaggerBasicConfig swaggerBasicConfig;@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest) request;HttpServletResponse httpResponse = (HttpServletResponse) response;String requestURI = httpRequest.getRequestURI();// 检查是否为Swagger相关路径if (isSwaggerPath(requestURI)) {if (swaggerBasicConfig.isEnable()) {if (!authenticate(httpRequest)) {httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);httpResponse.setHeader("WWW-Authenticate", "Basic realm=\"Swagger\"");httpResponse.getWriter().write("Unauthorized");return;}}}chain.doFilter(request, response);}private boolean isSwaggerPath(String requestURI) {// 排除登录相关路径和静态资源if (requestURI.equals("/") || requestURI.equals("/login") || requestURI.startsWith("/login") ||requestURI.startsWith("/static/") ||requestURI.startsWith("/webjars/") ||requestURI.startsWith("/resources/")) {return false;}return requestURI.contains("/swagger-ui") || requestURI.contains("/doc.html") ||requestURI.contains("/swagger-resources") ||requestURI.contains("/v2/api-docs");}private boolean authenticate(HttpServletRequest request) {String authHeader = request.getHeader("Authorization");if (!StringUtils.hasText(authHeader) || !authHeader.startsWith("Basic ")) {return false;}try {String base64Credentials = authHeader.substring(6);byte[] credDecoded = Base64Utils.decodeFromString(base64Credentials);String credentials = new String(credDecoded, StandardCharsets.UTF_8);String[] values = credentials.split(":", 2);if (values.length != 2) {return false;}String username = values[0];String password = values[1];return swaggerBasicConfig.getUsername().equals(username) && swaggerBasicConfig.getPassword().equals(password);} catch (Exception e) {return false;}}
}
1.3 配置化认证参数
@Component
public class SwaggerBasicConfig {@Value("${swagger.basic.enable}")private boolean enable;@Value("${swagger.basic.username}")private String username;@Value("${swagger.basic.password}")private String password;// getter方法...
}
对应的配置文件:
swagger:basic:enable: trueusername: adminpassword: swagger123
二、Controller层注解
2.1 控制器级别注解
@RestController
@RequestMapping("/algorithmExecute")
@Api(tags = "算法执行接口")
@Slf4j
public class AlgorithmExecuteController extends BaseController {@Autowiredprivate IAlgorithmConfigService algorithmConfigService;@Autowiredprivate IAlgorithmExecuteService algorithmExecuteService;/*** 获取所有可用的算法配置*/@Log(title = "可用算法配置查询", businessType = BusinessTypeEnum.SELECT)@ApiOperation("获取可用算法配置列表")@GetMapping("/listAvailable")public AjaxResult listAvailable() {try {LambdaQueryWrapper<AlgorithmConfig> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(AlgorithmConfig::getStatus, 1);List<AlgorithmConfig> list = algorithmConfigService.list(queryWrapper);return success(list);} catch (Exception e) {log.error("获取可用算法配置列表失败: {}", e.getMessage(), e);return error("获取可用算法配置列表失败: " + e.getMessage());}}/*** 执行算法*/@Log(title = "算法执行操作", businessType = BusinessTypeEnum.OPERATING)@ApiOperation("执行算法")@PostMapping("/execute")public AjaxResult execute(@RequestParam Long algorithmId, @RequestParam String fileUrl) {if (algorithmId == null) {return error("算法ID不能为空");}if (StringUtils.isEmpty(fileUrl)) {return error("文件URL不能为空");}try {JSONObject result = algorithmExecuteService.executeAlgorithm(algorithmId, fileUrl);return success(result);} catch (Exception e) {log.error("算法执行异常: {}", e.getMessage(), e);return error("算法执行异常: " + e.getMessage());}}
}
2.2 实体类注解规范
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel(value="设备控制对象", description="设备控制字段及数据表")
public class DeviceControl extends BaseEntity {private static final long serialVersionUID = 1L;@ApiModelProperty(value = "主键")@TableId(value = "id", type = IdType.AUTO)private Integer id;@ApiModelProperty(value = "设备ID")@TableField("node_id")private Integer nodeId;@ApiModelProperty(value = "字段代码")@TableField("field_code")private String fieldCode;@ApiModelProperty(value = "字段名称")@TableField("field_name")private String fieldName;@ApiModelProperty(value = "字段单位")@TableField("field_unit")private String fieldUnit;@ApiModelProperty(value = "字段说明")@TableField("field_desc")private String fieldDesc;@ApiModelProperty(value = "字段值,控制时有用")@TableField("field_value")private String fieldValue;@ApiModelProperty(value = "字段类型(0:值 ,1:按钮)")@TableField("field_type")private String fieldType;@ApiModelProperty(value = "字段是否显示(0:不显示 ,1:显示)")@TableField("field_show")private String fieldShow;@ApiModelProperty(value = "显示顺序")@TableField("order_num")private Integer orderNum;@ApiModelProperty(value = "输入输出 i1输入,o0输出")@TableField("in_out")private String inOut;
}
2.3 复杂参数注解示例
@RestController
@RequestMapping("/admin/device")
@Api(tags = "设备管理")
public class DeviceController extends BaseController {@ApiOperation(value = "设备列表查询", notes = "支持分页和条件查询")@ApiImplicitParams({@ApiImplicitParam(name = "pageNum", value = "页码", defaultValue = "1", dataType = "int", paramType = "query"),@ApiImplicitParam(name = "pageSize", value = "每页数量", defaultValue = "10", dataType = "int", paramType = "query"),@ApiImplicitParam(name = "deviceName", value = "设备名称", dataType = "string", paramType = "query"),@ApiImplicitParam(name = "status", value = "设备状态", dataType = "string", paramType = "query")})@ApiResponses({@ApiResponse(code = 200, message = "查询成功"),@ApiResponse(code = 400, message = "参数错误"),@ApiResponse(code = 500, message = "服务器内部错误")})@GetMapping("/list")public AjaxResult list(@ApiParam(value = "查询条件") DeviceQueryParam queryParam) {// 实现逻辑return success(deviceService.selectDeviceList(queryParam));}@ApiOperation(value = "新增设备", notes = "创建新的物联网设备")@PostMappingpublic AjaxResult add(@ApiParam(value = "设备信息", required = true) @RequestBody @Valid Device device) {return toAjax(deviceService.insertDevice(device));}@ApiOperation(value = "修改设备", notes = "更新设备信息")@PutMappingpublic AjaxResult edit(@ApiParam(value = "设备信息", required = true) @RequestBody @Valid Device device) {return toAjax(deviceService.updateDevice(device));}@ApiOperation(value = "删除设备", notes = "根据设备ID删除设备")@DeleteMapping("/{deviceIds}")public AjaxResult remove(@ApiParam(value = "设备ID数组", required = true) @PathVariable Long[] deviceIds) {return toAjax(deviceService.deleteDeviceByIds(deviceIds));}
}
三、生产环境部署策略
3.1 环境隔离配置
# application-dev.yml (开发环境)
swagger:enabled: truebasic:enable: falseusername: adminpassword: swagger123# application-test.yml (测试环境)
swagger:enabled: truebasic:enable: trueusername: testpassword: test123# application-prod.yml (生产环境)
swagger:enabled: false # 生产环境禁用
3.2 条件化配置
@EnableKnife4j
@EnableSwagger2
@Profile({"dev", "test"}) // 只在开发和测试环境启用
@ConditionalOnProperty(name = "swagger.enabled", havingValue = "true")
@Configuration
public class SwaggerConfig implements WebMvcConfigurer {// 配置内容
}
3.3 访问地址配置
# 开发环境访问地址
server:port: 8900# 访问URL:
# Swagger UI: http://localhost:8900/swagger-ui.html
# Knife4j文档: http://localhost:8900/doc.html
四、常见问题
4.1 版本兼容性问题
问题:SpringFox 2.9.2版本存在NumberFormatException
解决方案:
<dependency><groupId>io.swagger</groupId><artifactId>swagger-models</artifactId><version>1.6.0</version>
</dependency>
4.2 静态资源访问问题
问题:Swagger UI界面无法正常加载
解决方案:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
4.3 安全配置冲突
问题:Swagger与Spring Security配置冲突
解决方案:
@Override
public void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/swagger-ui.html","/doc.html","/webjars/**","/swagger-resources/**","/v2/api-docs");
}
总结
实际应用中,建议根据项目具体需求和团队规模,适当调整配置策略和使用规范。
参考:https://apifox.com/apiskills/how-to-solve-swagger-api-security/
https://www.cnblogs.com/fortuneju/p/18590291
https://blog.csdn.net/luChenH/article/details/96598433