일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- Spring Security
- await
- CCPC
- 텐서플로맛
- 반응형 웹
- BOJ 18877
- async
- VUE
- 백준 18877번
- BOJ
- java
- 백준 BOJ
- vue.js
- 모바일 버전만들기
- social distancing
- 백준 Social Distancing II
- BOJ Social Distancing
- Social Distancing II
- 백준
- CSS
- BOJ 18881
- Catholic univ Computer Programming Contest
- spring boot
- 18877번 Social Distancing
- 베리어블 폰트
- 18877번
- 18881번
- BOJ Social Distancing II
- 일해라 개발자
- JavaScript
- Today
- Total
나아가는 길에 발자국을 찍어보자
[Spring Security Architecture] 스프링 시큐리티 아키텍쳐 본문
스프링부트로 로그인 코드를 분석하던 중 너무 모르는 함수들과 흐름이 많아서 한번 정리해보는 시간을 가졌다.
이미 시큐리티 부분은 프레임워크에 존재하기 때문에, 이번에 이해해 보려고 정리를 해봤다.
굉장히 자세한 부분까지 깊게 들어가진 않았지만 이번 기회로 자바 시큐리티의 흐름과 클래스들을 알수 있게 되어서 좋았다.
참고한 블로그는 정아마추어님 블로그이다.
굉장히 잘 설명해주셨다. 내용이 쉬운편이 아니라 2번 3번(심지어는 4~5번씩..) 읽으면서 내가 이해한 내용을 바탕으로 정리한것이다. 만약 이글을 보게된다면 나는 간략하게 정리해논 것이라 정아마추어님의 글을 읽어보는 것을 추천한다.
(미리 간략하게 정리한 내용을 올릴 것이라 허락은 맡았다! 저글을 중점으로 요약했기에 상당히 비슷할것 이라서.. )
https://jeong-pro.tistory.com/205?category=921668
[Spring Security Architecture]
-
스프링 시큐리티에서는 "인증"과 "권한"을 분리하여 체크할 수 있도록 구조를 만들었다.
-
Authentication(인증): 'A'라고 주장하는 주체(user,subject,principal)가 'A'가 맞는지 확인 하는 것
- 코드에서 Authentication : 인증과정에서 사용되는 핵심 객체 -> ID/PASSWORD, JWT, OAuth 등 여러 방식으로 인증에 필요한 값이 전달되는데 이것을 하나의 인터페이스로 받아 수행하도록 추상화 하는 역할의 인터페이스다.
-
Authorization(권한) :특정 자원에 대한 권한이 있는지 확인 하는 것.
- 프로세스 상 신분 "인증"을 거치고 신분 인증이 되었으면 권한이 있는지 확인 후, 서버자원에 대해서 접근할 수있게 되는 순서다.
- 애플리케이션에서 이권한 과정은 굉장히 여러번 일어난다. ex) id/password ,공인 인증서, 지문 등으로 로그인을 하는것은 '인증'에 해당한다.
-
Credential(증명서) : 인증 과정 중, 주체가 본인을 인증하기 위해 서버에 제공하는 것. (ID, Password 같은 것)
-
여러 필터가 있지만 요청에 대한 인증은 UsernamePasswordAuthenticationFilter가 담당한다.
- Filter 동작 코드 (예시)
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (this.postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
} else {
String username = this.obtainUsername(request);
String password = this.obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
this.setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
-
UsernamePasswordAuthenticationFilter 클래스 내의 attemptAuthentication(request, response) 메서드를 보면, 요청으로부터 username과 password를 얻어오고 그 값으로 UsernamePasswordAuthenticationToken(Authentication)을 생성한다. 그 다음에 참조하고 있던 AuthenticationManager(구현체인 ProviderManager)에게 인증을 진행하도록 위임한다.
-
UsernamePasswordAuthenticationToken 은 Authentication 인터페이스의 구현체이다. 참고로 Authentication(Interface)을 구현한 구현체여야만 AuthenticationManager에서 인증 과정을 수행할 수 있다.
->UsernamePasswordAuthenticationToken 같은 경우에는 인증 받을 때에는 아이디가 전달되며, 인증받은 후에 DB에서 받은 객체로 변경된다. => 스프링 시큐리티를 이용하는 커스텀한 인증 절차를 만드려면 어떻게 해야할까? -> UsernamePasswordAuthenticationFilter와 유사한 커스텀 필터를 만들고, 그 필터내에서 Authentication 인터페이스를 구현한 커스텀 클래스의 객체(토큰)를 만들어서 AuthenticationManager에 인증해달라고 던져주면 된다.
2. AuthenticationManager > ProviderManager > AuthenticationProvider
1) AuthenticationManager(interface)
- Authentication 객체를 받아 인증하고 인증되었다면 인증된 Authentication 객체를 돌려주는 메서드를 구현하도록 하는 인터페이스다.
- 이 메서드를 통해 인증되면 isAuthenticated(boolean)값을 TRUE로 바꿔준다
2) ProviderManager(Class)
- AuthenticationManager의 구현체로 스프링에서 인증을 담당하는 클래스로 볼 수 있다.(스프링 시큐리티가 생성하고 등록하고 관리하는 스프링 빈이므로 직접 구현할 필요가 없다.)
- 직접 인증 과정을 진행하는 것이 아니라 멤버 변수로 가지고 있는 AuthenticationProvider들에게 인증을 위임처리하고 그 중에 하나의 AuthenticationProvier객체(명확하게는 AuthenticationProvider를 구현한 클래스)가 인증과정을 거쳐서 인증에 성공하면 요청에 대해서 ProviderManager가 인증이 되었다고 알려주는 방식.
- 인증이 되었다고 알려주는 건 AuthenticationManager 인터페이스의 메서드인 authenticate() 메서드의 리턴 값인 Authentication객체 안에 인증 값을 넣어주는 것으로 처리한다
3) AthenticationProvider (Interface)
- authenticate(Authentication):Authentication → 앞서 AuthenticationManager에서 봤던 메서드와 똑같은 메서드로 인증과정이 이 메서드를 통해 진행된다.
- supports(Class<?>):boolean -> 앞에서 필터에서 보내준 Authentication 객체를 이 AuthenticationProvider가 인증 가능한 클래스인지 확인하는 메서드다.
- UsernamePasswordAuthenticationToken이 ProviderManager에 도착한다면 ProviderManager는 자기가 갖고 있는 AuthenticationProvider 목록을 순회하면서 '너가 이거 해결해줄 수 있어?' 하고 물어보고(supports()) 해결 가능하다고 TRUE를 리턴해주는 AuthenticationProvider에게 authenticate() 메서드를 실행한다. (아래에 ProviderManager.class 내의 authenticate() 메서드를 가져왔으니 앞서 말한 동작을 확인해보면 도움될 것이다.)
[중간 흐름 정리]
- 처음 UsernamePasswordAuthenticationFilter가 요청을 가로채 UsernamePasswordAuthenticationToken 객체를 AuthenticationManager에게 넘긴다.
- 실제로는 AuthenticationManager Interface를 구현한 ProviderManager에게 넘겨진 것이다.
- ProviderManager는 여러 AthenticationProvider들을 순회하면서 UsernamePasswordAuthenticationToken을 처리해줄 AuthenticationProvider를 찾는다.
- 해당 객체를 찾으면 인증을 요청한다.
=> AuthenticationProvider은 인터페이스이므로 이 인터페이스를 구현한 클래스를 만들어 ProviderManager가 클래스 객체에게 인증을 위임하도록 하면, 직접 구현한 클래스가 인증처리를 하게된다.
3. Authentication Provider
- 이곳에서 인증이 일어난다.
- 핵심은 Authentication객체로부터 인증에 필요한 정보(Id,Password)를 받아오고, UserDetailService 인터페이스를 구현한 객체 (CustomUserDetailService)로 부터 DB에 저장된 유저 정보를 받아온 후, password를 비교하고 인증 완료되면 인증 완료된 Authetication객체를 리턴한다.
4. UserDetailService (CustomUserDetailService가 상속 받는다.)
-
구현 예시(UserDetailService)
-
구현 예시(UserRepository)
=> UserRepositroty는 DB에 유저 정보를 가져오는 JPA구현체이다.
5. CustomUserDetailService.java(예시)
=> 여기서는 private UserService userService; 를 호출하여 이 클래스에서 UserRepository를 사용해 DB에서 유저정보를 가져온다.
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class CustomUserDetailsService implements UserDetailsService {
@NonNull
private UserService userService;
@Override
@Transactional(readOnly = true)
public UserDetails loadUserByUsername(String userId) {
User user = this.userService.findByUserId(userId);
return new UserPrincipal(user.getUserNo().getId(),
user.getUserId(), user.getPassword(), user.getRoles(), !user.getLocked(), user.checkActiveUser());
}
}
6. AuthenticationProvider 추가하기
- 직접 작성한 인증시스템인 CustomAuthenticationProvider를 ProviderManager가 알 수 있게 ProviderManager에게 등록하면된다.
'Spring && Spring boot' 카테고리의 다른 글
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations:{} 에러 (0) | 2020.08.21 |
---|---|
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {} 에러 (0) | 2020.08.21 |
Lombok 라이브러리 (0) | 2020.07.27 |
[Spring-boot + JPA] 댓글 기능 추가하기 (0) | 2020.07.24 |
JwtAuthenticationResponse 와 ResponseEntity (0) | 2020.07.21 |