inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)

hasIpAddress(), getRemoteAddr() 질문입니다.

458

작성자 없음

작성한 질문수 0

0

이전에 이런 질문들((https://www.inflearn.com/questions/423204))이 있던 것을 확인하였으나 저는 해결이 되지 않아 질문을 남깁니다.

postman으로 회원가입하고 로그인하려고 하면 401 Unauthorized가 나며 많이 보셨을 이런 에러가 나옵니다.

java.lang.UnsupportedOperationException: public abstract java.lang.String javax.servlet.ServletRequest.getRemoteAddr() is not supported

....

이와 같은 에러의 질문과 답변을 보고 hasIpAddress()에서 .access("hasIpAddress('" + IP + "')")로 바꿔서 해보았는데도 또는 스프링 부트 버전을 바꾸어 시도해(2.6.2, 2.4.2 등)보았으나 해결이 되지 않아 질문을 드립니다.

처음 설정한 spring boot 버전은 2.7.15입니다.

build.gradle

plugins {
    id 'java'
    id 'org.springframework.boot' version '2.7.15'
    id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
    sourceCompatibility = '11'
}

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

ext {
    set('springCloudVersion', "2021.0.8")
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    runtimeOnly 'com.h2database:h2:1.3.176'

    implementation 'org.springframework.boot:spring-boot-starter-validation'
    implementation 'org.modelmapper:modelmapper:3.1.1'

    implementation 'org.springframework.boot:spring-boot-starter-security'
    testImplementation 'org.springframework.security:spring-security-test'


    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

tasks.named('test') {
    useJUnitPlatform()
}

WebSecurity은

@Configuration
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {

    private final Environment environment;
    private final UserService userService;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;

     private String IP = "192.168.1.2";

    @Autowired
    public WebSecurity(Environment environment, UserService userService, BCryptPasswordEncoder bCryptPasswordEncoder) {
        this.environment = environment;
        this.userService = userService;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    }

   

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();

        http.authorizeRequests()
                .antMatchers("/**")
//                .hasIpAddress(IP)
                .access("hasIpAddress('" + IP + "')")
                .and()
                .addFilter(getAuthenticationFilter());

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

    private AuthenticationFilter getAuthenticationFilter() throws Exception {
        AuthenticationFilter authenticationFilter =
                new AuthenticationFilter(authenticationManager(), userService, environment);
        return authenticationFilter;
    }

    // db password와 input password 비교
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder);
    }

}

2.7 이후 버전에서는 https://www.inflearn.com/chats/789887와 같이 WebSecurityConfigurerAdapter 를 상속하지 않고 configure함수 오버라이드 대신 SecurityFilterChain을 사용해보았으나 이렇게 해도 되지 않았습니다.

AuthenticationFilter에 unsuccessfulAuthentication를 오버라이드하여 확인해본 결과

org.springframework.security.authentication.BadCredentialsException: 자격 증명에 실패하였습니다.
	at org.springframework.security.authentication.dao.DaoAuthenticationProvider.additionalAuthenticationChecks(DaoAuthenticationProvider.java:80)
	at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:147)
	at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:182)
	at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:201)
	at com.example.userservice.filter.AuthenticationFilter.attemptAuthentication(AuthenticationFilter.java:46)

라고 오류가 나오는 것을 확인했습니다.

AuthenticationFilter.java:46 부분은 return getAuthenticationManager().authenticate( 부분 입니다.

AuthenticationFilter 코드는 다음과 같습니다.

@Slf4j
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final UserService userService;
    private final Environment environment;

    public AuthenticationFilter(AuthenticationManager authenticationManager,
                                UserService userService,
                                Environment environment) {
        super.setAuthenticationManager(authenticationManager);
        this.userService = userService;
        this.environment = environment;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException {
        try {
            RequestLogin creds = new ObjectMapper().readValue(request.getInputStream(), RequestLogin.class);

            return getAuthenticationManager().authenticate(
                    new UsernamePasswordAuthenticationToken(
                            creds.getEmail(), creds.getPassword(), new ArrayList<>()
                    )
            );
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {
//        log.debug(((User)authResult.getPrincipal()).getUsername());
        String userName = ((User)authResult.getPrincipal()).getUsername();
        UserDto userDetails = userService.getUserDetailsByEmail(userName);
        System.out.println("details :" + userDetails);

        String token = Jwts.builder()
                           .setSubject(userDetails.getUserId())
                           .setExpiration(
                                   new Date((System.currentTimeMillis() + Long.parseLong(environment.getProperty("token.expiration_time"))))
                           )
                           .signWith(SignatureAlgorithm.HS512, environment.getProperty("token.secret"))
                           .compact();

        System.out.println("token :" + token);
        response.addHeader("token", token);
        response.addHeader("userId", userDetails.getUserId());
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request,
                                              HttpServletResponse response,
                                              AuthenticationException failed) throws IOException, ServletException {
//        super.unsuccessfulAuthentication(request, response, failed);
        failed.printStackTrace();
    }
}

 

어느 부분을 어떻게 수정하여 진행을 해야 할까요?

 


AuthenticationFilter에서 attemptAuthentication 함수를 UsernamePasswordAuthenticationToken 받는 것으로 수정하여 디버깅해보니 아래 코드의 authenticationToken 값은 잘 나옵니다. 그런데 위의 질문처럼 getAuthenticationManager().authenticate 여기서 문제가 발생합니다.

문제가 발생하는 부분은 AbstractAuthenticationProcessingFilter의 저 부분으로 잡히며AuthenticationFilter 코드는 다음과 같습니다.

import com.example.userservice.dto.UserDto;
import com.example.userservice.service.UserService;
import com.example.userservice.vo.RequestLogin;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;

@Slf4j
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final UserService userService;
    private final Environment environment;

    public AuthenticationFilter(AuthenticationManager authenticationManager,
                                UserService userService,
                                Environment environment) {
        super.setAuthenticationManager(authenticationManager);
        this.userService = userService;
        this.environment = environment;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException {
        try {
            RequestLogin creds = new ObjectMapper().readValue(request.getInputStream(), RequestLogin.class);

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

            return getAuthenticationManager().authenticate(authenticationToken);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {
//        log.debug(((User)authResult.getPrincipal()).getUsername());
        String userName = ((User)authResult.getPrincipal()).getUsername();
        UserDto userDetails = userService.getUserDetailsByEmail(userName);
        System.out.println("details :" + userDetails);

        String token = Jwts.builder()
                           .setSubject(userDetails.getUserId())
                           .setExpiration(
                                   new Date((System.currentTimeMillis() + Long.parseLong(environment.getProperty("token.expiration_time"))))
                           )
                           .signWith(SignatureAlgorithm.HS512, environment.getProperty("token.secret"))
                           .compact();

        System.out.println("token :" + token);
        response.addHeader("token", token);
        response.addHeader("userId", userDetails.getUserId());
    }

//    @Override
//    protected void unsuccessfulAuthentication(HttpServletRequest request,
//                                              HttpServletResponse response,
//                                              AuthenticationException failed) throws IOException, ServletException {
////        super.unsuccessfulAuthentication(request, response, failed);
//        failed.printStackTrace();
//    }
}

 

해결 방법 좀 알려주시면 감사하겠습니다.

spring-boot jpa 아키텍처 spring-cloud kafka msa

답변 1

-3

인프런 AI 인턴

안녕하세요, 인프런 AI 인턴이에요.

postman으로 회원가입하고 로그인 시 401 Unauthorized 에러가 발생하며 해당 에러의 원인은 다음과 같습니다.

에러 메시지인 “java.lang.UnsupportedOperationException: public abstract java.lang.String javax.servlet.ServletRequest.getRemoteAddr() is not supported”는 Spring MVC에서 HttpServletRequest의 getRemoteAddr() 메서드를 사용하려고 하지만, 이 메서드가 지원되지 않는 경우 발생합니다.

보통 이런 경우는 Spring Security의 hasIpAddress() 메서드를 사용하여 IP 주소를 검사할 때 발생할 수 있습니다.

그러나 해당 에러가 발생하는 이유와 해결 방법에 대해서는 자세한 코드나 설정 정보를 제공해주셔야 합니다. 현재는 build.gradle 파일의 일부 정보만 제공되었기 때문에 정확한 답변을 드리기 어렵습니다.

따라서, 아래의 정보를 함께 제공해주시면 더 정확한 답변을 드릴 수 있을 것입니다.
- 해당 에러가 발생하는 코드 부분
- WebSecurityConfigurerAdapter를 상속받은 클래스에서 어떻게 hasIpAddress() 메서드를 사용하고 있는지 코드 예시
- 사용 중인 Spring Security 버전

추가로, 해당 에러의 원인을 파악하기 위해 로그를 확인해보시는 것도 도움이 될 수 있습니다. 로그에서 더 자세한 에러 메시지와 스택 트레이스를 확인해보시면 원인을 찾을 수 있을 것입니다.

더 자세한 도움이 필요하시다면, 코드나 설정 정보를 더 자세하게 제공해주시거나 Spring Security 관련 문서를 참고하시면 됩니다. 참고로, 인프런에서는 Spring Security 강의도 제공하고 있으니 참고하시면 도움이 될 수 있을 것입니다.

다른 질문이 있으면 언제든지 물어보세요. 좋은 하루 되세요~요.

kafka 업데이트 강의 듣고 시포요

0

85

1

강의 교안

0

73

1

마이크로서비스간 통신 시, 인증 처리

0

79

1

api gateway 에서 인증 처리

0

65

1

섹션 19 질문드립니다

0

53

1

강의 자료 업데이트

0

82

1

부하분산 강의 섹션

0

57

1

강의자료는 어디에서?

0

71

1

강의 자료는 어디서 다운 받을 수 있나요?

0

110

1

전체 사용자 조회시 오류

0

58

1

혹시 pk 외 별도의 id 를 부여한 이유가 있을까요 ??

0

110

2

학습 방향

0

95

2

카프카 커넥터 사용 목적 문의

0

85

2

kafka 강의

0

108

2

서비스 디스커버리 종류

0

87

2

강의 자료에 대해서 궁금해요

0

116

2

GlobalFilter, LoggingFilter가 동작하지 않습니다.

0

90

2

Kafka Source Connect 버전 에러

0

85

2

소스커넥터는 사용안한 거 맞죠?

0

81

2

강의자료 업데이트 문의

0

95

2

강의에서 BCryptPasswordEncoder 에 역할(5-2)

0

56

1

강의 업데이트 계획이 궁금합니다.

0

112

2

MSA 애플리케이션에 Spring Web과 Spring Data JPA를 사용하는 것이 바람직한지 궁금합니다. (MSA 설계와 관련된 질문입니다)

0

161

2

어떤 것이 업데이트 된 건가요?

0

164

2