Spring - Bean, 인증과 인가

김상엽·2024년 1월 30일
0

Spring

목록 보기
8/26
post-thumbnail

TIL

Bean

자동으로 Bean 등록하기

  • @Component를 사용하면 @ComponentScan에 의해 자동으로 스캔되어 해당 클래스를 Bean으로 자동으로 등록해준다.
  • 비즈니스 로직과 관련된 클래스들은 그 수가 많기 때문에 @Controller, @Service와 같은 애너테이션들을 사용해서 Bean으로 등록하고 관리하면 개발 생산성에 유리하다.

수동으로 Bean 등록하기

이러한 자동의 장점들이 있음에도 수동으로 Bean을 등록해야 할때도 있다.

  • 기술적인 문제나 공통적인 관심사를 처리할 때 사용하는 객체들을 수동으로 등록하는 것이 좋다.
@Configuration
public class PasswordConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
  • Bean으로 등록하고자하는 객체를 반환하는 메서드를 선언하고 @Bean을 설정한다.
  • Bean을 등록하는 메서드가 속한 해당 클래스에 @Configuration을 설정한다.
  • Spring 서버가 뜰 때 Spring IoC 컨테이너에 'Bean'으로 저장된다.

같은 타입 Bean 처리하기

자동이던 수동이던 Bean을 등록하다가 같은 타입의 Bean이 등록되면 어떻게 될까?
테스트를 위해 Food 타입의 pizza와 chicken을 등록하고 사용하려고 하면 다음과 같은 에러가 발생한다.

Food food; 필드에 @Autowired를 사용하여 Bean 객체를 주입려고 했으나, Food 타입의 Bean 객체가 하나 이상 있기때문에 어떤 Bean을 주입해줘야할지 몰라서 발생한 오류이다.
이 오류를 해결하는 방법을 알아보자.

1. 등록된 Bean 이름 명시하기

    @Autowired
    Food pizza;

    @Autowired
    Food chicken;

이렇게 등록된 Bean의 이름을 정확하게 명시해주면 해결할 수 있다.

2. @Primary 사용하기

@Component
@Primary
public class Chicken implements Food {
    @Override
    public void eat() {
        System.out.println("치킨을 먹습니다.");
    }
}

@Primary가 추가되면 같은 타입의 Bean이 여러 개 있더라도 우선 @Primary가 설정된 Bean 객체를 주입 해준다.

3. @Qualifier 사용하기

@Component
@Qualifier("pizza")
public class Pizza implements Food {
    @Override
    public void eat() {
        System.out.println("피자를 먹습니다.");
    }
}

@SpringBootTest
public class BeanTest {

    @Autowired
    @Qualifier("pizza")
    Food food;
}
  • @Qualifier@Primary와 동시에 적용되어 있어도 @Qualifier의 우선순위가 더 높아 @Qualifier가 적용된다.
  • 하지만 Bean을 주입 받고자 하는 곳에도 @Qualifier를 추가해야한다는 번거로움이 있다.
  • 따라서 같은 타입의 Bean이 여러 개 있을 때는 범용적으로 사용되는 Bean 객체에는 @Primary를 설정하고 지엽적으로 사용되는 Bean 객체에는 @Qualifier를 사용하는 것이 좋다.

인증과 인가

인증(Authentication)

  • 인증은 해당 유저가 실제 유저인지 인증하는 개념이다.
  • 지문인식, 로그인 등과 같이, 실제 그 유저가 맞는지를 확인하는 절차이다.

인가(Authorization)

  • 인가는 해당 유저가 특정 리소스에 접근이 가능한지 허가를 확인하는 개념이다.
  • 관리자 페이지, 관리자 권한 같은 것들을 예로 들 수 있다.

쿠키-세션 방식의 인증

    1. 사용자가 로그인 요청을 보낸다.
    1. 서버는 DB의 유저 테이블과 아이디 비밀번호를 대조한다.
    1. DB의 유저테이블의 정보와 일치한다면 인증을 통과한 것으로 보고 “세션 저장소”에 해당 유저가 로그인 되었다는 정보를 넣는다.
    1. 세션 저장소에서는 유저의 정보와는 관련 없는 난수인 session-id를 발급한다.
    1. 서버는 로그인 요청의 응답으로 session-id를 내어준다.
    1. 클라이언트는 그 session-id를 쿠키라는 저장소에 보관하고 앞으로의 요청마다 세션아이디를 같이 보낸다.
    1. 클라이언트의 요청에서 쿠키를 발견했다면 서버는 세션 저장소에서 쿠키를 검증한다.
    1. 세션 저장소에서 유저정보를 받아온다.
    1. 유저정보에 따른 응답을 내어준다.
public static void addCookie(String cookieValue, HttpServletResponse res) {
    try {
        cookieValue = URLEncoder.encode(cookieValue, "utf-8").replaceAll("\\+", "%20");
        // Cookie Value 에는 공백이 불가능해서 encoding 진행

        Cookie cookie = new Cookie(AUTHORIZATION_HEADER, cookieValue); // Name-Value
        cookie.setPath("/");
        cookie.setMaxAge(30 * 60);

        // Response 객체에 Cookie 추가
        res.addCookie(cookie);
    } catch (UnsupportedEncodingException e) {
        throw new RuntimeException(e.getMessage());
    }
}
  • 클라이언트에 저장될 목적으로 생성한 작은 정보를 담은 파일이다.
  • 클라이언트 (웹 브라우져)에 저장된다.
  • 쿠키 저장 시 만료일시 설정 가능하다. 브라우져가 종료되어도 유지된다.
  • 클라이언트에 저장되기 때문에 보안에 취약하다.

세션 (Session)

@GetMapping("/create-session")
public String createSession(HttpServletRequest req) {
    // 세션이 존재할 경우 세션 반환, 없을 경우 새로운 세션을 생성한 후 반환
    HttpSession session = req.getSession(true);

    // 세션에 저장될 정보 Name - Value 를 추가
    session.setAttribute(AUTHORIZATION_HEADER, "Robbie Auth");

    return "createSession";
}

@GetMapping("/get-session")
public String getSession(HttpServletRequest req) {
    // 세션이 존재할 경우 세션 반환, 없을 경우 null 반환
    HttpSession session = req.getSession(false);

    String value = (String) session.getAttribute(AUTHORIZATION_HEADER); 
    // 가져온 세션에 저장된 Value 를 Name 을 사용하여 가져옴
    System.out.println("value = " + value);

    return "getSession : " + value;
}
  • 서버에서 일정시간 동안 클라이언트 상태를 유지하기 위해 사용한다.
  • 웹 서버에 저장된다.
  • 다음 조건 중 하나가 만족될 경우 만료된다.
    • 브라우져 종료 시까지
    • 클라이언트 로그아웃 시까지
    • 서버에 설정한 유지기간까지 해당 클라이언트의 재요청이 없는 경우
  • 웹 서버에 저장되기 때문에 상대적으로 보안이 안전하다.

오늘의 회고

오늘은 백엔드의 핵심 중 하나인 로그인부분을 공부했다.
그동안 프로젝트 하면서 로그인쪽은 맡아보지 않았었는데 이런식으로 동작하는구나~ 하면서 재밌게 공부했다. 매번 새로운걸 배워서 그런지 재밌다 :)

profile
개발하는 기록자

0개의 댓글