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이 아님
이제 회원수정하고 회원정보에 들어가면 수정 된 정보가 뜬다.
'Spring boot | 블로그 만들기' 카테고리의 다른 글
블로그 만들기 | [카카오API] .getBody() , ObjectMapper 라이브러리(3) (1) | 2022.11.18 |
---|---|
블로그 만들기 | [카카오 API] 엑세스 토큰 받기(2) (0) | 2022.11.17 |
블로그 만들기 | 회원 정보 수정하기 1 (0) | 2022.11.15 |
블로그 만들기 | 게시글 상세 페이지, 수정 , 삭제 (1) | 2022.11.15 |
블로그 만들기 | 게시글 목록 보기, 페이징 처리하기 (0) | 2022.11.14 |