[Spring#23] IoC, DI, JWT, Spring Security, Validation

김한준 Hanjun Kim·2023년 11월 10일
0

내일배움캠프

목록 보기
23/70

복습할 때를 대비해서

제목과 태그를 좀더 간결하게 해보고자 바꿨다.

나중에 찾아볼 때 편하게 보려면 어떻게 해야할까?

계속 고민 중이다..

왜 태그가 소문자로 될까?

! 알고리즘은 깃허브에 자동 등록하게 확장 프로그램 설정 해놨다.


학습 정리

Ioc 와 DI ( 제어의 역전과 주입)

또, 추가 설명을 적어놓으려고 한다.

스프링은 main 메소드에 관련 로직을 구현하는 그런 식으로 작성하지 않습니다.
## 스프링은 main 메소드에 관련 로직을 구현하는 그런 식으로 작성하지 않습니다.
## 로직은 pojo 형태의 java bean 클래스에 구현하고 이를 spring의 bean으로 등록한 뒤에 
spring에 해당 bean을 등록해서 이를 injection 받아 호출하여 사용하는 방식이에요.
생성자가 1개만 있을 경우에 @Autowired를 생략해도 주입이 가능함
그래서 강의에서 보면 주입할때 

public UserController(UserService userService) {
        this.userService = userService;
    }

이런식으로 생성자만 딸랑 있어도 주입이 되는것
@RequiredArgsConstructor, @Autowired보다 생성자 주입을 선호하는 편!!
Filter 또한 Component로 등록되어 처음 실행할 때 메인에서 쫙 스캔하고 그에 해당하는 어노테이션들
ex) @Controller, @Service, @Repository, @Filter 등등
을 Container에 담아 사용한다는 개념 이해

로그인 구현 JWT

  • 에러표시
    try-catch로 감싼 후 에러가 발생하면
    redirect:/api/user/login-page?error <- 클라이언트에서 이렇게 보내주세요 하고 요청이 들어옴

  • UserService -로그인 구현
    원래는 우리가 만든 QueryMethod가 Optional이기 때문에 Optional 타입으로 만들어서 확인해야됨
    바로 user로 받고 싶어서 User user로 반환함
    orElseThrow : Optional 기능. 문제가 있으면 throw

    !passwordEncoder.matches(평문, 암호화 되서 저장된 password)

Filter ( 필터 )

  • 웹 어플리케이션에서 관리되는 영역
    클라이언트에서 가장 먼저 요청, 응답의 정보를 변경하거나 부가적인 기능을 수행
    ex) 로깅, 보안 처리, 인증, 인가 등
    여러 개 만들 수 있음
  1. 로깅 필터
  2. 인증 및 인가 처리 필터
  • 로깅 필터
    @Slf4j : 로깅
    @Component : 필터를 Component로 등록
    @Order(1) : 순서를 정해줘야 해서 쓰임

  • implements Filter : 필터 인터페이스 상속
    이후 오버라이딩 후 재정의

  • 전처리, 후처리
    전처리 : 요청 -> (첫)필터 -> 필터-> ... -> 필터 -> DispatherServlet -> Controller
    후처리 : Controller -> ... -> DispatherServlet -> 필터 -> ... -> 필터(첫) -> 로그 처리!

  • 인증 및 인가 필터
    유저에 접근해야 하기 때문에 Repository, JwtUtil 생성자로 주입 받아옴.

    필터에서는 DispatherServlet보다 앞부분이기 때문에 쿠키를 직접 뽑아오는 메서드 구현 필요.

  • 실행시켜보면 서버에서 중간중간 로그가 보이는데
    log.info() 가 sout()같이 로그 찍는 기능이다.

Spring Security

  • Spring 서버에서 필요한 인증 및 인가를 위해 기능을 제공하여 개발자의 부담을 줄임.
    필터 기반으로 동작함! -> Filter의 @Component 주석

그림양해좀

  • UsernamePasswordAuthenticationFilter
    토큰 -> 매니저 -> 인증완료 -> SecutiryContextHolder에 저장, 인증처리 완료
    SecutiryContext는 인증이 완료된 사용자의 상세 정보(Authentication)을 저장한다.
    SecutiryContext는 SecutiryContextHolder로 접글할 수 있다.

  • Authentication
    principal : 사용자 식별
    credentials : 비밀번호
    authorities : 권한

Spring Security 로그인

  • Client의 모든 요청은 Spring Security를 거치게 된다

  • UserDetailService, UserDetails 구현
    위 2개를 커스텀 하게 되면 Security의 default 로그인 사용하지 않겠다는 뜻

    세션을 임의로 삭제하면 인증이 없어져 똑같이 튕겨져 나간다
    = default와 기능은 같음

Spring Security : JWT 로그인

+ Controller, Service에서 하던 JWT토큰 생성을 JwtAuthenticationFilter에서 구현하게 분리한다.
  로그인 삭제

+ 로그인 JwtAuthenticationFilter에서 구현
토큰 가져와서 이래저래
인증, 인가 처리 완료 -> 필터 다 만들었으면 필터를 등록하고 JwtAuthorizationFilter 에 추가적인 값 (JwtUtil, userDetailsService) 넘겨줘야함

 ...수동등록중...

 다 했으면 필터를 만들었지 체인 안에 넣은게 아니니까 또 체인 등록도 해줘야함
// 필터 관리
http.addFilterBefore(jwtAuthorizationFilter(), JwtAuthenticationFilter.class);
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
인가 -> 인증 -> User~Filter

@AuthenticationPrincipal 이거 어렵다

  • 구조상
    JwtAuthenticationFilter -> attemptAuthentication -> successfulAuthentication -> JwtAuthorizationFilter -> doFilterInternal -> setAuthentication -> HomeController
    근데 위에서 필터 순서가 인가가 먼저 아닌가?

    UsernamePasswordAuthenticationFilter를 상속받아서 오버라이딩 해서 사용하니까(세션방식이 아니라 JWT 쓰니까) 알아서 연동되나?

    attemptAuthentication -> successfulAuthentication 여기로 넘어가는 부분 어디?

--> 검색 해보니
여기서 드는 의문은 "어? 보통 요청이 오면 Controller에 URL과 매칭되는 메서드가 호출되는게 아닌가..? 여기서 끝이라고?" 이다. 근데 여기서 끝이 맞다. 왜냐하면 chain.doFilter(req, res) 를 통해 다음 Filter로 해당 Request를 전달하지 않았기 때문이다.
라고 한다

--> 아 이것도 맞지만 'Spring Security가 제공하는 필터들' 여기서 필터 순서대로 실행되니까 다음 필터로 알아서 넘어가는듯

Spring Security에서 접근 불가 페이지 만들기

  • 사용자의 권한(Authority)에 따라서 제어를 하겠다.
    Admin 같은 개념

  • 회원 상세정보(UserDetailslmpl)를 통해 권한 설정 가능
    규칙 : 'ROLE_' 으로 시작해야함
    Ex) ROLE_USER / ROLE_ADMIN

  • Controller 에 '@Secured' 애너테이션으로 권한 설정 가능.
    Ex) @Secured("권한 이름) 선언

  • @Secured 애너테이션 활성화 방법
    : 하고자 하는 클래스에 @EnableGlobalEMethodSecurity

  • static html은 바로 호출 가능. mvc에서 배움

Validation ( 검증 )

  • Java는 null값에 대해 NullPointerException 오류가 발생하기 때문에 꼭 Validation 과정이 필요하다.
    여러 편한 애너테이션을 제공 해준다.
@PostMapping("/validation")
@ResponseBody
public ProductRequestDto testValid(@RequestBody @Valid ProductRequestDto requestDto) {
    return requestDto;
}
// 클래스만 만드는게 아니라 컨트롤러에도 적용 해 줘야 한다.
  • PostMan으로 쿠키에 데이터 넣어서 테스트 완료!

토큰은 기한이 있으니 만료되면 지워주고 다시 테스트!

Validation 예외처리하기

  • SignupRequestDto를 검증하지 않았었으니 이 데이터를 검증해보기로
 @PostMapping("/user/signup")
    public String signup(@Valid SignupRequestDto requestDto, BindingResult bindingResult) {
        // Validation 예외처리
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        if(fieldErrors.size() > 0) {
            for (FieldError fieldError : bindingResult.getFieldErrors()) {
                log.error(fieldError.getField() + " 필드 : " + fieldError.getDefaultMessage());
            }
            return "redirect:/api/user/signup";
        }

        userService.signup(requestDto);

        return "redirect:/api/user/login-page";
    }
  • bindingResult : 해당 오류가 어떻게 발생하고 있는지 오류를 찍어주는 코드
    redirect로 다시 로그인 하게 유도!
profile
개발이 하고싶은 개발지망생

0개의 댓글