UserApiController

//회원가입 수정
@PutMapping("/user")
public ResponseDto<Integer> update(@RequestBody User user){ //@RequestBody로 받아야 json형태로 받음
    userService.회원수정(user);
    return new ResponseDto<Integer>(HttpStatus.OK.value(),1);
}

 

회원정보 수정시 여기까지는 트랜잭션이 종료되어 DB에는 값이 수정이 됐으나 

세션값은 변경이 않은 상태여서 로그아웃 후 다시 로그인 해야 회원정보가 변경된걸 확인할 수 있다. 

 

 

직접 세션값을 변경해주자 

 

 

 

우선 세션과정 기본개념을 알자면

Security 인증절차

 

 

 

 

SecurityContextHolder ⊂ SecurityContext Authentication 객체가 있으면 세션값이 저장된 상태.

 

 

@AuthenticationPrincipal PrincipalDetail principal 을 쓰면 principal은 Authentication객체를 가져온다 

@GetMapping("/user/updateForm")
public String updateForm(@AuthenticationPrincipal PrincipalDetail principal) {
    return "user/updateForm";
}

 

 

 

Authentication 객체가 만들어지는 과정

 

username, password로 로그인 요청이 들어왔을때 먼저 1. AuthenticationFilter 인증필터를 거친다. 

두 정보로 2. UsernamePasswordAuthenticationToken 을 만들고 3. AuthenticationManager 에게 준다.

= AuthenticationManager 가 UsernamePasswordAuthenticationToken을 받아 Authentication 객체를 만든다

 

 

어떻게 Authentication 객체를 만드느냐

 

 

 

UsernamePasswordAuthenticationToken 을 UserDetailsService에게 던져준다.

UserDetailService은 DB에 해당 username이 있는지 확인 후 있으면 Authentication 객체를 만든다

 

 

이때 UserDetailsService은 PrincipalDetailService이다. 

@Service // Bean 등록
public class PrincipalDetailService implements UserDetailsService{
	
	@Autowired
	private UserRepository userRepository;
	
	// 스프링이 로그인 요청을 가로챌 때, username, password 변수 2개를 가로채는데
	// password 부분 처리는 알아서 함.
	// username이 DB에 있는지만 확인해주면 됨.
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		User principal = userRepository.findByUsername(username)
				.orElseThrow(()->{
					return new UsernameNotFoundException("해당 사용자를 찾을 수 없습니다. : "+username);
				});
		return new PrincipalDetail(principal); // 시큐리티의 세션에 유저 정보가 저장이 됨.
	}

UsernamePasswordAuthenticationToken 을 PrincipalDetailService에 던져줬는데 username만 받는다

그리고 DB에 있는지 확인하고 있으면 PrincipalDetail 에 넣어져서 세션이 만들어진다. 

 

 

 

그렇다면 패스워드는? 

스프링이 따로 가져가서 인코딩을 한다 

 

 

[SecurityConfig]

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(principalDetailService).passwordEncoder(encodePWD());
}

AuthenticationManager 는 비밀번호가 BCryptPasswordEncoder로 암호화 된 것을 알고있어서

비밀번호를 인코딩하여 DB와 비교 후 password를 가져온다.

 

 

정리하자면

AuthenticationManager 는 password 을 가져오고

UserDetailService은 username 가져와서 세션에 저장

AuthenticationManager안에 username, password를 직접 넣어 Authentication 객체를 만듬

 

 

 

 

삽질한 것.. SecurityContext 안에 Authentication 객체를 직접 넣는건 x , 자동으로 넣어진다

 

이제 AuthenticationManager 을 직접 생성하기

 

 

 

 

[SecurityConfig]

- alt + shift + s 해서 Override/implement Methods 클릭 - authenticationManagerBean() 선택 후 OK

 

 

@Bean 어노테이션을 써서 스프링 컨텍스트에 넣어 얼마든지 꺼내 쓸 수 있도록 DI 한다.

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
     return super.authenticationManagerBean();
}

 

 

 

[UserApiController]

@Autowired
private AuthenticationManager authenticationManager;

//회원가입 수정
@PutMapping("/user")
public ResponseDto<Integer> update(@RequestBody User user){ //@RequestBody로 받아야 json형태로 받음
    userService.회원수정(user);
    // 여기서는 트랜잭션이 종료되기 때문에 DB값은 변경됐지만 세션값은 변경되지 않았다.
    // 비밀번호를 변경해도 로그아웃 후 로그인해야 세션이 변경된다. 직접 세션값 변경해줘야한다.

    //세션 등록
    Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword()));
    SecurityContextHolder.getContext().setAuthentication(authentication);

    return new ResponseDto<Integer>(HttpStatus.OK.value(),1);
}

세션 등록

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

authenticationManager.authenticate() 강제로 로그인 처리하는데 UsernamePasswordAuthenticationToken 에 있는 username, password 을 가져와 Authentication 객체에 저장하고

SecurityContextHolder가 시큐리티 컨텍스트(Authentication 객체만 저장하는)를 가져와서 Authentication 객체를 저장한다.

 

 

※ 순서 주의 (삽질한것..)

[UserService] 에 회원수정() 메서드에서  Authentication 객체에 저장를 저장한다면?

수정한 값이 DB에 저장하기 전이라 안된다. 

회원수정() 메서드가 끝나야 (= service 종료 후) 트랜잭션 종료하고 commit이 되어 유저 정보가 업데이트가 됨!!!!

즉 userService.회원수정(user) 을 수행 후 업데이트 된 값을 세션에 등록해준다 

 

 

그런데 회원수정을 했는데도 회원정보에 들어가면 수정이 안되었다.. 그 문제점은

new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword())); 

username이 null 이다. 왜냐면

회원수정을 실행하는 자바스크립트를 보면 username 값을 데이터로 안넣어줬기 때문

 

 

[user.js]

 

추가해주기 ▶ username: $("#username").val()

username 값을 추가해서 4개의 값을 data에 넣어주고 해당 주소(/user)로 data가 넘어간다.

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

    $.ajax({ 
        type: "PUT",
        url: "/user",
        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));
    }); 

},

 

 

[UserApiController]

@Autowired
private AuthenticationManager authenticationManager;

//회원가입 수정
@PutMapping("/user")
public ResponseDto<Integer> update(@RequestBody User user){ //@RequestBody로 받아야 json형태로 받음
    userService.회원수정(user);
    // 여기서는 트랜잭션이 종료되기 때문에 DB값은 변경됐지만 세션값은 변경되지 않았다.
    // 비밀번호를 변경해도 로그아웃 후 로그인해야 세션이 변경된다. 직접 세션값 변경해줘야한다.

    //세션 등록
    Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword()));
    SecurityContextHolder.getContext().setAuthentication(authentication);

    return new ResponseDto<Integer>(HttpStatus.OK.value(),1);
}

user.getUsername() 은 이제 NULL이 아님

 

 

이제 회원수정하고 회원정보에 들어가면 수정 된 정보가 뜬다.

 

반응형
LIST

+ Recent posts