Junit Test Application-21-jwt 인가 필터 세팅 및 등록

jaegeunsong97·2023년 8월 4일
0

Junit Bank Application 깃허브

Junit Bank Application 기록 노션

package shop.mtcoding.bank.config.jwt;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

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.security.web.authentication.www.BasicAuthenticationFilter;

import shop.mtcoding.bank.config.auth.LoginUser;

// 모든 주소에서 동작(검증)
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {

     public JwtAuthorizationFilter(AuthenticationManager authenticationManager) {
          super(authenticationManager);
     }

     @Override
     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
               throws IOException, ServletException {
          if (isHeaderVerify(request, response)) {
               // 토큰 존재
               String token = request.getHeader(JwtValueObject.HEADER).replace(JwtValueObject.TOKEN_PREFIX, "");
               LoginUser loginUser = JwtProcess.verify(token); // id, role만 존재

               // 임시 세션, 인증처럼 UserDetailsService 호출 X, UserDetails or username
               Authentication authentication = new UsernamePasswordAuthenticationToken(loginUser, null,
                         loginUser.getAuthorities());
               SecurityContextHolder.getContext().setAuthentication(authentication);

          }
          chain.doFilter(request, response);
     }

     private boolean isHeaderVerify(HttpServletRequest request, HttpServletResponse response) {
          String header = request.getHeader(JwtValueObject.HEADER);
          if (header == null || !header.startsWith(JwtValueObject.TOKEN_PREFIX)) {
               return false;
          } else {
               return true;
          }
     }
}

SecurityConfig 추가

// JWT 등록
     public class CustomSecurityFilterManager
               extends AbstractHttpConfigurer<CustomSecurityFilterManager, HttpSecurity> {

          @Override
          public void configure(HttpSecurity builder) throws Exception {
               AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);
               // 강제 세션 로그인을 위해 JwtAuthenticationFilter에 authenticationManager 필요
               builder.addFilter(new JwtAuthenticationFilter(authenticationManager));
               builder.addFilter(new JwtAuthorizationFilter(authenticationManager));
               super.configure(builder);
          }
     }

UsernamePasswordAuthenticationToken에서 첫번째 매개변수는 UserDetails or username 타입만 가능하다.

  • 테스트

베리어 토큰

404, 필터 통과하고 컨트롤러까지 갔다는 의미, 즉 인증 그리고 권한 통과

admin 테스트

형태를 잡아야한다.

ex 에 코드 추가

package shop.mtcoding.bank.handler.ex;

public class CustomForbiddenException extends RuntimeException {

     public CustomForbiddenException(String message) {
          super(message);
     }
}
@RestControllerAdvice
public class CumstomExceptionHandler {

     private final Logger log = LoggerFactory.getLogger(getClass());

     @ExceptionHandler(CustomApiException.class)
     public ResponseEntity<?> apiException(CustomApiException e) {
          log.error(e.getMessage());
          return new ResponseEntity<>(new ResponseDto<>(-1, e.getMessage(), null), HttpStatus.BAD_REQUEST);
     }

     @ExceptionHandler(CustomForbiddenException.class)
     public ResponseEntity<?> forbiddenException(CustomForbiddenException e) {
          log.error(e.getMessage());
          return new ResponseEntity<>(new ResponseDto<>(-1, e.getMessage(), null), HttpStatus.FORBIDDEN);
     }

     @ExceptionHandler(CustomValidationException.class)
     public ResponseEntity<?> validationApiException(CustomValidationException e) {
          log.error(e.getMessage());
          return new ResponseEntity<>(new ResponseDto<>(-1, e.getMessage(), e.getErrorMap()), HttpStatus.BAD_REQUEST);
     }
}

SecurityConfig 수정

// JWT 서버 -> 세션 사용 X(무상태 서버)
     @Bean
     public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
          log.debug("디버그 : filterChain 빈 등록됨");
          http.headers().frameOptions().disable(); // iframe 허용 X
          http.csrf().disable();
          http.cors().configurationSource(configurationSource());

          // jSessionId 서버쪽에서 관리 안하겠다는 것
          http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
          // react, app 요청 예정
          http.formLogin().disable();
          // 브라우저가 팝업창을 이용해서 사용자 인증을 진행
          http.httpBasic().disable();
          // 필터 적용
          http.apply(new CustomSecurityFilterManager());

          // 인증실패
          http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
               CustomResponseUtil.fail(response, "로그인을 진행해 주세요", HttpStatus.UNAUTHORIZED);
          });

          // 권한실패
          http.exceptionHandling().accessDeniedHandler((request, response, e) -> {
               CustomResponseUtil.fail(response, "권한이 없습니다.", HttpStatus.FORBIDDEN);
          });

          http.authorizeRequests()
                    .antMatchers("/api/s/**").authenticated()
                    .antMatchers("/api/admin/**").hasRole("" + UserEnum.ADMIN) // ROLE_
                    .anyRequest().permitAll();

          return http.build();
     }

accessDeniedHandler -> AccessDeniedHandler

handle 부분 3가지를 받아서 처리하면 끝

참고로 fail 부분 바꿈

profile
블로그 이전 : https://medium.com/@jaegeunsong97

0개의 댓글