如何基于Spring-Security注解生成Swagger接口授权信息并复用注解?
Awesome question! Dealing with duplicate authorization config between Spring Security and Swagger is a common pain point—let's fix this properly. The solution below addresses both your questions: auto-generating per-endpoint Swagger authorization info and reusing your existing Spring Security annotations to avoid redundant work.
Instead of manually duplicating role info in both Spring Security and Swagger annotations, we'll build a custom Springfox plugin that automatically reads authorization rules from @Secured, @PreAuthorize, or @RolesAllowed annotations and injects them into your Swagger documentation.
Step 1: Create a Custom Springfox Plugin
This plugin hooks into Springfox's operation-building pipeline to extract authorization data from your Spring Security annotations:
import org.springframework.security.access.annotation.Secured; import org.springframework.security.access.prepost.PreAuthorize; import springfox.documentation.builders.AuthorizationScopeBuilder; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.OperationBuilderPlugin; import springfox.documentation.spi.service.contexts.OperationContext; import springfox.documentation.service.AuthorizationScope; import springfox.documentation.service.SecurityReference; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; public class SpringSecuritySwaggerPlugin implements OperationBuilderPlugin { @Override public void apply(OperationContext context) { List<String> authorizedRoles = new ArrayList<>(); // Extract roles from @Secured annotation Optional<Secured> securedAnnotation = context.findAnnotation(Secured.class); securedAnnotation.ifPresent(secured -> authorizedRoles.addAll(Arrays.asList(secured.value()))); // Extract roles/permissions from @PreAuthorize expression Optional<PreAuthorize> preAuthAnnotation = context.findAnnotation(PreAuthorize.class); preAuthAnnotation.ifPresent(preAuth -> { // Parse common cases like hasRole('ROLE_ADMIN') or hasAnyRole('ROLE_USER','ROLE_EDITOR') String expression = preAuth.value(); List<String> parsedRoles = Arrays.stream(expression.split("'")) .filter(s -> s.startsWith("ROLE_") || s.startsWith("PERMISSION_")) .collect(Collectors.toList()); authorizedRoles.addAll(parsedRoles); }); // Attach authorization info to Swagger operation if roles exist if (!authorizedRoles.isEmpty()) { List<AuthorizationScope> scopes = authorizedRoles.stream() .map(role -> new AuthorizationScopeBuilder() .scope(role) .description("Required role/permission: " + role) .build()) .collect(Collectors.toList()); SecurityReference securityRef = SecurityReference.builder() .reference("BearerAuth") // Match your Swagger security scheme name .scopes(scopes.toArray(new AuthorizationScope[0])) .build(); context.operationBuilder().security(List.of(securityRef)); } } @Override public boolean supports(DocumentationType documentationType) { return true; } }
Step 2: Register the Plugin in Your Swagger Config
Update your SwaggerConfig to register the plugin and configure your security scheme (e.g., Bearer token authentication):
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiKey; import springfox.documentation.service.SecurityScheme; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; import java.util.List; @Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.basePackage("com.your.app.package")) .paths(PathSelectors.any()) .build() .securitySchemes(List.of(apiKey())) .securityContexts(List.of(securityContext())) .plugins(new SpringSecuritySwaggerPlugin()); // Register our custom plugin } // Define your security scheme (e.g., Bearer token) private SecurityScheme apiKey() { return new ApiKey("BearerAuth", "Authorization", "header"); } private SecurityContext securityContext() { return SecurityContext.builder() .securityReferences(defaultAuth()) .forPaths(PathSelectors.any()) .build(); } private List<SecurityReference> defaultAuth() { // Leave empty—our plugin will add per-endpoint security refs dynamically return List.of(); } }
Step 3: Use Spring Security Annotations Normally
Now you can annotate your controllers with Spring Security annotations as usual—Swagger will automatically pick up the authorization rules:
@RestController @RequestMapping("/api/models") public class ModelController { @GetMapping @Secured("ROLE_USER") public List<Model> getAllModels() { // Your logic here } @PostMapping @PreAuthorize("hasRole('ROLE_ADMIN') or hasAuthority('PERMISSION_CREATE_MODEL')") public Model createModel(@RequestBody Model model) { // Your logic here } }
How This Solves Your Problems
- Per-endpoint authorization info: Each API's Swagger entry will show the exact roles/permissions required, pulled directly from your Spring Security annotations.
- No duplicate maintenance: You only need to update your Spring Security annotations—Swagger stays in sync automatically, eliminating the risk of mismatched configs.
Notes for Complex Expressions
If you use advanced @PreAuthorize expressions (e.g., hasAuthority('EDIT') and authentication.name == #model.owner), you can extend the parsing logic in the plugin. For example, add regex patterns to extract custom permissions, or integrate a SpEL parser for full expression evaluation.
内容的提问来源于stack exchange,提问作者Mahatma_Fatal_Error

