본문 바로가기
카테고리 없음

230220 TIL

by hbIncoding 2023. 2. 21.

1.  Spring Security

  1) Spring Security란?

  • Spring 기반의 애플리케이션의 보안(인증과 권한, 인가 등)을 담당하는 스프링 하위 프레임워크
  • Spring Security는 '인증'과 '권한'에 대한 부분을 Filter 흐름에 따라 처리하고 있다.
  • Filter는 Dispatcher Servlet으로 가기 전에 적용되므로 가장 먼저 URL 요청을 받지만, Interceptor는 Dispatcher와 Controller사이에 위치한다는 점에서 적용 시기의 차이가 있다.
  • Spring Security는 보안과 관련해서 체계적으로 많은 옵션을 제공해주기 때문에 개발자 입장에서는 일일이 보안관련 로직을 작성하지 않아도 된다는 장점이 있다.

  2) 인증과 인가

  3) Spring Security 모듈

  • SecurityContextHolder
    • 보안 주체의 세부 정보를 포함하여 응용프래그램의 현재 보안 컨텍스트에 대한 세부 정보가 저장된다.
    • SecurityContextHolder는 기본적으로 SecurityContextHolder.MODE_INHERITABLETHREADLOCAL 방법과SecurityContextHolder.MODE_THREADLOCAL 방법을 제공한다.
  • SecurityContext
    • Authentication을 보관하는 역할을 하며, SecurityContext를 통해 Authentication 객체를 꺼내올 수 있다.
  • Authentication
    • Authentication는 현재 접근하는 주체의 정보와 권한을 담는 인터페이스
    • Authentication 객체는 Security Context에 저장되며, SecurityContextHolder를 통해 SecurityContext에 접근하고, SecurityContext를 통해 Authentication에 접근할 수 있다.
public interface Authentication extends Principal, Serializable {
    // 현재 사용자의 권한 목록을 가져옴
    Collection<? extends GrantedAuthority> getAuthorities();
    
    // credentials(주로 비밀번호)을 가져옴
    Object getCredentials();
    
    Object getDetails();
    
    // Principal 객체를 가져옴.
    Object getPrincipal();
    
    // 인증 여부를 가져옴
    boolean isAuthenticated();
    
    // 인증 여부를 설정함
    void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
  • UsernamePasswordAuthenticationToken
    • Authentication을 implements한 AbstractAuthenticationToken의 하위 클래스로, User의 ID가 Principal 역할을 하고, Password가 Credential의 역할을 한다.
    • UsernamePasswordAuthenticationToken의 첫 번째 생성자는 인증 전의 객체를 생성하고, 두번째 생성자는 인증이 완려된 객체를 생성한다.
public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
    // 주로 사용자의 ID에 해당함
    private final Object principal;
    // 주로 사용자의 PW에 해당함
    private Object credentials;
    
    // 인증 완료 전의 객체 생성
    public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
		super(null);
		this.principal = principal;
		this.credentials = credentials;
		setAuthenticated(false);
	}
    
    // 인증 완료 후의 객체 생성
    public UsernamePasswordAuthenticationToken(Object principal, Object credentials,
			Collection<? extends GrantedAuthority> authorities) {
		super(authorities);
		this.principal = principal;
		this.credentials = credentials;
		super.setAuthenticated(true); // must use super, as we override
	}
}


public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer {
}
  • AuthenticationProvider
    • .제 인증에 대한 부분을 처리하는데, 인증 전의 Authentication객체를 받아서 인증이 완료된 객체를 반환하는 역할을 한다.
    • 아래와 같은 AuthenticationProvider 인터페이스를 구현해서 Custom한 AuthenticationProvider을 작성해서 AuthenticationManager에 등록하면 된다.
public interface AuthenticationProvider {

	// 인증 전의 Authenticaion 객체를 받아서 인증된 Authentication 객체를 반환
    Authentication authenticate(Authentication var1) throws AuthenticationException;

    boolean supports(Class<?> var1);
    
}
  • Authentication Manager
    • 인증에 대한 부분은 SpringSecurity의 AuthenticatonManager를 통해서 처리하게 되는데, 실질적으로는 AuthenticationManager에 등록된 AuthenticationProvider에 의해 처리된다.
    • 인증이 성공하면 2번째 생성자를 이용해 인증이 성공한(isAuthenticated=true) 객체를 생성하여 Security Context에 저장한다. 그리고 인증 상태를 유지하기 위해 세션에 보관하며, 인증이 실패한 경우에는 AuthenticationException를 발생시킨다.
public interface AuthenticationManager {
	Authentication authenticate(Authentication authentication) 
		throws AuthenticationException;
}
      
  • --> AuthenticationManager를 implements한 ProviderManager는 실제 인증 과정에 대한 로직을 가지고 있는 AuthenticaionProvider를 List로 가지고 있으며, ProividerManager는 for문을 통해 모든 provider를 조회하면서 authenticate 처리를 한다.
public class ProviderManager implements AuthenticationManager, MessageSourceAware,
InitializingBean {
    public List<AuthenticationProvider> getProviders() {
		return providers;
	}
    public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {
		Class<? extends Authentication> toTest = authentication.getClass();
		AuthenticationException lastException = null;
		Authentication result = null;
		boolean debug = logger.isDebugEnabled();
        //for문으로 모든 provider를 순회하여 처리하고 result가 나올 때까지 반복한다.
		for (AuthenticationProvider provider : getProviders()) {
            ....
			try {
				result = provider.authenticate(authentication);

				if (result != null) {
					copyDetails(authentication, result);
					break;
				}
			}
			catch (AccountStatusException e) {
				prepareException(e, authentication);
				// SEC-546: Avoid polling additional providers if auth failure is due to
				// invalid account status
				throw e;
			}
            ....
		}
		throw lastException;
	}
}
  • --> 위에서 설명한 ProviderManager에 우리가 직접 구현한 CustomAuthenticationProvider를 등록하는 방법은 WebSecurityConfigurerAdapter를 상속해 만든 SecurityConfig에서 할 수 있다. WebSecurityConfigurerAdapter의 상위 클래스에서는 AuthenticationManager를 가지고 있기 때문에 우리가 직접 만든 CustomAuthenticationProvider를 등록할 수 있다.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  
    @Bean
    public AuthenticationManager getAuthenticationManager() throws Exception {
        return super.authenticationManagerBean();
    }
      
    @Bean
    public CustomAuthenticationProvider customAuthenticationProvider() throws Exception {
        return new CustomAuthenticationProvider();
    }
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(customAuthenticationProvider());
    }
}
  • Userdetails
    • 인증에 성공하여 생성된 UserDetails 객체는 Authentication객체를 구현한 UsernamePasswordAuthenticationToken을 생성하기 위해 사용된다.
    • UserDetails 인터페이스를 살펴보면 아래와 같이 정보를 반환하는 메소드를 가지고 있다. UserDetails 인터페이스의 경우 직접 개발한 UserVO 모델에 UserDetails를 implements하여 이를 처리하거나 UserDetailsVO에 UserDetails를 implements하여 처리할 수 있다.
public interface UserDetails extends Serializable {

    Collection<? extends GrantedAuthority> getAuthorities();

    String getPassword();

    String getUsername();

    boolean isAccountNonExpired();

    boolean isAccountNonLocked();

    boolean isCredentialsNonExpired();

    boolean isEnabled();
    
}
  • UserDetailsService
    • UserDetailsService 인터페이스는 UserDetails 객체를 반환하는 단 하나의 메소드를 가지고 있는데, 일반적으로 이를 구현한 클래스의 내부에 UserRepository를 주입받아 DB와 연결하여 처리한다. 
public interface UserDetailsService {

    UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;

}
  • Password Encoding
    • AuthenticationManagerBuilder.userDetailsService().passwordEncoder() 를 통해 패스워드 암호화에 사용될 PasswordEncoder 구현체를 지정할 수 있다.
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	// TODO Auto-generated method stub
	auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}

@Bean
public PasswordEncoder passwordEncoder(){
	return new BCryptPasswordEncoder();
}
  • GrantedAuthority
    • GrantAuthority는 현재 사용자(principal)가 가지고 있는 권한을 의미한다.
    • ROLE_ADMIN나 ROLE_USER와 같이 ROLE_*의 형태로 사용하며, 보통 "roles" 이라고 한다.
    • GrantedAuthority 객체는 UserDetailsService에 의해 불러올 수 있고, 특정 자원에 대한 권한이 있는지를 검사하여 접근 허용 여부를 결정한다.

2.  OAuth

  1) OAuth(Open Standard for Authorization)란?

  • 개방형 Authorization 의 표준이며 API 허가(Authorize)를 목적으로 JSON 형식으로 개발된 HTTP 기반의 보안 프로토콜
  • 사용자들이 사용하고자 하는 웹사이트 및 애플리케이션에 비밀번호를 제공하지 않고 접근 권한을 부여 받을 수 있게 해주는 공통적 수단으로서 사용 되어지는 기술입니다.
  • 다양한 클라이언트 환경에 적합한 인증(Authentication) 및 인가(Authorization) 의 위임 방법을 제공하고 그 결과로 클라이언트에게 접근 토큰 (Access Token) 을 발급하는 것에 대한 구조다.

  2) OAuth 1.0

 

  • 인증 순서
    1. 소비자가 서비스 제공자에게 요청 토큰을 요청한다.
    2. 서비스 제공자가 소비자에게 요청 토큰을 발급해준다.
    3. 소비자가 사용자를 서비스 제공자로 이동시킨다. 여기서 사용자 인증이 수행
    4. 서비스 제공자가 사용자를 소비자로 이동시킨다.
    5. 소비자가 접근 토큰을 요청한다.
    6. 제공자가 접근 토큰을 발행한다.
    7. 발근된 접근 토큰을 이용하여 소비자가 사용자 정보에 접근한다.
  • 고객, 고객이 이용하려는 애플리케이션, 고객 정보를 가지고 있는 애플리케이션 3개가 상호작용한다.
  • 구현이 복잡한 단점이 있다. 
    • HMAC(keyed-hash message authentication code, hash-based message authentication code)를 통한 암호화를 하는 번거로움
    • 인증토큰이 만료가 되지 않아 토큰을 만료하려면 제공자 애플리케이션의 비밀번호를 바꿔야 함

3) OAuth 2.0

  • OAuth 2.0 인증 과정

  • OAuth 2.0 프로세스

  • 1.0 과의 차이
    • 기능의 단순화, 기능과 규모의 확장성 등을 지원하기 위해 만들어졌다.
    • https를 통해 암호화를 하여 과정의 단수화를 하였다.
    • 다양한 인증 방식이 제공된다.
    • api서버에서 인증서버와 리소스 서버가 분리됐다.

3.  Test

  1) 테스트의 종류

  • 블랙박스 테스팅 : 소프트웨어 내부 구조나 동작원리를 모르는 블랙박스와 같은 상태에서, 즉 웹 서비스의 사용자 입장에서 동작을 검사하는 방법
    • 장점
      • 누구나 테스트가 가능합니다 - 개발자부터 디자이너, 베타 테스터 혹은 사장님까지!
    • 단점
      • 기능이 증가될 수록 테스트의 범위가 증가, 시간이 갈수록 테스트하는 사람이 계속 늘어나야함
      • 테스트 하는 사람에 따라 테스트 퀄러티가 다를 수 있습니다. → QA 직군이 있는 이유
  • 개발자 테스팅 : 개발자가 직접 '본인이 작성한 코드'를 검증하기 위해 "테스트 코드"를 작성
    • 장점
      • 빠르고 정확한 테스트가 가능합니다. (예상 동작 VS 실제 동작)
      • 테스트 자동화가 가능, 배포 절차 시 테스트 코드가 수행되어 동작 검증
      • 리팩토링이나 기능 추가를 할 때 더욱 편리합니다.
    • 단점
      • 개발 시간이 오래 걸림
      • 테스트 코드를 유지보수하는 비용

2) 단위 테스트

  • 프로그램을 작은 단위로 쪼개서 각 단위가 정확하게 동작하는지 검사하고 이를 통해 문제 발생 시 정확하게 어느 부분이 잘못되었는지를 재빨리 확인할 수 있게 해준다.

  • Development : 개발
  • Unit Test(단위 테스트) : 개발자 테스트
  • QA Testing: 블랙박스 테스팅, 주로 QA팀이 Production 환경과 유사한 환경(stage)에서 테스팅
  • Production : 실 서비스 운영 환경

3) TDD(Test-Driven-Development, 테스트 주도 개발)

  • TDD는 작가가 책을 쓰는 과정과 유사하다. 책을 쓸 때는 목차를 처음 구성한다. 이후 각 목차에 맞는 내용을 구상하여 초안을 작성하고 고쳐쓰기를 반복한다. 이 과정을 TDD에 비유하면 목차구성은 “테스트코드 작성”, 초안작성은 “코드개발”, 고쳐쓰기는 “코드수정”에 해당한다. 반복적인 검토와 고쳐쓰기를 통해서 좋은 글이 완성되는 것처럼 소프트웨어도 반복적인 테스트와 수정을 통해서 고품질의 소프트웨어를 만들 수 있다.
  • TDD를 하는 이유 : 불확실성이 높을 때 “피드백”과 “협력”이 중요하기 때문에 피드백과 협력이 자주 이루어진다면 더 좋은 결과가 나올 수 있다.
  • TDD를 시행하면 좋은 상황
    • 처음해보는 프로그램 주제-나에 대한 불확실성이 높은 경우
    • 고객의 요구조건이 바뀔 수 있는 프로젝트- 외부적인 불확실성이 높은 경우
    • 개발하는 중에 코드를 많이 바꿔야 된다고 생각하는 경우
    • 내가 개발하고 나서 이 코드를 누가 유지보수할지 모르는 경우- 외부적인 불확실성이 즉, 불확실성이 높을 때 TDD를 하면 된다.
  • TDD 개발 방식의 장점
    • 보다 튼튼한 객체 지향적인 코드 생산 
    • 재설계 시간의 단축
    • 디버깅 시간의 단축
    • 테스트 문서의 대체 가능
    • 추가 구현의 용이함

4) JUnit 단위 테스트

  • 자바 프로그래밍 언어 용 단위 테스트 프레임 워크
  • 테스트 파일을 생성하여 테스트를 하는 방식
  • 주로 원하는 Entity에서 Generate를 통해 Test 생성하여 진행
  • 엣지 케이스 : 알고리즘이 처리하는 데이터의 값이 알고리즘의 특성에 따른 일정한 범위를 넘을 경우에 발생하는 문제
    • ex)fixnum이라는 변수의 값이 -128 ~ 127의 범위를 넘는 순간 문제가 발생하는 경우가 있을 수 있다. 어떤 분모가 0이 되는 상황처럼 데이터의 특정값에 대해 문제가 발생하는 경우도 마찬가지다.
    • 알고리즘의 특성에 따라 개발자가 면밀히 검토하여 예상할 수 있는 문제다. 이런 문제는 디버그가 쉽기도 하고 테스트를 통해 미리 방지하기도 쉽다.
  • 코너 케이스 : 여러 가지 변수와 환경의 복합적인 상호작용으로 발생하는 문제다.
    • ex)fixnum이라는 변수의 값으로 128이 입력되었을 때, A 기계에서 테스트했을 때는 정상작동하지만 B 기계에서는 오류가 발생한다면 코너 케이스라고 할 수 있다. 같은 장치에서라도 시간이나 다른 환경에 따라 오류가 발생하기도 하고 정상작동 하기도 한다면 이것도 코너 케이스다.
    • 특히 멀티코어 프로그래밍에서 만나기 쉬운 오류일 것이다.
    • 코너 케이스는 오류가 발생하는 상황을 재현하기가 쉽지 않아 디버그와 테스트가 어렵다.

4.  Spring AOP

  • 핵심 기능 : 각 API 별 수행해야 할 비즈니스 로직
  • 부가 기능 : 핵심 기능을 보조 하는 기능
  • 문제점 
    • 모든 '핵심기능'의 Controller 에 '부가기능' 코드를 추가했을 때..
      • '핵심기능' 이 100개라면??
        • 100개의 '핵심기능' 모두에 동일한 내용의 코드 추가 필요
    • '핵심기능' 수정 시
      • 같은 함수 내에 '핵심기능'과 '부가기능'이 섞여 있음
      • '핵심기능' 이해를 위해 '부가기능'까지 이해 필요
    • '부가기능'의 변경이 필요하다면??
      • '핵심기능'의 개수만큼 '부가기능'도 수정해 줘야 함
      • '부가기능' 삭제
  • AOP (Aspect Oriented Programming) 를 통해 부가기능을 모듈화
    • 부가기능은 핵심기능과는 관점(Aspect), 관심이 다름
    • 따라서 '핵심기능'과 분리해서 '부가기능' 중심으로 설계, 구현 가능

 1) Spring AOP Annotation

  1. @Aspect
    • 스프링 빈 (Bean) 클래스에만 적용 가능 , 포인트컷과 어드바이스의 결합이다. 어떤 포인트컷 메소드에 대해 어떤 어드바이스 메소드를 실행할지 결정한다.
  2. 어드바이스 종류
    1. @Around: '핵심기능' 수행 전과 후 (@Before + @After)
    2. @Before: '핵심기능' 호출 전 (ex. Client 의 입력값 Validation 수행)
    3. @After: '핵심기능' 수행 성공/실패 여부와 상관없이 언제나 동작 (try, catch 의 finally() 처럼 동작)
    4. @AfterReturning: '핵심기능' 호출 성공 시 (함수의 Return 값 사용 가능)
    5. @AfterThrowing: '핵심기능' 호출 실패 시. 즉, 예외 (Exception) 가 발생한 경우만 동작 (ex. 예외가 발생했을 때 개발자에게 email 이나 SMS 보냄)
  3. 포인트컷 : 특정 조건에 의해 필터링된 조인포인트, 수많은 조인포인트 중에 특정 메소드에서만 횡단 공통기능을 수행시키기 위해서 사용한다.
    • 표현식 : 리턴타입 패키지경로 클래스명 메소드명(매개변수)
  4. 조인포인트 : 클라이언트가 호출하는 모든 비즈니스 메소드, 조인포인트 중에서 포인트컷되기 때문에 포인트컷의 후보로 생각할 수 있다.
  5. 포인트컷과 조인포인트의 사용법은 최하단 참조를 통해 이해하자

5.  트랜잭션

  • 트랜잭션이란 : 데이터베이스에서 데이터에 대한 하나의 논리적 실행단계, ACID (원자성, 일관성, 고립성, 지속성)는 데이터베이스 트랜잭션이 안전하게 수행된다는 것을 보장하기 위한 성질을 가리키는 약어
  • 트랜잭션의 특징 
    • 더 이상 쪼갤 수 없는 최소단위의 작업입니다.
    • 하나의 최소 단위의 작업에 여러가지 데이터 변경을 넣으면, 모두 저장되거나, 아무 것도 저장되지 않거나를 보장합니다.
  • 좋은 예시 : 아래 예시 단계에서 도중에 에러가 난다면 그저 A계좌만 손해가 날 것이다. 하지만 모든 단계를 하나의 트랜잭션으로 묶어 모두 성공시 트랜잭션 Commit, 중간에 하나라도 실패시 트랜잭션 Rollback으로 문제를 해결한다. 
    1. A계좌 잔고 200원이상 확인
    2. A계좌 잔고 200원 금액 감소
    3. B계좌 잔고 200원 증가
  • Primary와 Replica

  • Primary : 쓰기 전용이다. Write된 Data(CUD)가 Replica로 Sync 된다.(Replication)
  • Replica : 읽기 전용(readOnly = true)
  • Primary 중 1개에 문제가 생겼으 때 Replica중 1개가 primary가 된다.

 

6. 참조 

  1) Spring Security란? : https://mangkyu.tistory.com/76

 

[SpringBoot] Spring Security란?

대부분의 시스템에서는 회원의 관리를 하고 있고, 그에 따른 인증(Authentication)과 인가(Authorization)에 대한 처리를 해주어야 한다. Spring에서는 Spring Security라는 별도의 프레임워크에서 관련된 기능

mangkyu.tistory.com

 2)OAuth 1.0과 2.0 그리고 인증과 토큰의 종류 : https://velog.io/@hyg8702/OAuth%EB%9E%80-OAuth1-vs-OAuth2

 

👨‍💻OAuth란? & OAuth1 vs OAuth2

요즘 시중에 SW 부트캠프가 많다.그 중, 웹 개발 프로젝트를 진행하는 곳이 많은데 OAuth기능을 경험해본 웹 개발자 지망생들 또한 많다. 하지만 OAuth라고 했을때, 소셜로그인 정도로 알고 그치는

velog.io

3)TDD의 특징과 장단점 : http://clipsoft.co.kr/wp/blog/tddtest-driven-development-%EB%B0%A9%EB%B2%95%EB%A1%A0/

 

TDD(Test-Driven-Development) 방법론 - CLIPSOFT

작성자 : 강성웅 부장   TDD(Test-Driven-Development) 방법론에 대하여…     - TDD가 무엇 일까? TDD란 Test Driven Development의 약자로 ‘테스트 주도 개발’이라고 한다. 테스트 주도 개발(TDD)은 설계 이후

clipsoft.co.kr

4)포인트컷과 조인포인트 : https://sjh836.tistory.com/157

 

Spring AOP (개념, 용어, 원리, 포인트컷 표현식, JoinPoint API)

1. AOP란?Aspect Oriented Programming 의 약자로 관점지향 프로그래밍이라고 부른다. IoC가 낮은 결합도와 관련된 것이라면 AOP 는 높은 응집도와 관련되어 있다.서비스들의 비즈니스 메소드들은 복잡한

sjh836.tistory.com