Java 记录Spring';s在Swagger中的登录/注销API
我正在使用Java 记录Spring';s在Swagger中的登录/注销API,java,spring-boot,swagger,swagger-ui,springfox,Java,Spring Boot,Swagger,Swagger Ui,Springfox,我正在使用springboot开发演示REST服务,其中用户必须登录才能执行某些操作子集。使用该简单配置添加Swagger UI(使用springfox库)后: @Bean public Docket docApi() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(any()) .paths(PathSelectors
springboot
开发演示REST服务,其中用户必须登录才能执行某些操作子集。使用该简单配置添加Swagger UI
(使用springfox
库)后:
@Bean
public Docket docApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(any())
.paths(PathSelectors.ant("/api/**"))
.build()
.pathMapping("/")
.apiInfo(apiInfo())
.directModelSubstitute(LocalDate.class, String.class)
.useDefaultResponseMessages(true)
.enableUrlTemplating(true);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginProcessingUrl("/api/login")
.usernameParameter("username")
.passwordParameter("password")
.successHandler(new CustomAuthenticationSuccessHandler())
.failureHandler(new CustomAuthenticationFailureHandler())
.permitAll()
.and()
.logout()
.logoutUrl("/api/logout")
.logoutSuccessHandler(new CustomLogoutSuccessHandler())
.deleteCookies("JSESSIONID")
.permitAll()
.and()
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(new CustomAuthenticationEntryPoint())
.and()
.authorizeRequests()
.and()
.headers()
.frameOptions()
.disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
我最终得到了所有API,所有操作都列在Swagger UI
页面上。不幸的是,其中没有列出登录/注销端点
问题是,部分操作无法通过内置表单执行(我发现这是一个非常好的功能,希望能够正常工作),因为用户未登录。那个问题有什么解决办法吗?我可以在Swagger
中手动定义一些端点吗
如果有提交凭据的表单(即登录/注销端点),我可以在使用该安全端点之前执行授权。然后,Swagger
用户可以从响应中提取令牌/sessionid
,并将其粘贴到通过@apimplicitparams
定义的自定义查询参数
您可以在下面找到我的安全配置:
@Bean
public Docket docApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(any())
.paths(PathSelectors.ant("/api/**"))
.build()
.pathMapping("/")
.apiInfo(apiInfo())
.directModelSubstitute(LocalDate.class, String.class)
.useDefaultResponseMessages(true)
.enableUrlTemplating(true);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginProcessingUrl("/api/login")
.usernameParameter("username")
.passwordParameter("password")
.successHandler(new CustomAuthenticationSuccessHandler())
.failureHandler(new CustomAuthenticationFailureHandler())
.permitAll()
.and()
.logout()
.logoutUrl("/api/logout")
.logoutSuccessHandler(new CustomLogoutSuccessHandler())
.deleteCookies("JSESSIONID")
.permitAll()
.and()
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(new CustomAuthenticationEntryPoint())
.and()
.authorizeRequests()
.and()
.headers()
.frameOptions()
.disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
派对有点晚了,但是由于SpringFox依赖Springbeans来构建文档,我们可以很容易地操纵它。希望这能帮助别人 将其注册为bean
@Primary
@Bean
public ApiListingScanner addExtraOperations(ApiDescriptionReader apiDescriptionReader, ApiModelReader apiModelReader, DocumentationPluginsManager pluginsManager)
{
return new FormLoginOperations(apiDescriptionReader, apiModelReader, pluginsManager);
}
用于手动添加任何操作的类:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import com.fasterxml.classmate.TypeResolver;
import com.google.common.collect.Multimap;
import springfox.documentation.builders.ApiListingBuilder;
import springfox.documentation.builders.OperationBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiDescription;
import springfox.documentation.service.ApiListing;
import springfox.documentation.service.Operation;
import springfox.documentation.spring.web.plugins.DocumentationPluginsManager;
import springfox.documentation.spring.web.readers.operation.CachingOperationNameGenerator;
import springfox.documentation.spring.web.scanners.ApiDescriptionReader;
import springfox.documentation.spring.web.scanners.ApiListingScanner;
import springfox.documentation.spring.web.scanners.ApiListingScanningContext;
import springfox.documentation.spring.web.scanners.ApiModelReader;
public class FormLoginOperations extends ApiListingScanner
{
@Autowired
private TypeResolver typeResolver;
@Autowired
public FormLoginOperations(ApiDescriptionReader apiDescriptionReader, ApiModelReader apiModelReader, DocumentationPluginsManager pluginsManager)
{
super(apiDescriptionReader, apiModelReader, pluginsManager);
}
@Override
public Multimap<String, ApiListing> scan(ApiListingScanningContext context)
{
final Multimap<String, ApiListing> def = super.scan(context);
final List<ApiDescription> apis = new LinkedList<>();
final List<Operation> operations = new ArrayList<>();
operations.add(new OperationBuilder(new CachingOperationNameGenerator())
.method(HttpMethod.POST)
.uniqueId("login")
.parameters(Arrays.asList(new ParameterBuilder()
.name("username")
.description("The username")
.parameterType("query")
.type(typeResolver.resolve(String.class))
.modelRef(new ModelRef("string"))
.build(),
new ParameterBuilder()
.name("password")
.description("The password")
.parameterType("query")
.type(typeResolver.resolve(String.class))
.modelRef(new ModelRef("string"))
.build()))
.summary("Log in") //
.notes("Here you can log in")
.build());
apis.add(new ApiDescription("/api/login/", "Authentication documentation", operations, false));
def.put("authentication", new ApiListingBuilder(context.getDocumentationContext().getApiDescriptionOrdering())
.apis(apis)
.description("Custom authentication")
.build());
return def;
}
}
只是加上一点修正。 如果您想发出真正的POST请求(例如,通过swagger ui的HTML页面),您需要对Morten的答案进行一些更改 的代码发出POST请求/登录,如下所示:
@Override
public Multimap<String, ApiListing> scan(ApiListingScanningContext context)
{
final Multimap<String, ApiListing> def = super.scan(context);
final List<ApiDescription> apis = new LinkedList<>();
final List<Operation> operations = new ArrayList<>();
operations.add(new OperationBuilder(new CachingOperationNameGenerator())
.method(HttpMethod.POST)
.uniqueId("login")
.parameters(Arrays.asList(new ParameterBuilder()
.name("body")
.required(true)
.description("The body of request")
.parameterType("body")
.type(typeResolver.resolve(String.class))
.modelRef(new ModelRef("string"))
.build()))
.summary("Log in") //
.notes("Here you can log in")
.build());
apis.add(new ApiDescription("/api/login/", "Authentication documentation", operations, false));
def.put("authentication", new ApiListingBuilder(context.getDocumentationContext().getApiDescriptionOrdering())
.apis(apis)
.description("Custom authentication")
.build());
return def;
}
http:///api/login?username=&password=
但是,如果要发出POST请求,则需要传递一个正文,而不仅仅是查询参数。
要实现这一点,您需要添加名为body
的参数和类型为body
的参数,如下所示:
@Override
public Multimap<String, ApiListing> scan(ApiListingScanningContext context)
{
final Multimap<String, ApiListing> def = super.scan(context);
final List<ApiDescription> apis = new LinkedList<>();
final List<Operation> operations = new ArrayList<>();
operations.add(new OperationBuilder(new CachingOperationNameGenerator())
.method(HttpMethod.POST)
.uniqueId("login")
.parameters(Arrays.asList(new ParameterBuilder()
.name("body")
.required(true)
.description("The body of request")
.parameterType("body")
.type(typeResolver.resolve(String.class))
.modelRef(new ModelRef("string"))
.build()))
.summary("Log in") //
.notes("Here you can log in")
.build());
apis.add(new ApiDescription("/api/login/", "Authentication documentation", operations, false));
def.put("authentication", new ApiListingBuilder(context.getDocumentationContext().getApiDescriptionOrdering())
.apis(apis)
.description("Custom authentication")
.build());
return def;
}
@覆盖
公共多映射扫描(ApiListingScanningContext)
{
最终多重映射定义=super.scan(上下文);
最终列表API=新建LinkedList();
最终列表操作=新建ArrayList();
添加(新的OperationBuilder(新的CachingOperationNameGenerator())
.方法(HttpMethod.POST)
.uniqueId(“登录”)
.parameters(Arrays.asList)(新参数生成器()
.名称(“机构”)
。必需(正确)
.说明(“请求正文”)
.参数类型(“主体”)
.type(typeResolver.resolve(String.class))
.modelRef(新modelRef(“字符串”))
.build())
.摘要(“登录”)//
.notes(“您可以在此登录”)
.build());
add(新的apisdescription(“/api/login/”,“身份验证文档”,operations,false));
def.put(“身份验证”,新的ApiListingBuilder(context.getDocumentationContext().getApisDescriptionOrdering())
.API(API)
.说明(“自定义身份验证”)
.build());
返回def;
}
现在我们可以通过一个机构与我们的职位要求。主体可以是JSON,例如:
{“username”:“admin”,“password”:“admin”}
您可以在API中添加一个虚假的登录和注销方法来生成Swagger文档,它将被Spring安全过滤器自动覆盖
@ApiOperation("Login.")
@PostMapping("/login")
public void fakeLogin(@ApiParam("User") @RequestParam String email, @ApiParam("Password") @RequestParam String password) {
throw new IllegalStateException("This method shouldn't be called. It's implemented by Spring Security filters.");
}
@ApiOperation("Logout.")
@PostMapping("/logout")
public void fakeLogout() {
throw new IllegalStateException("This method shouldn't be called. It's implemented by Spring Security filters.");
}
您可以使用描述身份验证API的接口。acutal实现由Spring Security提供。(这是的一个变体,其中使用接口而不是伪实现。)
非常感谢你的比赛!祝福你!