2 분 소요

저번에 만들었던 user-service에 인증 기능을 추가해보자.

user-service

AuthenticationFilter 추가

아래 두 가지 메서드를 재정의(override) 할 것이다.

  • attemptAuthentication()
  • successfulAuthentication()
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws
        AuthenticationException {

        try {
            RequestLogin creds = new ObjectMapper().readValue(request.getInputStream(), RequestLogin.class);

            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                new UsernamePasswordAuthenticationToken(
                    creds.getEmail(),
                    creds.getPassword(),
                    new ArrayList<>()
                );

            return getAuthenticationManager().authenticate(usernamePasswordAuthenticationToken);

        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
        Authentication authResult) throws IOException, ServletException {
    }
}

attemptAuthentication

request.getInputStream()으로 받은 이유는
전달하고자하는 로그인의 값은 POST 형태로 전달되는데, POST 형태로 전달되는 값은 request parameter로 받을 수 없기 때문에
inputStream을 사용하면 수작업으로 어떤 데이터가 들어왔는지 처리할 수 있다.

successfulAuthentication()

사용자가 입력한 아이디와 패스워드 값이 일치했을 때, 어떤 처리를 해줄 것인지, (ex. 토큰 생성)
토큰의 만료시간을 어떻게 설정할건지,
사용자가 로그인 했을 때 반환값으로 어떤걸 줄건지… 등을 정의한다.

WebSecurity 변경

@Configuration
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http.authorizeRequests()
            .antMatchers("/**")
            .hasIpAddress("192.168.x.x") // 🌟 본인 IP 변경
            .and()
            .addFilter(getAuthenticationFilter());

        http.headers().frameOptions().disable();
    }

    private AuthenticationFilter getAuthenticationFilter() throws Exception {
        AuthenticationFilter authenticationFilter = new AuthenticationFilter();
        authenticationFilter.setAuthenticationManager(authenticationManager());

        return authenticationFilter;
    }
}

loadUserByUsername() 구현

UserService를 먼저 변경한 뒤, UserServiceImplloadUserByUsername()을 구현한다.

UserService

public interface UserService extends UserDetailsService { // 🌟 UserDetailsService 상속
    ...
}

UserServiceImpl

@Service
public class UserServiceImpl implements UserService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserEntity userEntity = userRepository.findByEmail(username);
        if (userEntity == null) {
            throw new UsernameNotFoundException(username);
        }
        return new User(
            userEntity.getEmail(),
            userEntity.getEncryptedPwd(),
            true,
            true,
            true,
            true,
            new ArrayList<>()
        );
    }
    ...
}

gateway-service

application.yml

server:
  port: 8000

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:8761/eureka

spring:
  application:
    name: gateway-service
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://USER-SERVICE
          predicates:
            - Path=/user-service/login # 🌟 로그인 엔드포인트 등록
            - Method=POST
          filters:
            - RemoveRequestHeader=Cookie
            - RewritePath=/user-service/(?<segment>.*), /$\{segment}

        - id: user-service
          uri: lb://USER-SERVICE
          predicates:
            - Path=/user-service/users # 🌟 회원가입 엔드포인트 등록
            - Method=POST
          filters:
            - RemoveRequestHeader=Cookie
            - RewritePath=/user-service/(?<segment>.*), /$\{segment}

        - id: user-service
          uri: lb://USER-SERVICE
          predicates:
            - Path=/user-service/** # 🌟 /user-service/** 등록
            - Method=GET
          filters:
            - RemoveRequestHeader=Cookie
            - RewritePath=/user-service/(?<segment>.*), /$\{segment}

        - id: catalog-service
          uri: lb://CATALOG-SERVICE
          predicates:
            - Path=/catalog-service/**

        - id: order-service
          uri: lb://ORDER-SERVICE
          predicates:
            - Path=/order-service/**

테스트

해당 마이크로 서비스에 할당된 포트번호로 직접 요청하려면 아래와 같이 요청하면 된다.
http://localhost:60910/login
이 때, localhost: 뒤에는 할당된 랜덤 프트를 적으면 되고, prefix는 필요없다.

api gateway를 통해 요청하려면 아래와 같이 요청하면 된다.
http://localhost:8000/user-serivce/login
이때는 /user-service라는 prefix를 붙여야 한다.

🤔 생각해보니 /login 엔드포인트는 컨트롤러에 따로 정의하지 않았는데 어떻게 가능할까?
/login 엔드포인트는 Spring Security를 사용하게되면 기본적으로 제공하는 엔드포인트이다.
하지만 /login 엔드포인트를 직접 정의해 추가적인 설정을 할 수도 있다.



💛 개인 공부 기록용 블로그입니다. 👻

맨 위로 이동하기