
Jwt_Security (2) 포스트에서 만든 jwtProvider와 jwtauthentication 등을 이용하고, 확장하여 회원가입과 로그인 Swagger문서를 활용한 RestApi를 구현하였습니다.
SecurityConfigration
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final JwtTokenProvider jwtTokenProvider;
@Autowired
public SecurityConfiguration(JwtTokenProvider jwtTokenProvider){
this.jwtTokenProvider = jwtTokenProvider;
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception{
httpSecurity.httpBasic().disable() // RestAPI는 UI를 사용하지 않기 때문에, 기본설정을 비활성화
.csrf().disable() //RestAPI는 csrf 보안이 필요 없으므로 비활성화
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
//JwtToken 인증방식이므로 세션을 사용하지 않으므로 비활성화
.and()
.authorizeRequests() // 요청에 대한 사용권한 체크
.antMatchers("/sign-api/sign-in","/sign-api/sign-up","/sign-api/exception")
.permitAll() //가입과 로그인 주소는 허용
.antMatchers(HttpMethod.GET,"/product/**").permitAll()
//product로 시작하는 GET 요청은 허용
.antMatchers("**exception**").permitAll()
.anyRequest().hasRole("ADMIN") //나머지 요청은 ADMIN만 접근 가능
.and()
.exceptionHandling().accessDeniedHandler(new CustomAccessDeniedHandler())
.and()
.exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint())
.and()
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class);
}
@Bean
public PasswordEncoder passwordEncoder(){
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Override
public void configure(WebSecurity webSecurity) {
webSecurity.ignoring().antMatchers("/v2/api-docs", "/swagger-resources/**",
"/swagger-ui.html", "/webjars/**", "/swagger/**", "/sign-api/exception");
}
}
<HttpSecurity 파라미터의 configure() 메서드>
HttpSecurity의 대표적인 기능
구분 별 설명
CSRF란?
CSRF(Cross-Site Request Forgery)의 줄임말로 '사이트 간 요청 위조'를 의미합니다.
'사이트 간 요청 위조'란?
웹 어플리케이셔의 취약점 중 하나로서 사용자가 자신의 의지와 무관하게 웹 어플리케이션을 대상으로 공격자가 의도한 행동을 함으로써 특정 페이지의 보안을 취약하게 한다거나 수정, 삭제등의 잡업을 하는 공격 방법입니다.
스프링 시큐리티 csrf() 메서드는 기본적으로 CSRF 토큰을 발급해서 클라이언트로부터 요청을 받을 때마다 토큰을 검증하는 방식으로 동작하기 때문에 브라우저 사용 환경이 아니라면 비활성화 해도 크게 문제 되 지않습니다.
<WebSecurity 파라미터의 configure() 메서드>
시큐리티 설정에서 이 부분들을 보시면, new CustomAccessDeniedHandler(),new CustomAuthenticationEntryPoint()이 있습니다. 이 경우 직접 AccessDeniedHandler와 AuthenticationEntryPoint 를 구현하였습니다.
.exceptionHandling().accessDeniedHandler(new CustomAccessDeniedHandler())
.and()
.exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint())
< CustomAccessDeniedHandler >
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
private Logger logger = LoggerFactory.getLogger(CustomAccessDeniedHandler.class);
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException e)
throws IOException, ServletException{
logger.info("[handle] 접근이 막혔을 경우 리다이렉트");
response.sendRedirect("/sign-api/exception");
}
}
< CustomAuthenticationEntryPoint >
@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
private Logger logger = LoggerFactory.getLogger(CustomAuthenticationEntryPoint.class);
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException e)
throws IOException, ServletException{
ObjectMapper objectMapper = new ObjectMapper();
logger.info("[commence] 인증 실패로 response.sendError 발생");
EntryPointErrorResponse entryPointErrorResponse = new EntryPointErrorResponse();
entryPointErrorResponse.setMsg("인증이 실패 하였습니다.");
response.setStatus(401);
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
response.getWriter().write(objectMapper.writeValueAsString(entryPointErrorResponse));
}
}
< EntryPointErrorResponse >
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class EntryPointErrorResponse {
private String msg;
}
Swagger문서에 대해서는 나중에 더 자세히 포스팅 하겠습니다!
스웨거문서를 작성하려면 디펜던시를 추가해야 합니다.
<dependencies>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
</dependencies>
@Configuration
@EnableSwagger2
public class SwaggerConfiguration {
@Bean
public Docket api(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.springboot.jwt_securityprac"))
.build();
}
private ApiInfo apiInfo(){
return new ApiInfoBuilder()
.title("jwt_security")
.description("jwt_security")
.version("1.0.0")
.build();
}
}
@Configuration: 이 어노테이션은 스프링 빈 구성 클래스임을 나타냅니다. 스프링 애플리케이션 컨텍스트에 이 클래스를 빈으로 등록합니다.
@EnableSwagger2: Swagger 2.0 버전을 사용하도록 설정하는 어노테이션입니다. Swagger 2를 활성화합니다.
@Bean public Docket api(): Docket 객체를 빈으로 생성하고 반환합니다. Docket은 Swagger 문서를 빌드하고 커스터마이즈하는 데 사용됩니다.
apiInfo(): API 문서에 대한 정보를 설정하는 메서드입니다. ApiInfo 객체를 생성하고 반환합니다.
select(): Swagger가 문서화할 API를 선택하는데 사용됩니다.
apis(RequestHandlerSelectors.basePackage("com.springboot.jwt_securityprac")): com.springboot.jwt_securityprac 패키지에 속한 컨트롤러 클래스에서만 문서화할 API를 선택합니다. 이 패키지 아래에 있는 컨트롤러 클래스들의 API만 문서화됩니다.
build(): Docket 객체를 빌드하고 설정을 완료합니다.
private ApiInfo apiInfo(): API 문서에 대한 정보를 설정하는 메서드입니다. ApiInfoBuilder를 사용하여 API 문서의 제목, 설명, 버전 등을 설정합니다.