위 페이지는 회원 정보를 보여주는 회원 정보 페이지이며, 이 페이지에서 바로 정보를 수정(update)할 수 있게 만들었다.
회원 정보를 수정하고, 회원수정완료
버튼을 누르면, 정보가 "DB에는" 잘 수정되지만, "세션"에는 수정되지 않아서, 회원을 수정한다음 다시 회원정보를 눌러 들어가게 되면 수정 전의 값이 여전히 세션에 남아 업데이트 되지 않는 문제가 발생한다. 이를 해결하기 위해서는 두가지 문제를 해결해야 했다.
세션값을 새로 업데이트한 값으로 바꿔주기 위해서는 내가 직접 세션 값을 바꿔주는 코드를 추가해주는 방법이 있다. (하지만 이 방법에는 문제가 있다) 참고로 현재 세션값 로직을 컨트롤러에 작성했는데, 서비스에 적어주는 것이 더 좋다.
// 이 컨트롤러는 나중에 "앱"에도 쓸 수 있음!
@RestController
public class UserApiController {
@Autowired
private UserService userService;
@PostMapping("/auth/joinProc")
public ResponseDto<Integer> save(@RequestBody User user){
userService.회원가입(user);
return new ResponseDto<Integer>(HttpStatus.OK,1);
}
// json 데이터를 받으려면 반드시 @RequestBody 를 써주어야 함.
// key=value(mime 타입이 x-www-form-urlencoded 를 받고싶다면
// @RequestBody 안적어도 됨
@PutMapping("/user")
public ResponseDto<Integer> update(@RequestBody User user,
@AuthenticationPrincipal PrincipalDetail principal,
HttpSession session){
System.out.println("업데이트 시작");
userService.회원수정(user);
// 여기서는 트랜잭션이 종료되기 때문에 DB 값은 변경이 됐음.
// 하지만 세션값은 변경되지 않은 상태이기 때문에 내가 "직접 세션값을 변경"해줄 것임.
// 세션값을 변경하는 방법은 스프링 시큐리티가 어떻게 로그인이 되는지,
// 로그인이 될 때 세션이 어떻게 만들어지는지에 대한 기본 개념이 필요함
// =====강제로 세션값을 바꿈======
Authentication authentication = new UsernamePasswordAuthenticationToken(
principal, null, principal.getAuthorities());
SecurityContext securityContext = SecurityContextHolder.getContext();
securityContext.setAuthentication(authentication);
session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
// =============================
return new ResponseDto<Integer>(HttpStatus.OK,1);
}
}
==== 이렇게 세션값을 변경하는 로직을 작성하면 잘 변경될줄 알았지만.. 애석하게도 변경되지 않았다. 아래와 같은 WARNING 이 떴기 때문.
WARN Cannot deserialize session attribute [SPRING_SECURITY_CONTEXT] for session [3D838C9C072205C27CBADA325F790882]
예전에는 SPRING_SECURITY_CONTEXT 속성에 바로 접근해서 세션값을 변경하는게 가능했지만 지금은 불가능한 것으로 보인다(spring-boot-starter-security:2.6.3)
즉 아래 그림처럼 내가 직접 시큐리티 컨텍스트를 집어넣는 것이 불가능,,,
Manager를 통해 넣어주어야 함!
@Service
public class UserService {
private final BCryptPasswordEncoder encoder;
public UserService(BCryptPasswordEncoder bCryptPasswordEncoder){
this.encoder = bCryptPasswordEncoder;
}
@Autowired
private AuthenticationManager authenticationManager; // 얘를 통해 세션값 변경해야함
@Autowired
private UserRepository userRepository;
@Transactional
public void 회원가입(User user){
String name = user.getUserName();
String email = user.getEmail();
String password = user.getPassword();
System.out.println("========");
System.out.println(name + email + password);
String hashedPWD = encoder.encode(password);
User newUser = new User();
newUser.setUserName(name);
newUser.setEmail(email);
newUser.setPassword(hashedPWD);
newUser.setRole(RoleType.USER);
userRepository.save(newUser);
}
@Transactional
public void 회원수정(User user){
// 수정시에는 영속성 컨텍스트 User 오브젝트를 영속화시키고, 영속화된 User 오브젝트를 수정
// select 를 해서 User 오브젝트를 DB 로부터 가져오는 이유는 영속화를 하기 위해서!!
// 영속화된 오브젝트를 변경하면 자동으로 DB에 update 문을 날려주기 때문
User persistence = userRepository.findById(user.getId()).orElseThrow(()->{
return new IllegalArgumentException("회원찾기 실패");
});
String rawPassword = user.getPassword();
String encPassword = encoder.encode(rawPassword);
persistence.setPassword(encPassword);
persistence.setEmail(user.getEmail());
// 세션 등록
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
// 회원수정 함수 종료 시 == 서비스 종료 시 == 트랜잭션 종료 == commit 이 자동으로 된다
// 영속화된 persistence 객체의 변화가 감지되면 더티체킹이 되어 변화된 것들에 대해 update 문을 날려줌.
}
}
근데, 이렇게 한다음 계속 시도해봤는데, username의 값이 계속 null이 나왔다. 바로 자바스크립트에서 username을 주지 않았기 때문이다... 어이없어...
username을 수정할 수 없게끔 내가 설정했고, 이메일과 비밀번호만 바꿀 수 있게 하였기에 컨트롤러로 데이터를 보낼때 단순히 email과 password 만을 담았었다. 이것을 컨트롤러에서 @RequestBody User를 사용해 받게 되는데, 당연히 자바스크립트에서 username을 주지 않았기에 user 에는 이메일과 패스워드 정보는 담기지만, username은 null 이 되게 된다.
// 세션 등록
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
이 코드가 바로 세션을 변경시키는 코드인데, 유저네임이 반드시 필요하지만 현재 user 의 username은 당연히 null이니 제대로 동작하지 않았던 것이다.