🐳
Engineering Wiki
  • 🖐️Welcome
  • 📚백엔드 로드맵
    • 메인페이지
  • Spring
    • spring boot
      • security
        • security 기본
        • filter
        • JWT
      • 스프링 핵심 원리
        • 객체지향 설계와 스프링
        • 스프링IoC컨테이너와 bean
      • IntelliJ
        • Spring boot 생성 및 git clone
        • Spring boot 프로젝트 생성
      • vscode
        • Spring boot 프로젝트 생성
      • scheduling
        • 스케쥴링 설정시 에러 상황
      • paging
      • 에러 핸들링
        • ErrorCode생성 및 ExceptionHandler로 에러처리
        • Security & JWT 에러처리
        • spring cloud sleuth
      • 로그 핸들링
        • logback
        • HttpRequestServlet 래핑
      • gradle
        • hidetake.ssh 키파일 설정
      • maven
        • maven tomcat
      • lib
        • lombok
        • tiles
      • API 부하테스트 툴 K6
      • JPA
        • Mybatis / JPA 차이
      • Mybatis
    • spring batch
      • batch
        • Spring Batch 기본개념
  • FRONT
    • vue
      • Spring boot & Vue.js 설치 및 연동
      • Spring boot & Vue.js 웹개발 세팅
      • vue의 기본구조 실행순서
      • SPA 이해
  • JAVA
    • 환경설정
    • 자바의 정석
      • generics
  • DATABASE
    • mongoDB
      • 정규표현식을 사용해 대소문자 구분없이 검색
      • mongoDB export import
      • MAC 설치 및 실행
    • MYSQL
      • dbeaver 데이터 내보내기 불러오기
      • [에러] 스프링 mysql 8 연결 에러
      • MAC M1 mysql 설치
      • GROUP BY 정리
      • 테이블 명세서 빠르게 생성
  • AWS
    • IAM
    • 설치&명령어
      • eb 설치 & 명령어
      • CLI 설치 & 명령어
    • sam
      • SAM 개념
      • SAM Lambda S3이벤트 트리거, MongoDB 접근코드
      • SAM intellij 배포
    • peering
      • mongodb atlas AWS vpc peering
      • MongoDB & Lambda VPC peering ,endpoint설정
    • 쉘스크립트
      • 도커 컨테이너 중단시 슬랙 리포팅 및 재실행
  • DOCKER
    • 설치&명령어
      • Docker 기초
      • Docker Container 유용한 명령어
    • MAC관련 문제
      • 이미지 빌드 관련 문제상황
      • MAC M1 도커 실행 원리
      • [에러] docker: Error response from daemon: Mounts denied:
  • ELK
    • 세팅
      • 로드벨런서에 logstash 세팅
      • Elastic Beanstalk + Elastic Cloud + docker 설정
      • ElasticCloud + filebeat + logstash + docker 설정 (버전8.5.0)
      • ELK 적용 사례, 로그수집(filebeat/logstash) 설명
    • logstash
      • Logstash는 로그를 왜 message라는 field로 저장할까?
      • logstash health check
    • filebeat
      • filebeat 아키텍쳐
  • unity
    • 유니티 기본
      • 캐릭터 이동
      • 카메라
  • WORDPRESS
    • 워드프레스 기본
  • git
    • GIT 개념
      • 라이프사이클
    • 명령어
      • defult 브랜치 main 으로 변경
      • 첫번째 커밋 삭제(브런치삭제) 후 원격저장소에 강제 push
      • git 원격저장소에 remote 방법(vscode로 진행)
      • git gh
      • git reset
      • git rebase
  • MAC
    • 개발 환경세팅
      • 맥 초기 개발세팅
    • 유용한내용
      • app store 다운로드 없이 웹에서 Xcode 다운
      • ubuntu iso 설치 usb 만들기
      • 응용프로그램 에러
      • 잠김 파일
  • CS
    • data structure & algorism
      • 자료구조의 정의 및 종류
  • 방통대
    • 대학수학의 이해
      • 1강. 수학의 기초(1)
    • 딥러닝
      • 1강.신경망의 개요
  • NODE
    • 개발기록
      • 인스타그램 API 활용하여 게시물 슬랙에 리포팅
Powered by GitBook
On this page
  1. Spring
  2. spring boot
  3. 에러 핸들링

Security & JWT 에러처리

생성일: 2022년 12월 1일 오후 2:14

Security

인증 Authentication - 인증 하는거

인가 Authorization - 권한 주는거

에러처리시 Security에서 커스텀 하는 클래스들

  • AccessDeniedHandler - 권한체크하고 권한 없으면 동작 - 권한

  • AuthenticationEntryPoint - 인증안된 유저가 요청했을때 - 인증

개발자가 커스텀 예외처리 하는건 스프링 영역임

But 시큐리티는 스프링 이전에 필터링 하고 DispatcherServlet 에 들어옴

그래서 exception 마다 자세히 에러처리해줌

본인은 인증만 구현함

  • 시큐리티 에러 종류

/**
 * Security exception종류
* UsernameNotFoundException :계정 없음
* InternalAuthenticationServiceException :존재하지 않는 아이디
* InsufficientAuthenticationException :자격증명 신뢰할 수 잆음(jwt)
 * BadCredentialsException :비밀번호 불일치(현재email, pw불일치시 실행됨)
 * AccountExpiredException :계정만료
* CredentialsExpiredException :비밀번호 만료
* DisabledException :계정 비활성화
* LockedException :계정 잠김
*/
  • AuthenticationEntryPoint 를 구현한 시큐리티 에러 처리 commence 오버라이드

@Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
                         AuthenticationException e) throws IOException {

        if (e.getClass().equals(BadCredentialsException.class)) {

            ApiException ex = new ApiException(ErrorCode.ERROR_USER_LOGIN_INVALID);
            HashMap<String, String> errors = apiExceptionHandler.handleException(ex, httpServletResponse);
            log.error("Unauthorized error: {}",errors);

        }

				.....(생략)

}
  • handleException 메서드

  • @ExceptionHandler 같은걸로 처리되지 않아서 HttpServletResponse 를 활용해 응답을 보냄

  • 참고

/**
     * Security&JWT Filter Exception
     */
    public HashMap<String, String> handleException(ApiException ex, HttpServletResponse res) throws IOException {

        res.setContentType("application/json");
        HashMap<String, String> errors = new HashMap<>();
        errors.put("error_code", ex.getErrorCode().getErrorCode()+"");
        errors.put("error_user_msg", ex.getErrorCode().name());
        res.getWriter().println(
                "{ \"error_code\": \"" + ex.getErrorCode().getErrorCode()+"" + "\""
                +", \"error_user_msg\": \"" + ex.getErrorCode().name() + "\" }");

        return errors;
    }

JWT 에러 종류

/**
 * MalformedJwtException, SignatureException :잘못된 구성의 토큰
* ExpiredJwtException :만료된 토큰
* UnsupportedJwtException :지원하지 않는 포멧의 토큰
*/
  • 여기있는게 다는 아님 필요해보이는것만 일단 검색해서 사용했음

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
    try {
        Authentication authentication = tokenProvider.getAuthentication(request);
        SecurityContextHolder.getContext().setAuthentication(authentication);

        filterChain.doFilter(request, response);
    } catch (JwtException e) {
        ApiException ex = new ApiException(ErrorCode.ERROR_USER_TOKEN);
        HashMap<String, String> errors = apiExceptionHandler.handleException(ex, response);
log.error("jwt error: {} : {}", errors ,e.getMessage());
    }
}
  • OncePerRequestFilter 를 구현하고 doFilterInternal 를 오버라이드

  • 인증과 같은 방식, handleException 사용

  • getAuthentication 메서드는 TokenProvider 클래스를 만들고 토큰을 파싱해 유저정보를 가져옴

public String getAuthenticationUser(String token) {
    try {
        return Jwts.parser()
                .setSigningKey(jwtSecret)
                .parseClaimsJws(trimToken(token))
                .getBody()
                .getSubject();
    } catch (MalformedJwtException | SignatureException e) {
log.error("{} : {}", ErrorCode.ERROR_USER_TOKEN_INVALID.getErrorCode(),
                ErrorCode.ERROR_USER_TOKEN_INVALID.getLogMsg());

    } catch (ExpiredJwtException e) {
log.error("{} : {}", ErrorCode.ERROR_USER_TOKEN_EXPIRED.getErrorCode(),
                ErrorCode.ERROR_USER_TOKEN_EXPIRED.getLogMsg());

    } catch (UnsupportedJwtException e) {
log.error("{} : {}", ErrorCode.ERROR_USER_TOKEN_SUPPORT.getErrorCode(),
                ErrorCode.ERROR_USER_TOKEN_SUPPORT.getLogMsg());

    }
    return null;
}

public Authentication getAuthentication(HttpServletRequest request) {
    String token = request.getHeader(HEADER_STRING);
    if (token != null) {
        // parse the token.
        String user = getAuthenticationUser(token);
        return user != null ? new UsernamePasswordAuthenticationToken(user, null,emptyList()) : null;
    }
    return null;
}
  • 토큰 생성이나 get 등 토큰에 관련된 메서드를 다 여기다 만듬, 때문에 여기서 예외처리해줌

JWTAuthenticationFilter authFilter = new JWTAuthenticationFilter(this.tokenProvider);

http
 ...
		.exceptionHandling().authenticationEntryPoint(unauthorizedHandler)
		.addFilterBefore(authFilter, UsernamePasswordAuthenticationFilter.class);
  • WebSecurityConfigurerAdapter 를 구현한 시큐리티 config 파일의 설정

  • 인증관련 에러 핸들링 넣어주고

  • UsernamePasswordAuthenticationFilter 는 인증관련 클래스인데 (아이디 비번 체크함) 인증전에 토큰관련된 예외처리 먼저한다는 거임

개발시 문제 상황

헬스체크를 하는 데 http 로 ‘/’ 경로로 헬스체크했음, 시큐리티 인증 에러가남, beanstalk 이 http로 헬스체크해서 해결해야했음

그래서 에러의 이유를 찾아봄

  • json 이 아니라 html 로 요청 (/) 했을때 에러가남 -> 뷰리졸버를 찾는데 없어서 에러가 나고 -> /error 요청 -> resources, static 등 아래 정적파일 찾는데 -> 이것들에 관해 인증 허용이 안되서 (에러페이지에 인증X) 인증에러가남

WebSecurityConfigurerAdapter 를 구현한 클래스에 정적파일 접근 권한 줌

/*
     * resources, static 아래 파일들의 접근권한 설정
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
                .requestMatchers(
                        PathRequest.toStaticResources().atCommonLocations()
                );
    }

에러페이지 404 도 접근허용 해줌

@Override
    protected void configure(HttpSecurity http) throws Exception {
        JWTAuthenticationFilter authFilter = new JWTAuthenticationFilter(this.tokenProvider);

        http
                .cors()
                .and()
                .csrf().disable() // Restful API 방식 사용으로 disable 처리
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler)
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/",
                        ...
                        "/404"
                        ).permitAll()
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilterBefore(authFilter, UsernamePasswordAuthenticationFilter.class);
    }

참고

  • CORS

  • WebMvcConfig 는 시큐리티 필터가 먼저 막음,,, 그래서 필터에서 허용해줘야됨

  • csrf 도 잘 설정해줘야한다고함 Spring Security csrf token 검색 ㄱ

PreviousErrorCode생성 및 ExceptionHandler로 에러처리Nextspring cloud sleuth

Last updated 2 years ago

ApiException 의 경우 참고

에러 핸들링 ExceptionHandler
Handle spring security authentication exceptions with @ExceptionHandler
https://imbf.github.io/spring/2020/06/29/Spring-Security-with-JWT.html
https://hou27.tistory.com/entry/Spring-Security-JWT
https://devlog-wjdrbs96.tistory.com/429
https://atoz-develop.tistory.com/entry/Spring-BootSpring-Web-MVC-ViewController%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%B4%EC%84%9C-%EB%B7%B0-%EB%A7%A4%ED%95%91%ED%95%98%EA%B8%B0
https://csy7792.tistory.com/243
https://hou27.tistory.com/entry/Spring-Security-JWT
https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter
https://kimchanjung.github.io/programming/2020/07/02/spring-security-02/