Swagger를 적용시키기 전에 알아둬야 할 것 바로 Swagger는 2개라는 것이다. springfox, springdocs 가 있는데 2020년부터 springfox 는 업데이트가 없었고, springboot 2.6.x 이상 버전이 호환이 안됩니다.
Spring-Doc는 최근에 나와서 업데이트도 진행되고 있고 Spring Boot 2.6 이상도 지원합니다.
그렇다고 docs이 더 좋다는 이야기는 아니구 많이 넘어가는 추세입니다.
사실 저는 springfox를 아무리 적용해봐도 security를 적용시키면 403, 404 에러가 해결되지 않아서 정말 안찾아본 자료가 없을정도로 다 해봤어요.
버전도 낮춰보고 Security 필터도 빼보고 모든 url을 permitAll() 을 해주기도 하고 진짜 다해봤는데 안돼서...
3일차.. 이게 뭐라고 3일이나 걸렸징..
암튼, Swagger는 개발할때 하는 문서화들을 자동화해주는 툴인데요. 백엔드 컨트롤러에 특정 어노테이션을 달아주면 ui형태로 명세서를 자동으로 만들어주는 참 편한 프로그램이라 자주 사용합니다.
그럼 적용시켜서 한번 써볼까요?
Security를 적용하면 기본적으로 Security가 url을 막는 기능이 있어서 적용을 시켜보겠습니다.
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtProvider jwtTokenProvider;
private final JwtAuthenticationFilter jwtAuthenticationFilter;
private final ObjectMapper objectMapper;
@Bean
public WebSecurityCustomizer configure(){
return (web) -> web.ignoring().mvcMatchers(
"v3/api-docs"
,"swagger-ui/**"
);
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http.antMatcher("/**")
.authorizeRequests()
.and()
.httpBasic().disable() //
.cors().disable() // cors 사용 중지
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 세션 사용 중지
.and()
.authorizeRequests()// 시큐리티 처리에 HttpServeltRequest를 사용합니다.
.anyRequest().permitAll()
.and()
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider),
UsernamePasswordAuthenticationFilter.class)
//JwtAuthenticationFilter를 UsernamePasswordAuthenticationFilter 전에 넣는다
.exceptionHandling() //Exception Handler
.authenticationEntryPoint(((request, response, authException) -> {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
objectMapper.writeValue(
response.getOutputStream(),
new BasicResponse("exception event",HttpStatus.FORBIDDEN)
);
})).and().build();
Spring Security를 설정해보려고 WebSecurityConfigurerAdapter를 사용하려 보니 Deprecated가 되어 있습니다.
Deprecated 된걸 그대로 쓰려니 당연히 permission이 막힐텐데 말이죠..
Deprecated. Use a SecurityFilterChain Bean to configure HttpSecurity or a WebSecurityCustomizer Bean to configure WebSecurity
하이구야..
그래서 기존 webmvcconfigureradapter
를 extends 쓰는 방법을 계속 쓰면?
404 에러가 발생하거나 permission denied 가 발생합니다.
SwaggerConfig에서 코드 설명을 하겠습니다.
WebSecurityConfigurerAdapter 공식문서를 보면 위와 같이 나와 있습니다.
그래서 코드를 어떻게 변경해서 써야 할까요?
SpringSecurity 공식문서을 보면 코드와 함께 나와있습니다.
기존 코드
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/ignore1", "/ignore2");
}
}
변경 코드 (WebSecurityCustomizer)
@Configuration
public class SecurityConfiguration {
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().antMatchers("/ignore1", "/ignore2");
}
}
기존 코드
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated()
)
.httpBasic(withDefaults());
}
}
변경 코드 ( WebSecurityFilterChain 등록해서 사용)
@Configuration
public class SecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated()
)
.httpBasic(withDefaults());
return http.build();
}
}
@EnableWebSecurity 어노테이션을 명시하는 것만으로도 springSecurityFilterChain가 자동으로 포함되어 지기 때문에
WebSecurityFilterChain을 Bean으로 등록해서 사용하면 됩니다.
@Bean
public GroupedOpenApi jwtApi() {
return GroupedOpenApi.builder()
.group("jwt-api")
.pathsToMatch("/**")
.build();
}
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.components(new Components())
.info(new Info().title("Spring Boot API Example")
.description("Spring Boot API 예시 프로젝트입니다.")
.version("v0.0.1"));
}
이런식으로 SwaggerConfig 를 선언해주시면 됩니다.
GroupedOpenApi 를 사용하면, 특정 그룹으로 선언된 기능들을 분리를 할 수 있습니다.
OpenAPI를 선언하면 기본 Swagger 설명들을 세팅할 수 있습니다. 자세한건 Document를 살펴봅시다~
이제 컨트롤러에 어노테이션을 붙여서 명세서 내용을 채워주면 끝입니다.
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
--
@Operation(summary = "logout", description = "로그아웃")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK"),
@ApiResponse(responseCode = "400", description = "BAD REQUEST"),
@ApiResponse(responseCode = "404", description = "NOT FOUND"),
@ApiResponse(responseCode = "500", description = "INTERNAL SERVER ERROR")
})
@GetMapping("/logout")
public ResponseEntity<BasicResponse> logout(@AuthUser Account account, HttpServletRequest request) {
~~~~~~~~~~~~~~~~~~
}
이렇게 작성하면
와! 이제 동작을 합니다 ㅠㅠㅠ deprecated는 오기부리지 말고 쓰지 마세요~