TIL - 0717

sql

테이블 생성, 수정하기

  • TINYINT와 INT 타입 : 가격 데이터를 저장해야하는데, TINYINT는 -127 ~ 128까지 저장가능함 -> 데이터 타입을 변경해야함, INT(21억)
    • 늘릴 수 있지만 줄일 수 없음
> CREATE TABLE ITEM (
    ITEM_ID INT AUTO_INCREMENT,
    NAME VARCHAR(50) NOT NULL,
    PRICE TINYINT DEFAULT 0,
    QUANTITY INT DEFAULT 0
    PRIMARY KEY (ITEM_ID);
  );
  
> ALTER TABLE ITEM MODIFY COLUMNN PRICE INT DEFAULT 0;

SELECT - ORDER BY

  • 정렬하기 : ORDER BY 열 DESC ASC
    • 복수열 정렬 가능
    • 데이터 타입에 따라 정렬 방식이 다름 : 정수 - 크기, 문자 - 사전 순서(같은 3을 저장하더라도 데이터 타입에 따라 다르게 정렬됨)
    • NULL을 가진 경우에는 데이터베이스 제품에 따라 가장 먼저 표시되거나 가장 나중에 표시됨
> SELECT * FROM MEMBER ORDER BY MEMBER_ID, NAME DESC;
> SELECT * FROM MEMBER ORDER BY MEMBER_ID ASC, NAME DESC;

SELECT - LIMIT

  • 조회 데이터의 행의 수를 제한
    • mysql, postgresql에서만 사용가능 : 표준 SQL이 아님(oracle은 ROWNUM을 사용 - LIMIT과는 달리 다 찾아온 후 설정된 개수만큼만 추리는 것인가봄, 서브쿼리)
    • 오프셋 지정가능 : 시작 시점을 지정가능함, 페이지네이션 구현할 때 유용할 듯, 배열 인덱스 처럼 사용(PK - 1), 기본값 0
> SELECT * FROM MEMBER LIMIT 3;;;gtt
> SELECT * FROM MEMBER WHERE TEAM_ID IS NOT NULL ORDER BY NAME DESC LIMIT 2;
> SELECT * FROM MEMBER LIMIT 3 OFFSET 0;

SELECT - 연산, Alias 지정

  • 연산 한 후 그 결과를 가지고 새로운 열을 만듦 : AS로 별명 지정
    • WHERE 구문에 TOTAL을 가지고 조건을 만들 수 없음(그렇게 처리하고있는 데이터베이스가 많음) : WHERE 실행 -> SELECT 실행이라서 없는 열(ORDER BY는 사용가능 : SELECT 실행 이후에 실행됨)
    • NULL 연산의 결과는 NULL
> SELECT PRICE * QUANTITY AS TOTAL FROM ITEM;
+---------+
| total   |
+---------+
|    5000 |
|   10000 |
| 1900000 |
| 7194500 |
+---------+ 
> SELECT PRICE * QUANTITY AS TOTAL FROM ITEM WHERE ITEM_ID >= 3;
> SELECT PRICE * QUANTITY AS TOTAL FROM ITEM WHERE TOTAL > 9000;
ERROR 1054 (42S22): Unknown column 'TOTAL' in 'where clause'

> SELECT PRICE * QUANTITY AS TOTAL FROM ITEM ORDER BY TOTAL DESC;
  • 함수 사용해서 연산하기 : MOD(나머지 연산), ROUND(소수점 반올림)

spring security

security 역할

  • 인증과 인가 작업 관련 코드 작업을 편하게 할 수 있게 만들어진 스프링 프레임워크의 모듈

인증과 인가의 차이

  • 인증 : 사용자임을 인증
  • 인가 : 사용자 중 허가 받은 사용자만이 해당 기능을 사용할 수 있는데, 그 권한이 있는지 없는지 체크

인증 관련 개념

Implicit Grant Flow

  • 클라이언트가 인증서버에 요청을 해서 인증정보를 받고 인증정보(토큰)를 서버로 전송, 토큰을 가지고 서버가 인증서버에 토큰 확인 요청함 인증서버가 성공/실패를 응답해주고 서버가 그결과를 가지고 jwt를 만들어 클라이언트에 응답해줌
    • 클라이언트가 기본적으로 인증 토큰을 받아오기때문에 서버의 오버헤드가 훨씬 줄어듬 : 인증과 관련해서 확인하고 jwt만 만들어주면 됨

JWT

  • 쿠키와 세션 / JWT 어떤 차이가 있나?
    • 보안성이 뛰어나기보다 서버의 부하를 줄여주는 의미 ————– 더쓰라 ——————
  • json web token : json으로 만들어진 웹에서 사용되는 토큰(서버가 인증된 사용자에게 응답해주는 토큰 - 다음부터는 토큰이 만료되지만 않았다면 해당 토큰으로 인증가능)
    • 총 3개 부분으로 나뉨 : “.”으로 구분하고, 각각 Header(JOSE - JSON Object Signing and Encryption), JWT Claim Set, Signature임
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
  • <Header, Claim Set, Signature>
  • Header(JOSE) : json 암호화 알고리즘, 타입(jwt), Base64로 단순 인코딩
  • Claim Set : body(key-value), 권장하는 컨텐츠가 있음(만료기간 등), Base64로 단순 인코딩(최소한의 정보를 담아야함), 토큰 정보를 클레임이라고 부르는데 그런 정보들의 모음을 클레임셋이라고 함
  • Signature : 헤더와 클레임셋이 위조되었는지를 판단하는 시그니쳐 부분 - 해당 부분으로 jwt가 유효(위조 여부)한지 알 수 있음, 헤더에 적힌 암호화 알고리즘과 비밀키로 생성(비밀키는 절대 하드코딩하면 안되고, 따로 파일로 관리하고 있으면서 주입하는 방식으로 사용해야함)

  • jwt 라이브러리를 사용하면 됨 : mvn repo : java-jwt
    • 디펜던시 추가 후 JWTCreator 클래스 살펴보면 됨
  • Signature가 유효한지 판단 후, Claim Set에 포함된 만료기간이 유효하면 인증됨 : 조회없이 인증할 수 있음(jwt만 발급해주면) - 무상태

인증 관련 스프링 컴포넌트

필터

  • Request, Response에 대한 전/후처리 작업을 하는 역할
  • 필터를 하나만 넣는게 아니라 필터 체인 개념으로 여러 필터를 걸어둘 수 있음 : 인증에 필요한 설정을 필터에서 설정함
    • 인증 객체(? extends Authentication)를 만듦
    • 폼 로그인, 소셜로그인 flow 단위에 따라 필터를 만듦

Authentication Provider Manager

  • 인증서버 구현하고 제공하는 제공자를 담고 있는 매니져
  • 관련 스프링 인터페이스와 구현체 : AuthenticationManager, ProviderManager
    • AuthenticationProvider 리턴(forEach를 돌면서 적합한 프로바이더 찾음) : 실제 인증을 하는 객체 - 인증 전 객체를 받아 인증 후 객체를 만들어 돌려주던지, 예외를 던짐(인증 서버 컨택을 할 수도 있고), 얘를 구현해서 인증 작업을 해야함, 구현을 하고 매니져에 등록하면 됨
    • 매니져는 직접 구현하지않음
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
    @Override
    public Authentication authenticate(Authentication authenticatio) throws AuthenticationException {
        .
        .
        .
    }

    @Override
    public boolean supports(Class<?> authentication) {
        .
        .
        .        
    }
}

Provider

  • 인증 작업을 처리하는 객체

Authentication(Token)

  • 인증 객체 전/후 객체 : 토큰 - 단순히 생성자의 파라미터 개수로 인증 전 후 객체를 나누기때문에 래핑 클래스를 만드는게 더 좋네
  • Principal : 인증의 시작점이 되는 것을 통칭, userId를 가지고 인증 대상을 찾는다면 userName이 Principal
  • Credential : 인증의 키, userId가 Principal이라면 password가 Credential
  • isAuthenticated() : 인증 전(기본값 false)에는 false, 인증 후에는 true(권한 객체를 리스트로 가짐)로
    • 인증 관련 flow를 보면 처음에는 Authentication 객체를 생성하지만 인증 전 객체이고, Provider를 통해서 인증이 되면 인증된 객체를 생성해서 리턴하면 됨
    • 생성자에 따라 isAuthenticated()의 기본값이 다름 : Authentication 구현 객체를 보기 - UsernamePasswordAuthenticationToken

Security Context

  • 쓰레드당 할당되는 인증된 객체 관리자
  • 아직 정확히 모르겠음

환경세팅

  • jwt 발급을 위한 디펜던시 추가
  • 인텔리j가 제공하는 http 리퀘스트 파일 작성 : postman 사용없이 리퀘스트를 간편하게 만들 수 있음
/* login-request.http */
POST http://localhost:8080/login
Content-Type: application/json

{"id":"colin", "password":"1234"}

구현

전체 흐름

  1. 유저가 요청한 인증 요청 정보가 PreAuthentication에 담김 : 필터에 의해
  2. ProviderManager가 Provider를 찾음
  3. Provider가 PreAuthenticationToken 객체를 받음
  4. AccountService에 Account 정보를 요청을 함 : PreAuthenticationToken에 담긴 userId를 가지고 찾음
  5. PasswordEncoder를 통해서 AccountService에서 찾아온 Account와 PreAuthenticationToken에 담긴 password 정보를 비교함
  6. 맞으면 인증객체(PostAuthenticationToken)를 돌려줌
  7. 필터에서 후처리를 함 : 성공 시 jwt 생성, 실패 시 클라이언트에 알려줄 수 있도록 함

구현할 것

  • 유저 모델 추상화 : User를 이름으로 사용하지말고 Account로 사용 - User라는 이름을 가진 클래스가 많음(심지어 인증과정에서 사용할 User 스프링 클래스가 있음)
  • 유저 repo 생성 : 이름과 소셜ID로 유저를 검색할 수 있도록 메소드 선언
  • 인증관련 컴포넌트 만들기
    • 모델 객체정보를 담을 객체와 인증 과정에서 사용할 DTO 개념의 UserDetails를 구현한 User를 상속하는 클래스 정의
    • 필터 : 인증 요청 시 PreAuthenticationToken 생성과 프로바이더에 요청 - 결과에 대해서 어떻게 처리할 것인지(성공 - jwt, 실패 - 실패 정보 알리기)를 처리하는 필터
    • 토큰(인증 전/후) : Principal, Credential, (GrantedAuthority) - UsernamePasswordAuthenticationToken을 상속하는 클래스 정의
    • 인증 프로바이더 객체 생성 : Form, SocialLoginAuthenticationProvider
    • 인증 서비스 구현 : 서비스 계층 - Repository에 요청하고, 결과 응답해주고
    • 인증 관련 빈 생성 : 패스워드 인코더
  • 모델객체를 인증과정에서 처리하는 방법
    • 모델 객체를 그대로 사용하기
    • User(스프링 제공 UserDetails 구현체) 사용 - 스프링에서 제공하는 프로바이더에서 기본적으로 사용(DaoAuthentication 코드 참고), 구현체를 extends해서 커스텀(모델 객체 -> UserDetails 타입으로 변경), DTO 개념으로

JPA

@Enumerated

  • Enum을 데이터베이스에 저장할 때 어떻게 저장할지 선택하는 어노테이션
    • Ordinary : 선언되어있는 순서를 저장, 단점은 이미 저장되어있는 데이터가 있고 Enum에 선언된 상수의 순서가 변경되었을 때 어떻게 할건가? 확장성을 가지지 못함
    • String : Enum.value() 리턴 값 - 선언된 이름