Chapter#01 : [Spring] IntelliJ를 사용한 Spring Project 생성( Gradle )
Chapter#02 : [Spring] MVC 패턴 및 MyBatis를 사용한 게시판 제작
Chapter#03 : [Spring] Log4j 설정 및 사용하기( log 파일 저장하기 )
Chapter#04 : [Spring] SpringSecurity를 이용한 사용자 인증 프로세스 구축
Chapter#05 : [Spring] JWT 토큰 발급 및 토큰 인증 받기
Chapter#06 : [Spring] Swagger 웹 서비스 RESTful API 문서 자동 생성
※ 해당 포스팅은 [Spring] JWT 토큰 발급 및 토큰 인증 받기 포스팅의 내용에 이어 진행됩니다.
1. Swagger, Swagger-UI 라이브러리 추가
build.gradle을 열고 Dependencies에 Swagger, Swagger-UI 라이브러리를 추가하여 준다.
build.gradle
~~ 이 하 생 략 ~~
dependencies {
~~ 이 하 생 략 ~~
// Swagger RESTFull API
implementation "io.springfox:springfox-core:2.9.2"
implementation "io.springfox:springfox-schema:2.9.2"
implementation "io.springfox:springfox-spi:2.9.2"
implementation "io.springfox:springfox-spring-web:2.9.2"
implementation "io.springfox:springfox-swagger2:2.9.2"
implementation "io.springfox:springfox-swagger-common:2.9.2"
implementation "io.springfox:springfox-swagger-ui:2.9.2"
implementation "io.swagger:swagger-annotations:1.5.20"
implementation "io.swagger:swagger-models:1.5.20"
~~ 이 하 생 략 ~~
}
~~ 이 하 생 략 ~~
※ 해당 포스팅은 Swagger 2.9.2 버전을 사용하여 진행됩니다.
plugins {
id "java"
id "war"
}
apply plugin : "war"
group "org.example"
version "0.0.1-SNAPSHOT"
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
testImplementation "org.junit.jupiter:junit-jupiter-api:5.8.1"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.8.1"
// Servelt API
providedCompile "javax.servlet:servlet-api:2.5"
// JSTL
implementation "javax.servlet:jstl:1.2"
// SpringFramework
implementation "org.springframework:spring-aop:5.2.22.RELEASE"
implementation "org.springframework:spring-beans:5.2.22.RELEASE"
implementation "org.springframework:spring-context:5.2.22.RELEASE"
implementation "org.springframework:spring-core:5.2.22.RELEASE"
implementation "org.springframework:spring-expression:5.2.22.RELEASE"
implementation "org.springframework:spring-jcl:5.2.22.RELEASE"
implementation "org.springframework:spring-web:5.2.22.RELEASE"
implementation "org.springframework:spring-webmvc:5.2.22.RELEASE"
implementation "org.springframework:spring-jdbc:5.2.22.RELEASE"
// Spring Security
implementation "org.springframework.security:spring-security-acl:5.5.0"
implementation "org.springframework.security:spring-security-config:5.5.0"
implementation "org.springframework.security:spring-security-core:5.5.0"
implementation "org.springframework.security:spring-security-crypto:5.5.0"
implementation "org.springframework.security:spring-security-taglibs:5.5.0"
implementation "org.springframework.security:spring-security-web:5.5.0"
// Swagger RESTFull API
implementation "io.springfox:springfox-core:2.9.2"
implementation "io.springfox:springfox-schema:2.9.2"
implementation "io.springfox:springfox-spi:2.9.2"
implementation "io.springfox:springfox-spring-web:2.9.2"
implementation "io.springfox:springfox-swagger2:2.9.2"
implementation "io.springfox:springfox-swagger-common:2.9.2"
implementation "io.springfox:springfox-swagger-ui:2.9.2"
implementation "io.swagger:swagger-annotations:1.5.20"
implementation "io.swagger:swagger-models:1.5.20"
// JWT( JsonWebToken )
implementation "io.jsonwebtoken:jjwt:0.9.1"
// log4j
implementation "org.apache.logging.log4j:log4j-api:2.14.1"
implementation "org.apache.logging.log4j:log4j-core:2.14.1"
implementation "org.apache.logging.log4j:log4j-slf4j-impl:2.14.1"
// slf4j
implementation "org.slf4j:slf4j-api:1.7.25"
// MariaDB Connect
implementation "org.mariadb.jdbc:mariadb-java-client:2.5.4"
// MyBatis
implementation "org.mybatis:mybatis:3.5.8"
implementation "org.mybatis:mybatis-spring:2.0.6"
// Jackson
implementation "com.fasterxml.jackson.core:jackson-core:2.11.4"
implementation "com.fasterxml.jackson.core:jackson-annotations:2.11.4"
implementation "com.fasterxml.jackson.core:jackson-databind:2.11.4"
}
test {
useJUnitPlatform()
}
2. Spring Application과 Swagger 통합 설정
1) Swagger UI 리소스 맵핑 설정 하는 XML 파일
Spring 어플리케이션 내에서 Swagger 문서화와 UI 를 설정하기 위한 빈( Bean )과 리소스( Resource )를 정의하는
swagger-servlet.xml 파일을 src/main/webapp/WEB-INF 디렉토리에 생성한다.
swagger-servlet.xml 파일이 생성되면 Swagerr UI 리소스를 맵핑하는 코드들 추가하여 준다.
swagger-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 스웨거 설정 -->
<bean id="swagger2Config" class="springfox.documentation.swagger2.configuration.Swagger2DocumentationConfiguration"></bean>
<!-- 스웨거 UI 리소스 매핑 -->
<mvc:resources location="classpath:/META-INF/resources/" mapping="swagger-ui.html"></mvc:resources>
<mvc:resources location="classpath:/META-INF/resources/webjars/" mapping="/webjars/**"></mvc:resources>
</beans>
2) Swagger UI 설정 서블릿( SEVLET ) 맵핑하기
web.xml 파일을 열고 Spring 어플리케이션에서 Swagger를 설정하여 준다.
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
~~ 이 하 생 략 ~~
<servlet>
<servlet-name>swagger</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/swagger-servlet.xml</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>swagger</servlet-name>
<url-pattern>/swagger-ui.html</url-pattern>
<url-pattern>/webjars/**</url-pattern>
<url-pattern>/</url-pattern>
</servlet-mapping>
~~ 이 하 생 략 ~~
</web-app>
이 설정은 서블릿을 통해 Swagger UI 관련된 요청을 처리하는 방식을 정의하고 있다.
"/swagger-ui.html" 및 "/webjjars/**" 경로로 요청이 들어올 때, 설정한 서블릿이 동작하여
Swagger UI 관련 리소스 및 페이지를 제공하게 된다.
또한 모든 경로(" / ")로 접근할 때도 서블릿이 호출되어 기본적인 처리를 수행 할 수 있게 된다.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- 보안 절차를 위해서는 모든 요청이 spring security 필터를 거치도록 설정 -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:context/context-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
<!-- url-pattern>/</url-pattern -->
</servlet-mapping>
<servlet>
<servlet-name>swagger</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/swagger-servlet.xml</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>swagger</servlet-name>
<url-pattern>/swagger-ui.html</url-pattern>
<url-pattern>/webjars/**</url-pattern>
<url-pattern>/</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<filter>
<filter-name>characterEncoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncoding</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
</web-app>
3) Spring Security에서 Swagger UI 사용 설정
스프링 시큐리티 설정을 담당하는 context-security.xml 파일을 열고 Swagger UI 사용을 위하여
아래 4개의 파일 및 경로( Swagger UI 접근 )에 대하여 access="permitAll( )" 설정을 추가하여 준다.
context-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- permitAll() : 인증여부와 상관없이 모든 사용자가 접근 -->
<security:http auto-config="false">
<!-- CSRF 설정 -->
<security:csrf disabled="true"></security:csrf>
<!-- API 로그인 설정 -->
<security:form-login login-processing-url="/access/authenticationProcess.do" username-parameter="memberId" password-parameter="memberPw" authentication-success-handler-ref="authenticationSuccessHandler" authentication-failure-handler-ref="authenticationFailureHandler"></security:form-login>
<!-- API 로그아웃 설정 -->
<security:logout logout-url="/access/logOut.do" invalidate-session="true" delete-cookies="JSESSIONID" success-handler-ref="jwtLogoutSuccessHandler"></security:logout>
<!-- Swagger UI 접근 허용 설정 -->
<security:intercept-url pattern="/swagger-ui.html" access="permitAll()"></security:intercept-url>
<security:intercept-url pattern="/swagger-resources/**" access="permitAll()"></security:intercept-url>
<security:intercept-url pattern="/v2/api-docs" access="permitAll()"></security:intercept-url>
<security:intercept-url pattern="/webjars/**" access="permitAll()"></security:intercept-url>
<!-- 토큰 유효성 검사를 수행할 URL 패턴 설정 -->
<security:intercept-url pattern="/swagger/**" access="isAuthenticated()"></security:intercept-url>
<!-- JwtTokenFilter를 추가하여 토큰 유효성 검사 수행 -->
<security:custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="jwtTokenProvider"></security:custom-filter>
</security:http>
~~ 이 하 생 략 ~~
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- permitAll() : 인증여부와 상관없이 모든 사용자가 접근 -->
<security:http auto-config="false">
<!-- CSRF 설정 -->
<security:csrf disabled="true"></security:csrf>
<!-- 로그인 페이지와 로그인 처리 URL 설정 -->
<!--security:form-login login-page="/login.do" login-processing-url="/authenticationProcess.do" username-parameter="memberId" password-parameter="memberPw" authentication-success-handler-ref="authenticationSuccessHandler" authentication-failure-handler-ref="authenticationFailureHandler"></security:form-login -->
<!-- 로그인 페이지는 접근권한 permitAll() 설정 -->
<!-- security:intercept-url pattern="/login.do" access="permitAll()"></security:intercept-url -->
<!-- 회원가입 페이지는 접근권한 permitAll() 설정 -->
<!-- security:intercept-url pattern="/singUp.do" access="permitAll()"></security:intercept-url -->
<!-- 회원가입시 회원 신청 ID 중복 확인 프로세스 접근권한 permitAll() 설정 -->
<!-- security:intercept-url pattern="/duplicateId.do" access="permitAll()"></security:intercept-url -->
<!-- 회원가입 로직처리 프로세스 접근권한 permitAll() 설정 -->
<!-- security:intercept-url pattern="/memberRegistry.do" access="permitAll()"></security:intercept-url -->
<!-- 로그인 인증된 사용자에 한해 isAuthenticated() 설정 -->
<!-- security:intercept-url pattern="/**" access="hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')"></security:intercept-url -->
<!-- 로그아웃 설정 -->
<!-- security:logout logout-url="/logout.do" logout-success-url="/login.do?logout" invalidate-session="true" delete-cookies="JSESSIONID"></security:logout -->
<!-- 동시 세션 제어 설정 -->
<!-- security:session-management>
<security:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" expired-url="/member/login.do?expired=true"></security:concurrency-control>
</security:session-management -->
<!-- API 로그인 설정 -->
<security:form-login login-processing-url="/access/authenticationProcess.do" username-parameter="memberId" password-parameter="memberPw" authentication-success-handler-ref="authenticationSuccessHandler" authentication-failure-handler-ref="authenticationFailureHandler"></security:form-login>
<!-- API 로그아웃 설정 -->
<security:logout logout-url="/access/logOut.do" invalidate-session="true" delete-cookies="JSESSIONID" success-handler-ref="jwtLogoutSuccessHandler"></security:logout>
<!-- Swagger UI 접근 허용 설정 -->
<security:intercept-url pattern="/swagger-ui.html" access="permitAll()"></security:intercept-url>
<security:intercept-url pattern="/swagger-resources/**" access="permitAll()"></security:intercept-url>
<security:intercept-url pattern="/v2/api-docs" access="permitAll()"></security:intercept-url>
<security:intercept-url pattern="/webjars/**" access="permitAll()"></security:intercept-url>
<!-- 토큰 유효성 검사를 수행할 URL 패턴 설정 -->
<security:intercept-url pattern="/swagger/**" access="isAuthenticated()"></security:intercept-url>
<!-- JwtTokenFilter를 추가하여 토큰 유효성 검사 수행 -->
<security:custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="jwtTokenProvider"></security:custom-filter>
</security:http>
<!-- Spring Security의 사용자 인증을 수행 -->
<security:authentication-manager id="authenticationManager">
<security:authentication-provider user-service-ref="securityUserDetailsService">
<security:password-encoder ref="passwordEncoder"></security:password-encoder>
</security:authentication-provider>
</security:authentication-manager>
<bean id="securityUserDetailsService" class="org.example.security.SecurityUserDetailsService"></bean>
<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>
<!-- jwtConfig bean -->
<bean id="jwtConfig" class="org.example.security.JwtConfig"></bean>
<!-- JWT 토큰 생성 및 유효성 검사 bean -->
<bean id="jwtTokenProvider" class="org.example.security.JwtTokenProvider">
<constructor-arg ref="jwtConfig"></constructor-arg>
</bean>
<!-- 인증 성공 bean -->
<bean id="authenticationSuccessHandler" class="org.example.security.SecurityAuthenticationSuccessHandler">
<constructor-arg ref="jwtTokenProvider"></constructor-arg>
</bean>
<!-- 인증 실패 bean -->
<bean id="authenticationFailureHandler" class="org.example.security.SecurityAuthenticationFailureHandler"></bean>
</beans>
4) Swagger 사용에 대한 빈( Bean ) 등록 or 컴포넌트 스캔 설정 변경
Swagger UI 사용을 위해 이전에 컴포넌트 스캔을 통해 빈( Bean )을 등록하고 컨트롤러( Controller ) 파일에 대해서만
스캔을 적용하지 않았던 context-common.xml 파일에 대한 설정을 변경한다.
context-common.xml을 수정하여 사용하는 방법은 2가지 방법이 존재한다.
원하는 방법을 선택하여 설정을 변경하여 사용을 하면 된다.
방법#01 : Swagger 사용 Controller에 대하여 Bean 등록
context-common.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 스웨거 빈 등록 -->
<bean id="swaggerConfig" class="org.example.config.SwaggerConfig"></bean>
<bean id="AccessController" class="org.example.api.controller.AccessController"></bean>
<bean id="swaggerController" class="org.example.api.controller.SwaggerController"></bean>
<!-- @Service, @Repository만 include -->
<context:component-scan base-package="org.example.*.*" use-default-filters="true">
<!-- Controller는 자동 스캔에 포함시키지 않는다. -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>
</context:component-scan>
</beans>
방법#02 - 컴포넌트 스캔에서 Controller또한 스캔하도록 설정 변경
context-common.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 스웨거 빈 등록 -->
<bean id="swaggerConfig" class="org.example.config.SwaggerConfig"></bean>
<!-- @Service, @Repository만 include -->
<!-- Controller는 자동 스캔에 포함시키지 않는다. -->
<!-- context:component-scan base-package="org.example.*.*" use-default-filters="true">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>
</context:component-scan -->
<!-- @Service, @Repository, @Controller 모두 스캔 -->
<context:component-scan base-package="org.example.*.*" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"></context:include-filter>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"></context:include-filter>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:include-filter>
</context:component-scan>
</beans>
4. Swagger API 문서 ( 시각화 도구 ) 생성
org.example 패키지 경로에 config 패키지를 추가하여준다.
config 패키지가 생성되면 org.example.config 패지키 내부에 SwaggerConfig.java 클래스 파일을 생성하여 준다.
SwaggerConfig 클래스 파일이 생성되면 Swagger를 사용하여 API 문서화와 관련된 설정을 추가하여 준다.
SwaggerConfig.java
package org.example.config;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Collections;
import java.util.List;
@Configuration // 이 클래스가 Spring의 설정 클래스임을 나타낸다.
@EnableSwagger2 // Swagger2를 활성화 하는 어노테이션이다.
@EnableWebMvc // Spring MVC를 활성화 하는 어노테이션이다.
public class SwaggerConfig {
// API 문서 정보를 생성하는 메서드이다.
// ( API문서의 제목, 설명, 라이선스 등을 설정한다. )
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("SpringFramework DeviceAPI 연계 서비스( Hybrid App )")
.description("스프링 프레임워크 하이브리드앱 실행 환경 - iOS / Android 하이브리드앱 RestAPI 서비스")
.termsOfServiceUrl("https://saakmiso.tistory.com/106")
.contact(new Contact("사악미소", "https://saakmiso.tistory.com/", "wicked@saakmiso.com"))
.license("Apache License Version 2.0")
.licenseUrl("https://www.apache.org/licenses/LICENSE-2.0")
.version("0.0.1")
.build();
}
// 전체 Swagger API 문서 페이지에 대하여 문서화 한다.
@Bean
public Docket apiAllService() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("00. 전체 API REST Service")
.apiInfo(apiInfo())
.select()
.paths(PathSelectors.any())
.build();
}
// 사용자 접속에 대한 Docket 빈을 생성
@Bean
public Docket accessService() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("01. 사용자 접속 RestAPI 서비스")
.apiInfo(apiInfo())
.useDefaultResponseMessages(false) // false로 설정하면
.host("localhost:8181") // 기본 URL을 명시적으로 지정합니다.
.select()
.apis(RequestHandlerSelectors.basePackage("org.example.api.controller")) // API 패키지 이름의 최상위 패키지를 입력하세요
.paths(PathSelectors.ant("/access/**"))
.build();
}
// 사용자 접속에 대한 Docket 빈을 생성
// ( JWT 토큰 인증을 필요로 한다. )
@Bean
public Docket swaggerService() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("02. Swagger RestAPI 서비스 샘플")
.apiInfo(apiInfo())
.useDefaultResponseMessages(false) // false로 설정하면
.host("localhost:8181") // 기본 URL을 명시적으로 지정합니다.
.select()
.apis(RequestHandlerSelectors.basePackage("org.example.api.controller")) // API 패키지 이름의 최상위 패키지를 입력하세요
.paths(PathSelectors.ant("/swagger/**"))
.build()
.securityContexts(Collections.singletonList(securityContext()))
.securitySchemes(Collections.singletonList(apiKey()));
}
// API 보안에 사용되는 ApiKey 객체를 생성한다.
private ApiKey apiKey() {
// "Bearer" 토큰을 헤더의 "Authorization" 필드로 사용한다.
return new ApiKey("Bearer", "Authorization", "header");
}
// 보안 컨텍스트를 생성합니다. 이 컨텍스트는 보안 관련 설정을 담고 있습니다.
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.build();
}
// 기본 보안 설정을 생성합니다.( Bearer 토큰을 사용 )
private List<SecurityReference> defaultAuth() {
// global" 범위의 권한으로 "accessEverything"을 지정한다.
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[]{authorizationScope};
return Collections.singletonList(new SecurityReference("Bearer", authorizationScopes));
}
}
SwaggerConfig 클래스 파일은 Spring 어플리케이션에 Swagger를 통합하여 API 문서화를 설정하는 클래스이다.
주어진 메서드 그룹에 따라 Swagger UI를 이용한 문서화를 진행하고
API 보안 설정을 추가하여 안전하게 Swagger API 문서를 노출할 수 있게 해준다.
5. Swagger API - 문서화 Controller 제작
1) 사용자 접속 정보 Controller - API 문서화
이전 포스팅에서 사용자 인증을 통해 JWT를 발급 받고 JWT 토큰 사용 유무를 인증하는 프로세스를 수행했던
org.example.api.controller 패키지에 생성한 AccessController.java 컨트롤러를 수정한다.
AccessController.java
package org.example.api.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiImplicitParam;
import org.example.member.service.MemberService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import springfox.documentation.annotations.ApiIgnore;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/access")
@Api(tags = "01. 사용자 접속 RestAPI 서비스")
public class AccessController {
@Resource(name="memberService")
private MemberService memberService;
@Autowired
@Qualifier("authenticationManager")
private AuthenticationManager authenticationManager;
@RequestMapping(value="/authenticationProcess.do", method=RequestMethod.POST)
@ApiOperation(value="회원 인증 처리", notes="회원 인증을 수행합니다.")
@ApiImplicitParams({
@ApiImplicitParam(name="memberId", value="회원 아이디", required=true, dataTypeClass=String.class, paramType="query"),
@ApiImplicitParam(name="memberPw", value="회원 비밀번호", required=true, dataTypeClass=String.class, paramType="query")
})
public void authenticate(@RequestParam String memberId, @RequestParam String memberPw, @ApiIgnore RedirectAttributes redirectAttributes) {
try {
// 사용자 인증을 위한 UsernamePasswordAuthenticationToken 객체 생성
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(memberId, memberPw);
// AuthenticationManager를 사용하여 인증 수행
Authentication authentication = authenticationManager.authenticate(token);
// 인증 성공 후 SecurityContext에 인증 객체 설정
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (Exception e) {
redirectAttributes.addAttribute("error", true);
}
}
@RequestMapping(value="/jwtTokenValidation.do", method=RequestMethod.POST)
@ApiOperation(value="JWT 유효성 검사", notes="JWT 토큰 사용가능 유무 검사")
public void jwtTokenValidation(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 스프링 시큐리티로 사용자 ID 가져오기
// Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// 사용자ID 가져오기
// String username = (String)authentication.getPrincipal();
Map<String, Object> responseBody = new HashMap<>();
String jwtToken = jwtTokenProvider.extractToken(request);
// 토큰이 유효한 경우
if(jwtTokenProvider.isTokenBlacklisted(jwtToken) == false) {
// JWT 토큰 Claim 설정값 가져오기
Claims claims = jwtTokenProvider.extractClaims(jwtToken);
String memberUuid = claims.getSubject();
String memberId = (String)claims.get("memberId");
String memberName = (String)claims.get("memberName");
responseBody.put("status", "success");
responseBody.put("message", "토큰이 유효");
responseBody.put("memberUuid", memberUuid);
responseBody.put("memberId", memberId);
responseBody.put("memberName", memberName);
}
// 토큰이 만료된 경우
else {
responseBody.put("status", "failure");
responseBody.put("message", "토큰만료");
}
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().write(new ObjectMapper().writeValueAsString(responseBody));
response.getWriter().flush();
}
}
2) Sagger API 사용 샘플 Controller - API 문서화
이번에는 org.example.api.controller 패키지에 SwaggerController 클래스 파일을 생성한다.
SwaggerController 컨트롤러 파일은 JWT 토큰의 인증을 기본적으로 필요로 하며
Swagger API 문서화 설정에 필요한 Annotation들에 대하여 정의되어 있다.
SwaggerController.java
package org.example.api.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiImplicitParam;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.example.member.service.MemberVO;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/swagger")
@Api(tags = "02. Swagger RestAPI 서비스 샘플")
public class SwaggerController {
private final Map<String, Object> responseBody = new HashMap<>();
@RequestMapping(value="/swaggerGetOne.do", method=RequestMethod.GET)
@ApiOperation(value="GET 테스트#01", notes="Swagger API GET 방식 사용 테스트 01 - JWT 토큰 유효성 검사", hidden=false)
public void getSwaggerOne(HttpServletResponse response) throws Exception {
responseBody.put("status", "success");
responseBody.put("message", "토큰이 유효");
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().write(new ObjectMapper().writeValueAsString(responseBody));
response.getWriter().flush();
}
@RequestMapping(value="/swaggerGetTwo/{koName}/{enName}", method=RequestMethod.GET)
@ApiOperation(value="GET 테스트#02", notes="Swagger API GET 방식 사용 테스트 02 - GET 방식 Request 확인", hidden=false)
@ApiImplicitParams({
@ApiImplicitParam(name="koName", value="한글 이름", required=true, dataTypeClass=String.class, paramType="path"),
@ApiImplicitParam(name="enName", value="영문 이름", required=true, dataTypeClass=String.class, paramType="path")
})
public void getSwaggerTwo(@PathVariable("koName") String koName, @PathVariable("enName") String enName, HttpServletResponse response) throws Exception {
String message = "";
// 한글 이름과 영어 이름 둘다 존재하는 경우
if(StringUtils.isEmpty("koName") == false && StringUtils.isEmpty("enName") == false) {
message = String.format("한글 이름 : %s, 영문 이름 : %s", koName, enName);
responseBody.put("status", "success");
responseBody.put("message", message);
response.setStatus(HttpServletResponse.SC_OK); // 200
}
// 한글 이름과 영어 이름 중 하나라도 입력되지 않은 경우
else {
message = "사용자 ID, 사용자 이름이 입력되지 않았습니다.";
responseBody.put("status", "failure");
responseBody.put("message", message);
response.setStatus(HttpServletResponse.SC_BAD_REQUEST); // 400
}
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().write(new ObjectMapper().writeValueAsString(responseBody));
response.getWriter().flush();
}
@RequestMapping(value="/swaggerPost.do", method=RequestMethod.POST)
@ApiOperation(value="POST 테스트", notes="Swagger API POST 방식 사용 테스트", hidden=false)
public void postSwagger(@RequestBody MemberVO memberVO, HttpServletResponse response) throws Exception {
responseBody.put("status", "success");
responseBody.put("memberId", memberVO.getMemberId());
responseBody.put("memberName", memberVO.getMemberName());
responseBody.put("memberPhone", memberVO.getMemberPhone());
responseBody.put("memberEmail", memberVO.getMemberEmail());
response.setStatus(HttpServletResponse.SC_OK); // 200
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().write(new ObjectMapper().writeValueAsString(responseBody));
response.getWriter().flush();
}
@RequestMapping(value="/swaggerPut/{memberId}", method=RequestMethod.PUT)
@ApiOperation(value="PUT 테스트", notes="Swagger API PUT 방식 사용 테스트", hidden=false)
public void swaggerPut(@RequestBody MemberVO memberVO, HttpServletResponse response) throws Exception {
if(memberVO.getMemberId().isEmpty() == false && memberVO.getMemberName().isEmpty() == false && memberVO.getMemberPhone().isEmpty() == false && memberVO.getMemberEmail().isEmpty() == false) {
responseBody.put("status", "success");
responseBody.put("message", "회원 가입에 성공하였습니다.");
responseBody.put("memberId", memberVO.getMemberId());
responseBody.put("memberName", memberVO.getMemberName());
responseBody.put("memberPhone", memberVO.getMemberPhone());
responseBody.put("memberEmail", memberVO.getMemberEmail());
response.setStatus(HttpServletResponse.SC_CREATED); // 201
} else {
responseBody.put("status", "success");
responseBody.put("message", "누락된 항목의 값이 존재합니다.");
response.setStatus(HttpServletResponse.SC_BAD_REQUEST); // 400
}
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().write(new ObjectMapper().writeValueAsString(responseBody));
response.getWriter().flush();
}
@RequestMapping(value="/swaggerDelete/{memberId}", method=RequestMethod.DELETE)
@ApiOperation(value="DELETE 테스트", notes="Swagger API DELETE 방식 사용 테스트", hidden=false)
@ApiImplicitParam(name="memberId", value="사용자 아이디", required=true, dataTypeClass=String.class, paramType="path")
public void swaggerDelete(@PathVariable("memberId") String memberId, HttpServletResponse response) throws Exception {
String message = String.format("%s 아이디가 삭제되었습니다.", memberId);
responseBody.put("status", "success");
responseBody.put("message", message);
response.setStatus(HttpServletResponse.SC_NO_CONTENT); // 204
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().write(new ObjectMapper().writeValueAsString(responseBody));
response.getWriter().flush();
}
}
6. Swagger API - JWT 토큰 유효성 검사 예외 처리
이전 JWT 토큰 발급 및 인증 포스팅에서 org.example.security 패키지에 JwtTokenProvider.java 파일을 생성하였다.
Swagger API를 사용하는데 있어 위에서 AccessController.java, SwaggerController.java 클래스를 제작하였다.
SwaggerController는 JWT 토큰의 유효성 검사를 진행하고 유효한 경우에만 사용을 할 수 있지만.
AccessController는 JWT 토큰을 발급을 진행하기에 JWT 토큰 유효성 검사에서 예외 처리를 필요로 한다.
JwtTokenProvider.java의 JWT 토큰 유효성 검사를 진행하는 doFilterInternal( ) 함수의 내용을 수정하고,
특정 URL 요청에 대하여 JWT 토큰 인증에 대한 예외처리를 지정하는 isSkipAuthentication( ) 함수를 추가하여준다.
JwtTokenProvider.java
package org.example.security;
~~ 이 하 생 략 ~~
@Component
public class JwtTokenProvider extends OncePerRequestFilter {
~~ 이 하 생 략 ~~
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// String token = extractToken(request);
//
// // 토큰이 유효한 경우, 컨트롤러 호출
// if (StringUtils.hasText(token) == true && validateToken(token) == true) {
//
// Authentication authentication = getAuthentication(token);
// SecurityContextHolder.getContext().setAuthentication(authentication);
//
// // 다음 필터 또는 컨트롤러로 요청 전달
// filterChain.doFilter(request, response);
// } else {
//
// // 토큰이 유효하지 않은 경우, 인증 실패 처리
// handleAuthenticationFailure(request, response);
// }
String requestUri = request.getRequestURI();
// 특정 URL 경우에만 토큰 유효성 검사 진행
if(isSkipAuthentication(requestUri) == true) {
String token = extractToken(request);
// 토큰이 유효한 경우, 컨트롤러 호출
if (StringUtils.hasText(token) == true && validateToken(token) == true) {
Authentication authentication = getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
// 다음 필터 또는 컨트롤러로 요청 전달
filterChain.doFilter(request, response);
} else {
// 토큰이 유효하지 않은 경우, 인증 실패 처리
handleAuthenticationFailure(request, response);
}
} else {
filterChain.doFilter(request, response);
}
}
// 특정 URL 요청에 대하여 JWT 토큰 인증에 대한 예외처리
private boolean isSkipAuthentication(String requestUri) {
// 해당 URL에 대해서만 JWT 토큰 인증을 검사함
List<String> skipUrls = Arrays.asList(
"/access/jwtTokenValidation.do"
, "/swagger/"
);
return skipUrls.stream().anyMatch(requestUri::startsWith);
}
~~ 이 하 생 략 ~~
}
package org.example.security;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.example.member.service.MemberDAO;
import org.example.member.service.MemberVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
@Component
public class JwtTokenProvider extends OncePerRequestFilter {
@Resource(name="memberDaoMyBatis")
private MemberDAO memberDAO;
private final JwtConfig jwtConfig;
@Autowired
public JwtTokenProvider(JwtConfig jwtConfig) {
this.jwtConfig = jwtConfig;
}
// 블랙리스트에 추가되어야 하는 토큰들을 저장하는 공간
private Set<String> tokenBlacklist = new HashSet<>();
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String requestUri = request.getRequestURI();
// 특정 URL 경우에만 토큰 유효성 검사 진행
if(isSkipAuthentication(requestUri) == true) {
String token = extractToken(request);
// 토큰이 유효한 경우, 컨트롤러 호출
if (StringUtils.hasText(token) == true && validateToken(token) == true) {
Authentication authentication = getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
// 다음 필터 또는 컨트롤러로 요청 전달
filterChain.doFilter(request, response);
} else {
// 토큰이 유효하지 않은 경우, 인증 실패 처리
handleAuthenticationFailure(request, response);
}
} else {
filterChain.doFilter(request, response);
}
}
// 특정 URL 요청에 대하여 JWT 토큰 인증에 대한 예외처리
private boolean isSkipAuthentication(String requestUri) {
// 해당 URL에 대해서만 JWT 토큰 인증을 검사함
List<String> skipUrls = Arrays.asList(
"/access/jwtTokenValidation.do"
, "/swagger/"
);
return skipUrls.stream().anyMatch(requestUri::startsWith);
}
// JWT 토큰 생성
public String generateToken(Authentication authentication) {
// 사용자 UUID 추출
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
String uuid = userDetails.getUsername();
List<String> roles = Arrays.asList("ROLE_USER", "ROLE_ADMIN");
try {
// 사용자 정보 조회
MemberVO memberVO = memberDAO.selectMemberInfo(uuid);
// JWT 토큰 생성
String token = Jwts.builder()
.setSubject(userDetails.getUsername()) // 사용자 UUID
.claim("roles", roles) // 사용자 권한 역할
.claim("memberId", memberVO.getMemberId()) // 사용자 ID
.claim("memberName", memberVO.getMemberName()) // 사용자 이름
.setIssuedAt(new Date()) // JWT 토큰 발행 시간
.setExpiration(new Date(System.currentTimeMillis() + jwtConfig.getExpiration())) // JWT 토큰 만료 시간
.signWith(SignatureAlgorithm.HS512, jwtConfig.getSecret()) // JWT 토큰 서명
.compact();
return token;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// JWT 토큰 유효성 검증
public boolean validateToken(String token) {
try {
// 토큰의 서명을 확인하여 변조 여부를 검사
Claims claims = Jwts.parser().setSigningKey(jwtConfig.getSecret()).parseClaimsJws(token).getBody();
// 토큰의 만료 시간을 확인하여 만료 여부를 검사
Date expirationDate = claims.getExpiration();
Date now = new Date();
if(expirationDate.before(now)) {
System.out.println("토큰이 만료됨");
return false; // 토큰이 만료됨
}
// 필요한 경우 추가적인 유효성 검사를 수행
return true; // 토큰 유효성 검증 성공
} catch (Exception ex) {
System.out.println("토큰 유효성 검증 실패");
return false; // 토큰 유효성 검증 실패
}
}
// 토큰 추출 로직 구현
public String extractToken(HttpServletRequest request) {
String header = request.getHeader(HttpHeaders.AUTHORIZATION);
if (StringUtils.hasText(header) && header.startsWith("Bearer ")) {
return header.substring("Bearer ".length());
} else {
return null;
}
}
// 토큰으로부터 인증 객체 생성 로직 구현
private Authentication getAuthentication(String token) {
Claims claims = Jwts.parser().setSigningKey(jwtConfig.getSecret()).parseClaimsJws(token).getBody();
// 토큰에서 필요한 정보 추출
String username = claims.getSubject();
List<String> roles = claims.get("roles", List.class);
// 인증 객체 생성
List<GrantedAuthority> authorities = roles.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
return new UsernamePasswordAuthenticationToken(username, null, authorities);
}
// 인증 실패 처리를 수행합니다. (예: 오류 응답 반환)
private void handleAuthenticationFailure(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=utf-8");
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getWriter().write("유효하지 않은 토큰 또는 만료된 토큰");
}
public String extractTokenFromRequest(HttpServletRequest request) {
// String header = request.getHeader(HttpHeaders.AUTHORIZATION);
String authorizationHeader = request.getHeader("Authorization");
if(authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
return authorizationHeader.substring(7);
} else {
return null;
}
}
// 토큰 파기
protected void invalidateToken(String token) {
// 토큰이 블랙리스트에 없으면 추가
if(tokenBlacklist.contains(token) == false) {
tokenBlacklist.add(token);
}
}
// 토큰이 블랙리스트에 있는지 확인
public boolean isTokenBlacklisted(String token) {
return tokenBlacklist.contains(token);
}
// JWT 토큰에서 사용자 정보를 추출
public Claims extractClaims(String token) {
// 토큰이 블랙리스트에 있는지 확인 후, 블랙리스트에 없으면 클레임을 추출
if(isTokenBlacklisted(token) == false) {
return Jwts.parser()
.setSigningKey(jwtConfig.getSecret())
.parseClaimsJws(token)
.getBody();
} else {
// 토큰이 블랙리스트에 있다면 유효하지 않은 토큰으로 처리
return null;
}
}
}
1) Swagger API 문서 페이지를 시작페이지로 설정
마지막으로 Web Application을 실행하면 Swagger API 문서 페이지가 가장 먼저 실행이 되도록 설정을 변경한다.
이전 포스팅에서는 `jsp:forward`를 사용하여 페이지 요청을 리다이렉션 하였지만.
웹 어플리케이션이 실행되고 Sagger UI 문서화 작업 실행에 관한 딜레이 때문인지 사용할 수 없어
index.jsp 페이지가 오픈되면 JavaScript를 통해 Swagger API 페이지( swagger-ui.html )를 호출한다.
index.jsp
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Redirecting to Swagger UI</title>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function() {
window.location.href = "/swagger-ui.html";
});
</script>
</head>
<body>
<h1>Redirecting to Swagger UI...</h1>
</body>
</html>
2) Swagger API 문서에 노출시키지 않을 페이지( 숨김처리 )
Swagger API 문서 자동 생성기능을 사용하면,
개발자가 생성한 Controller에 대하여 자동적으로 API 문서화 작업이 이루어 진다.
이때 특정 컨트롤러를 노출 시키고 싶지 않은 경우에는
`@ApiIgnore` 어노테이션을 이용하여 Swager API 문서에 노출되지 않게 설정을 적용할 수 있다.
이전 포스팅에서 제작하였던 MemberController.java, BoardController.java 클래스들을
Swagger API 자동 문서화에서 제외시킨다.
① MemberController 숨김 처리
org.example.member.controller 패키지의 MemberController.java 컨트롤러를 Swagger API 문서에서 노출 시키지 않는다.
MemberController.java
package org.example.member.controller;
import springfox.documentation.annotations.ApiIgnore;
~~ 이 하 생 략 ~~
@Controller
@ApiIgnore // 스웨거UI에서 숨김 처리
public class MemberController {
@Resource(name="memberService")
private MemberService memberService;
~~ 이 하 생 략 ~~
}
② BoardController 숨김 처리
org.example.board.controller 패키지의 BoardController.java 컨트롤러를 Swagger API 문서에서 노출 시키지 않는다.
BoardController.java
package org.example.board.controller;
import springfox.documentation.annotations.ApiIgnore;
~~ 이 하 생 략 ~~
@Controller
@RequestMapping("/board")
@ApiIgnore // 스웨거UI에서 숨김 처리
public class BoardController {
@Resource(name="boardService")
private BoardService boardService;
~~ 이 하 생 략 ~~
}
8. Swagger API 문서 사용
Apache Tomcat을 실행하여 Web Application을 시작하면 Sagger API 문서 페이지가 오픈되는 것을 확인 할 수 있다.
상단 Select a spec에서 01. 사용자 접속 RestAPI 서비스 를 선택하고
[POST] /access/authenticationProcess.do 를 선택하고
사용자_계정, 사용자_비밀번호를 입력하고 JWT 토큰을 발급받는다.
발급받은 token(혹은 authorization)의 JWT 토큰을 Bearer 값까지 포함하여 클립보드로 복사한다.
( Bearer JWT_Token 클립보드로 복사 )
상단 Select a spec에서 02. Swagger RestAPI 서비스 샘플을 선택한다.
이후 Authorize 버튼을 클릭하고 Available authorizations 팝업창이 뜨면 복사한 JWT 토큰값을 붙여넣기 한다.
Bearer JWT_Token 값을 붙여넣기 하고 Authorize 버튼을 클릭한다.
입력한 JWT_Token의 값이 저장되면 Close 버튼을 클릭하여 닫아준다.
JWT 토큰을 이용한 Authorize 권한 부여가 완료되면
[GET] /swagger/swaggerGetOne.do 버튼을 클릭하여준다.
인증이 완료되면 아래와 같이 토큰이 유효하다는 success 메시지를 확인 할 수 있다.
'Spring Web > Spring Framework' 카테고리의 다른 글
[IntelliJ] Console창 한글 깨짐 해결방법 (0) | 2023.07.17 |
---|---|
[Spring] JWT 토큰 발급 받고 및 토큰 인증 받기 (0) | 2023.06.13 |
[Spring] SpringSecurity를 이용한 사용자 인증 프로세스 구축 (0) | 2023.06.13 |
[Spring] Log4j 설정 및 사용하기(log 파일 저장하기) (0) | 2023.06.13 |
[Spring] MVC 패턴 및 MyBatis 사용하는 게시판 제작 (0) | 2023.06.13 |