작성자 없음
작성자 정보가 삭제된 글입니다.
23.09.15 22:24 작성
·
356
·
수정됨
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();
// }
}
해결 방법 좀 알려주시면 감사하겠습니다.
답변 1
-3
안녕하세요, 인프런 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 강의도 제공하고 있으니 참고하시면 도움이 될 수 있을 것입니다.
다른 질문이 있으면 언제든지 물어보세요. 좋은 하루 되세요~요.