You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何传递用户信息限制Swagger API端点及基于认证结果渲染API

针对你遇到的这两个Swagger技术难题,我分享下实际项目里用过的可行方案,应该能解决你的困扰:

1. 传递用户信息以限制Swagger API端点

这里核心思路是把用户身份信息传递到Swagger的配置逻辑中,以此过滤用户无权访问的端点,常用的实现方式有两种:

  • 配置Swagger安全机制+接口过滤
    你可以给Swagger添加自定义的安全参数(比如请求头里的用户ID或令牌),然后通过OperationFilter来动态过滤接口。举个Spring Boot环境下的代码示例:

    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build()
                // 添加安全参数,让用户在Swagger页面输入用户标识
                .securitySchemes(Collections.singletonList(new ApiKey("X-User-ID", "X-User-ID", "header")))
                .securityContexts(Collections.singletonList(SecurityContext.builder()
                        .securityReferences(Collections.singletonList(new SecurityReference("X-User-ID", new AuthorizationScope[0])))
                        .build()))
                // 自定义接口过滤器,根据用户信息判断是否展示该接口
                .operationFilter((operation, handlerMethod) -> {
                    HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
                    String userId = request.getHeader("X-User-ID");
                    // 这里替换成你的权限校验逻辑,比如根据userId查询用户权限,判断是否能访问当前接口
                    return userId != null && checkUserPermission(userId, operation);
                });
    }
    
    // 示例权限校验方法
    private boolean checkUserPermission(String userId, Operation operation) {
        // 实际业务中可以从数据库/缓存获取用户权限,和接口要求的权限对比
        Set<String> userPermissions = getUserPermissionsFromDB(userId);
        String apiPermission = operation.getNotes(); // 假设把接口权限标识写在ApiOperation的notes里
        return userPermissions.contains(apiPermission) || apiPermission == null;
    }
    

    配置后,用户在Swagger页面需要先填写X-User-ID参数,后端会根据这个标识过滤掉用户无权访问的接口,只展示符合权限的内容。

  • 结合现有权限框架联动
    如果你的系统已经用了Spring Security这类权限框架,可以直接复用现有逻辑。比如在ApiOperation注解里标记接口所需的角色,然后在Swagger的配置中通过RequestHandlerSelectors过滤出用户有权限的接口:

    @Bean
    public Docket api() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Set<String> userRoles = authentication.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.toSet());
    
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(input -> {
                    ApiOperation apiOp = input.getHandlerMethod().getMethodAnnotation(ApiOperation.class);
                    if (apiOp == null) return true;
                    // 从ApiOperation的extensions中获取所需角色
                    String requiredRole = apiOp.extensions().stream()
                            .filter(ext -> "requiredRole".equals(ext.getName()))
                            .map(ext -> ext.value().toString())
                            .findFirst().orElse(null);
                    return requiredRole == null || userRoles.contains(requiredRole);
                })
                .paths(PathSelectors.any())
                .build();
    }
    
2. 根据凭证验证结果动态渲染Swagger API

这个需求的核心是动态生成Swagger文档,而不是用静态的配置。具体可以分三步实现:

  • 第一步:给Swagger UI添加凭证输入模块
    你可以自定义Swagger UI的页面,添加用户名和密码输入框,用户提交后把凭证发送到后端的认证接口。如果是Spring Boot,可以通过修改swagger-ui.html模板(放在resources/static目录下)来实现,或者用前端代码在Swagger加载后注入自定义表单。

  • 第二步:后端验证凭证并生成动态Swagger配置
    认证接口验证用户名密码通过后,把用户的权限信息存入会话(Session),然后自定义一个接口来动态生成Swagger JSON。示例代码如下:

    @GetMapping("/v2/api-docs")
    public ResponseEntity<JsonNode> getDynamicApiDocs(HttpServletRequest request) {
        // 从会话获取已认证的用户信息
        User authenticatedUser = (User) request.getSession().getAttribute("authUser");
        if (authenticatedUser == null) {
            // 未认证时返回401或空文档
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
        }
    
        // 根据用户权限生成专属的Docket配置
        Docket userDocket = createDocketForAuthenticatedUser(authenticatedUser);
        // 把Docket转换为Swagger JSON格式
        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode swaggerJson = objectMapper.valueToTree(userDocket.getDocumentation());
    
        return ResponseEntity.ok(swaggerJson);
    }
    
    private Docket createDocketForAuthenticatedUser(User user) {
        Set<String> userPermissions = getUserPermissions(user.getId());
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(input -> {
                    // 过滤出用户有权限的接口
                    ApiOperation apiOp = input.getHandlerMethod().getMethodAnnotation(ApiOperation.class);
                    if (apiOp == null) return true;
                    String apiPermission = apiOp.extensions().stream()
                            .filter(ext -> "permission".equals(ext.getName()))
                            .map(ext -> ext.value().toString())
                            .findFirst().orElse(null);
                    return apiPermission == null || userPermissions.contains(apiPermission);
                })
                .paths(PathSelectors.any())
                .build();
    }
    
  • 第三步:Swagger UI动态加载认证后的文档
    在自定义的Swagger UI页面中,当用户认证成功后,调用Swagger UI的API重新加载文档。比如用JavaScript代码:

    // 认证成功后执行
    function loadDynamicSwaggerDocs() {
        const ui = SwaggerUIBundle({
            url: "/v2/api-docs", // 指向我们自定义的动态接口
            dom_id: '#swagger-ui',
            presets: [
                SwaggerUIBundle.presets.apis,
                SwaggerUIStandalonePreset
            ]
        });
        ui.initOAuth({});
    }
    

这样就能实现“用户输入凭证→后端验证→根据权限渲染对应Swagger接口”的流程了。

内容的提问来源于stack exchange,提问作者Sharad Ahire

火山引擎 最新活动