Swagger에서 Spring의 로그인/로그아웃 API 문서화
저는 다음을 이용하여 데모 REST 서비스를 개발하고 있습니다.Spring Boot
여기서 사용자는 특정 작업의 하위 집합을 수행하기 위해 로그인해야 합니다.추가 후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);
}
나는 모든 작업이 나열된 모든 아피스와 함께 끝납니다.Swagger UI
페이지. 유감스럽게도 로그인/로그아웃 엔드포인트가 목록에 없습니다.
문제는 해당 작업의 일부가 다음을 통해 수행될 수 없다는 것입니다.Swagger UI
사용자가 로그인하지 않았기 때문에 내장된 양식(나는 그것이 정말 좋은 기능이라고 생각하고 그것을 작동시키고 싶다).그 문제에 대한 해결책이 있습니까?에서 일부 엔드포인트를 수동으로 정의할 수 있습니까?Swagger
?
자격 증명을 제출할 양식(예: 로그인/로그아웃 엔드포인트)이 있으면 해당 보안 엔드포인트를 사용하기 전에 권한 부여를 수행할 수 있습니다.그리고나서,Swagger
사용자가 추출할 수 있음token/sessionid
응답에서 다음을 통해 정의된 사용자 지정 쿼리 매개 변수에 붙여넣습니다.@ApiImplicitParams
.
아래에서 내 보안 구성을 찾을 수 있습니다.
@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());
}
파티에 약간 늦었지만, 스프링 폭스가 문서를 만드는 데 스프링 콩에 의존하기 때문에, 우리는 그것을 쉽게 조작할 수 있습니다.이것이 누군가를 도울 수 있기를 바랍니다!
콩으로 등록합니다.
@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;
}
}
Swaggerjson 렌더링 중:
"/api/login/" : {
"post" : {
"summary" : "Log in",
"description" : "Here you can log in",
"operationId" : "loginUsingPOST",
"parameters" : [ {
"name" : "username",
"in" : "query",
"description" : "The username",
"required" : false,
"type" : "string"
}, {
"name" : "password",
"in" : "query",
"description" : "The password",
"required" : false,
"type" : "string"
} ]
}
}
가짜 로그인 및 로그아웃 방법을 API에 추가하여 Swagger 문서를 생성하면 Spring Security 필터에 의해 자동으로 재정의됩니다.
@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.");
}
약간의 수정만 더하면 됩니다.(예를 들어, swagger-ui의 HTML 페이지를 통해) 실제 POST 요청을 하려면 Morten의 응답을 약간 변경해야 합니다.
Morten의 코드는 /login에 대한 POST 요청을 다음과 같이 수행합니다.
http://<hostname>/api/login?username=<user>&password=<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;
}
이제 우리는 우리의 POST 요청으로 시체를 통과시킬 수 있습니다.본문은 JSON일 수 있습니다. 예:
{"username":"admin","password":"admin"}
인증 API를 설명하는 인터페이스를 사용할 수 있습니다.실제 구현은 Spring Security에서 제공합니다.(이는 Italo의 답변을 변형한 것으로, 가짜 구현 대신 인터페이스를 사용합니다.)
/**
* Authentication API specification for Swagger documentation and Code Generation.
* Implemented by Spring Security.
*/
@Api("Authentication")
@RequestMapping(value = "/", produces = MediaType.APPLICATION_JSON_VALUE)
public interface AuthApi {
/**
* Implemented by Spring Security
*/
@ApiOperation(value = "Login", notes = "Login with the given credentials.")
@ApiResponses({@ApiResponse(code = 200, message = "", response = Authentication.class)})
@RequestMapping(value = "/login", method = RequestMethod.POST)
default void login(
@RequestParam("username") String username,
@RequestParam("password") String password
) {
throw new IllegalStateException("Add Spring Security to handle authentication");
}
/**
* Implemented by Spring Security
*/
@ApiOperation(value = "Logout", notes = "Logout the current user.")
@ApiResponses({@ApiResponse(code = 200, message = "")})
@RequestMapping(value = "/logout", method = RequestMethod.POST)
default void logout() {
throw new IllegalStateException("Add Spring Security to handle authentication");
}
}
springdoc-openapi에 대한 나의 솔루션:
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.components(new Components())
.tags(List.of(
new Tag()
.name("Authentication")
.description("Login/logout controller")
))
.path("/logout", new PathItem()
.post(new Operation()
.tags(List.of(
"Authentication"
))
.summary("Logout")
.description("Logout the current user.")
.operationId("logout")
.responses(new ApiResponses()
.addApiResponse("200", new ApiResponse().description("OK"))
)
)
)
.path("/login", new PathItem()
.post(new Operation()
.tags(List.of(
"Authentication"
))
.summary("Login")
.description("Login with the given credentials.")
.operationId("login")
.parameters(List.of(
new Parameter().name("username").in("query").required(true).schema(new Schema().type("string")),
new Parameter().name("password").in("query").required(true).schema(new Schema().type("string"))
))
.responses(new ApiResponses()
.addApiResponse("200", new ApiResponse().description("OK"))
)
)
);
}
언급URL : https://stackoverflow.com/questions/34386337/documenting-springs-login-logout-api-in-swagger
'programing' 카테고리의 다른 글
모델 가져오기 시 1을 true로 또는 0을 false로 변환하는 방법 (0) | 2023.07.31 |
---|---|
iOS - 지연 후 여러 인수와 함께 performSelector를 구현하는 방법은 무엇입니까? (0) | 2023.07.31 |
Base64로 CSS에 배경 이미지 데이터를 포함하는 것이 좋은 관행입니까, 나쁜 관행입니까? (0) | 2023.07.31 |
How to download a file from AJAX request in Liferay serveResource(-, -) method (0) | 2023.07.31 |
Python에는 패키지/모듈 관리 시스템이 있습니까? (0) | 2023.07.31 |