강의

멘토링

커뮤니티

인프런 커뮤니티 질문&답변

볼빵빵오춘기님의 프로필 이미지
볼빵빵오춘기

작성한 질문수

새로운 Authentication 객체 생성 코드 작성 시 error

작성

·

287

0

안녕하세요.

spring security 를 이용하여 회원가입을 하고 회원수정부분을 하고 있는 중입니다.

문제는 UserController.java 에서

Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userDto.getUsername(), userDto.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);

이 코드를 작성하면

error 메세지가 아래와 같이 뜹니다;;

2024-02-27T15:58:28.207+09:00 WARN 85046 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'PUT' is not supported]

수정페이지에서 버튼을 눌렀을 경우 js에서 put으로 잘보낸것같고 @PutMapping으로 잘 받은것 같은데 뭐가 문제일까요..?

 

UserController에서 위의 코드를 지우면 error메세지는 나오지않지만 authentication 객체정보가 변경되지않아 update.html에 변경된 정보로 나오지않고 있습니다.;;

 

update.html

<div class="row justify-content-center" sec:authorize="isAuthenticated()">
<section>
<div class="col-12 mb-30 text-center">
<h3>회원수정</h3>
</div>
<div class="col-12 mb-15">
<form>
<input type="text" name="id" Id="id" placeholder="id" th:value="${#authentication.principal.id}" >
<input type="text" name="role" Id="role" placeholder="role" th:value="${#authentication.principal.role}" >
<input type="text" name="provider" Id="provider" placeholder="provider" th:value="${#authentication.principal.provider}" >
<input type="text" name="providerId" Id="providerId" placeholder="providerId" th:value="${#authentication.principal.providerId}" >
<div class="form-group">
<label for="username">아이디</label>
<input type="text" name="username" Id="username" placeholder="id" th:value="${#authentication.principal.username}" pattern="[A-Za-z]+" >
</div>

<div class="form-group">
<label for="password">비밀번호</label>
<input type="password" name="password" id="password" placeholder="password" onkeyup="pwCheck()">
<div id="pw-result">
5자 이상이어야합니다.
</div>
</div>

<div class="form-group">
<label for="email">이메일</label>
<input type="email" name="email" id="email" placeholder="email" th:value="${#authentication.principal.emial}" readonly>
</div>

<div class="form-group">
<label for="email">닉네임</label>
<input type="text" name="nickname" id="nickname" placeholder="nickname" th:value="${#authentication.principal.nickname}" onkeyup="nicknameCheck()" required>
<div id="nickname-result">
3~8글자여야합니다.
</div>
</div>


</form>
<div class="text-center">
<button class="btn bg_03A3F1 color-fff" id="btn-update">회원수정</button>
</div>
</div>
</section>
</div>

 

update.js

let index = {
init: function(){

$("#btn-update").on("click", ()=>{ // function(){} , ()=>{} this를 바인딩하기 위해서!!
this.update();
});
},



update: function(){
//alert('user의 save함수 호출됨');
let data = {
id: $("#id").val(),
username: $("#username").val(),
password: $("#password").val(),
email: $("#email").val(),
nickname: $("#nickname").val(),
role: $("#role").val(),
provider: $("#provider").val(),
providerid: $("#providerid").val()
};

$.ajax({
type: "PUT",
url: "/update",
data: JSON.stringify(data), // http body데이터
contentType: "application/json; charset=utf-8",// body데이터가 어떤 타입인지(MIME)
dataType: "json" // 요청을 서버로해서 응답이 왔을 때 기본적으로 모든 것이 문자열 (생긴게 json이라면) => javascript오브젝트로 변경
}).done(function(resp){
alert("회원수정이 완료되었습니다.");
//console.log(resp);
location.href = "/";
}).fail(function(error){
alert(JSON.stringify(error));
});

},
}

index.init();

 

UserController.java

@RestControllerpublic class UserController {

@Autowired private UserService userService;

@Autowired private BCryptPasswordEncoder bCryptPasswordEncoder;


@Autowired private AuthenticationManager authenticationManager;

@PutMapping("/update")
public ResponseDto<Integer> update(@RequestBody UserDto userDto){
System.out.println(userDto);

String rawPW = userDto.getPassword();
String encPW = bCryptPasswordEncoder.encode(rawPW);
System.out.println("1");
userDto.setPassword(encPW);

userService.update(userDto);
System.out.println("2");

Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userDto.getUsername(), userDto.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);

// System.out.println("authentication 정보 : "+authentication); System.out.println("3");

return new ResponseDto<Integer>(HttpStatus.OK.value(), 1);//자바오브젝트를 JSON으로 변환해서 리턴 }
}

 

SecurityConfig.java

@Configuration //
@EnableWebSecurity // 스프링 시큐리티 필터가 스프링 필터체인에 등록
//@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
@EnableMethodSecurity(securedEnabled = true)
public class SecurityConfig{

@Autowired
private PrincipalOauth2UserService principalOauth2UserService;

private final PrincipalDetailsService userDetailsService;

public SecurityConfig(PrincipalDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration)
throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}

protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(encodePwd());
}


@Bean
public BCryptPasswordEncoder encodePwd() {
return new BCryptPasswordEncoder();
}

@Bean public LoginSuccessHandler loginSuccessHandler(){
return new LoginSuccessHandler();
}

@Bean
public LoginFailureHandler loginFailureHandler(){
return new LoginFailureHandler();
}



@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
http.csrf(CsrfConfigurer::disable).cors(Customizer.withDefaults());

http.formLogin((formLogin) ->
formLogin.loginPage("/loginForm").loginProcessingUrl("/login").successHandler(loginSuccessHandler()).failureHandler(loginFailureHandler()))
.authorizeHttpRequests(authorize ->
authorize
.requestMatchers("/admin/**").hasAnyRole("ADMIN") // /center/** url => admin 권한이 있는 사용자만 접근 가능
.anyRequest().permitAll() // 권한을 주지않은 url 이 아니면 접근 허용
).oauth2Login((oauth2) -> oauth2
.loginPage("/loginForm").userInfoEndpoint(userInfoEndpoint -> userInfoEndpoint
.userService(principalOauth2UserService)).successHandler(loginSuccessHandler()).failureHandler(loginFailureHandler())) ;



return http.build();
}

}

이렇게 작성이 되어있는 상태인데요.

답변 1

0

안녕하세요, 인프런 AI 인턴입니다.
볼빵빵오춘기님, 스프링 시큐리티를 이용한 회원 정보 수정과 관련된 어려움을 겪고 계신 것으로 보입니다.

에러 메시지 Request method 'PUT' is not supported는 일반적으로 서버에서 지원하지 않는 HTTP 메소드로 요청을 보낼 때 발생합니다. 현재 UserController.java에서 @PutMapping("/update")를 사용하고 있으니, 서버는 PUT 메서드를 지원해야 하는데, 문제가 발생하고 있습니다.

UserController에서 Authentication 객체를 설정하는 코드를 추가하였을 때 나타나는 이 문제는, authenticationManager.authenticate 메서드 호출 중에 발생하는 것으로 추정됩니다. 이 authenticate 메서드는 내부적으로 여러 AuthenticationProvider를 거치게 되는데, 이 과정 중에서 PUT 메서드를 지원하지 않는 형태로 요청이 변경되었을 가능성이 있습니다.

해결 방법으로는 다음과 같은 접근을 시도해볼 수 있습니다:

  1. 컨트롤러 메서드에서 PUT 요청을 받는 것이 제대로 설정되어 있는지 확인해보세요. @RestController 이용 시 기본적으로 모든 메서드는 요청을 받을 수 있어야 합니다.

  2. AuthenticationManager 사용 시 UsernamePasswordAuthenticationToken 생성을 올바르게 하고 있는지 확인하세요. 사용자 이름과 암호화된 비밀번호를 정확히 전달해야 합니다.

  3. AuthenticationManagerPUT 요청을 처리하는 과정에서 문제가 있는지 확인해보세요. 이를 파악하기 위해서는, 디버깅을 통해 어떤 AuthenticationProvider가 작동하고 있는지, 또 그 과정에서 어떤 일이 발생하는지 파악할 필요가 있습니다.

  4. 서버 사이드의 CSRF 보호 기능이 활성화되어 있다면, 이것이 원인일 수도 있습니다. CSRF 토큰을 요청과 함께 보내야 합니다. 단, CSRF 보호를 비활성화하는 것은 보안상 위험할 수 있으니 충분히 고려한 후에 결정하셔야 합니다.

  5. 스프링 시큐리티의 로그 레벨을 조정하여 상세한 정보를 얻어보세요. 문제를 추적하는 데 도움이 됩니다.

  6. 스프링 시큐리티가 자동으로 구성되는 것을 방지하고 필요한 부분을 명시적으로 설정하고 싶다면 WebSecurityConfigurerAdapter를 상속받은 설정 클래스를 만들어 사용할 수 있습니다.

SecurityConfig.java에서 별다른 문제점은 보이지 않으니, 주로 Controller와 관련된 구성에 문제가 있을 수 있습니다. 에러 메시지와 실제 코드의 동작을 자세히 따져보시면서 문제를 해결해 나가야 할 것 같습니다.

그리고 스프링 시큐리티 관련된 문서의 추가 정보가 필요하시다면, 스프링 시큐리티 공식 문서에 있는 내용을 참고하시는 것이 좋습니다:

도움이 되셨기를 바랍니다. 문제 해결에 좋은 결과 있으시길 바랍니다.

볼빵빵오춘기님의 프로필 이미지
볼빵빵오춘기

작성한 질문수

질문하기