제목과 태그를 좀더 간결하게 해보고자 바꿨다.
나중에 찾아볼 때 편하게 보려면 어떻게 해야할까?
계속 고민 중이다..
왜 태그가 소문자로 될까?
! 알고리즘은 깃허브에 자동 등록하게 확장 프로그램 설정 해놨다.
Ioc 와 DI ( 제어의 역전과 주입)
이전에 넘어갔지만 개념 정리가 확실히 되지 않아 다시 찾아서 공부하던 중 정리가 잘 되어있는 블로그가 있어 링크를 남긴다.
특히 예제 부분이 나에게는 크게 와닿았기 때문에!
문제가 될 시 삭제!
https://velog.io/@squarebird/Spring-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85
또, 추가 설명을 적어놓으려고 한다.
스프링은 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 ( 필터 )
로깅 필터
@Slf4j : 로깅
@Component : 필터를 Component로 등록
@Order(1) : 순서를 정해줘야 해서 쓰임
implements Filter : 필터 인터페이스 상속
이후 오버라이딩 후 재정의
전처리, 후처리
전처리 : 요청 -> (첫)필터 -> 필터-> ... -> 필터 -> DispatherServlet -> Controller
후처리 : Controller -> ... -> DispatherServlet -> 필터 -> ... -> 필터(첫) -> 로그 처리!
인증 및 인가 필터
유저에 접근해야 하기 때문에 Repository, JwtUtil 생성자로 주입 받아옴.
필터에서는 DispatherServlet보다 앞부분이기 때문에 쿠키를 직접 뽑아오는 메서드 구현 필요.
실행시켜보면 서버에서 중간중간 로그가 보이는데
log.info() 가 sout()같이 로그 찍는 기능이다.
Spring Security
그림양해좀
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 ( 검증 )
@PostMapping("/validation")
@ResponseBody
public ProductRequestDto testValid(@RequestBody @Valid ProductRequestDto requestDto) {
return requestDto;
}
// 클래스만 만드는게 아니라 컨트롤러에도 적용 해 줘야 한다.
Validation 예외처리하기
@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";
}