Web/Spring

[Spring Boot] 게시판 프로젝트 - 04. 회원정보 수정

Jinn 2023. 10. 1. 00:57

 

이전 글

 

[Spring Boot] 게시판 프로젝트 - 03. 게시판 글 CRUD

이전 글 { this.save(); }); }, save: function() { let d" data-og-host="pressky99.tistory.com" data-og-source-url="https://pressky99.tistory.com/39" data-og-url="https://pressky99.tistory.com/39" data-og-image="https://scrap.kakaocdn.net/dn/be1dFf/hyWY8MN

pressky99.tistory.com

목차

1. 내 정보 수정하기
2. 탈퇴하기
3. 트러블슈팅

 

1. 내 정보 수정하기

회원정보 페이지

 

[user.js]
let index = {
    init: function() {
        $("#btn-save").on("click", () => {
            this.save();
        });
        $("#btn-update").on("click", () => {
            this.update();
        });
    },
    save: function() {
        // 생략
    },
    update: function() {
        let username = $("#username").val();

        let data = {
            password: $("#password").val(),
            email: $("#email").val()
        }

        $.ajax({
            type: "PUT",
            url: "/api/user/" + username,
            data: JSON.stringify(data),
            contentType: "application/json; charset=utf-8",
            dataType: "json"
        }).done(function() {
            alert("수정 완료");
            location.href = "/";
        }).fail(function(error) {
            alert(JSON.stringify(error));
        });
    }
}

index.init();
[SecurityConfig.java]

... 생략 ...

// AuthenticationManager 빈으로 등록
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
    return authenticationConfiguration.getAuthenticationManager();
}
@RequiredArgsConstructor
@RestController
public class UserApiController {

    private final UserService userService;
    private final AuthenticationManager authenticationManager; // 주입

    // 생략

    @PutMapping("/api/user/{username}")
    public ResponseEntity<Integer> update(@PathVariable String username,
                                          @RequestBody User user) {
        userService.update(username, user);
        // DB에 정보는 변경된 상태(트랜잭션 종료됨)
        // 세션은 변경되지 않은 상태. 직접 변경

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

        return new ResponseEntity<>(1, HttpStatus.OK);
    }
}
@RequiredArgsConstructor
@Service
public class UserService {

    private final UserRepository userRepository;
    private final BCryptPasswordEncoder passwordEncoder;

    // 생략

    @Transactional
    public void update(String username, User user) {
        User originUser = userRepository.findByUsername(username)
                .orElseThrow(() -> new NoSuchElementException("회원 수정 실패: 해당 사용자를 찾을 수 없음"));

        originUser.setPassword(passwordEncoder.encode(user.getPassword()));
        originUser.setEmail(user.getEmail());
    }
}

글 수정과 마찬가지로 ajax 요청을 보내고 서비스에서 jpa 더티체킹이 발생해서 정보를 수정하게 된다.

서비스 계층에서 트랜잭션이 종료되면 DB에 변경이 반영된다. 즉 flush가 된다.

하지만 세션은 바뀌지 않고 그대로라서 컨트롤러에서 세션을 바꾸는 작업도 해주었다.


2. 탈퇴하기

@RequiredArgsConstructor
@RestController
public class UserApiController {

    private final UserService userService;
    private final AuthenticationManager authenticationManager;

    // 생략

    @DeleteMapping("/api/user/{username}")
    public ResponseEntity<Integer> delete(@PathVariable(name = "username") String username,
                                          @AuthenticationPrincipal PrincipalDetail principal,
                                          HttpSession session) throws IllegalAccessException {
        userService.delete(username, principal);

        session.invalidate(); // 세션 삭제
        SecurityContextHolder.clearContext(); // 스프링 시큐리티 Context 초기화

        return new ResponseEntity<>(1, HttpStatus.OK);
    }
}
@RequiredArgsConstructor
@Service
public class UserService {

    private final UserRepository userRepository;
    private final BCryptPasswordEncoder passwordEncoder;

    // 생략

    @Transactional
    public void delete(String username, PrincipalDetail principal) throws IllegalAccessException {
        if (!username.equals(principal.getUser().getUsername())) {
            throw new IllegalAccessException("탈퇴 실패: 잘못된 접근.");
        }
        userRepository.deleteByUsername(username);
    }
}

 


3. 트러블슈팅

내 정보를 수정하고 다시 "회원정보" 페이지를 들어가면 변경하기 전의 정보가 나왔다.

DB에 있는 정보를 바꾸기만 했지 세션에 있는 정보는 바뀌지 않았다는 것을 이때 알았다.

 

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

컨트롤러에 이 두줄을 추가하니 회원정보가 수정한 정보로 잘 나왔다.

 

스프링 시큐리티는 Context holder 안에 Context가 있고 그 안에 세션 정보인 Authentication이 있다.

그래서 세션을 바꾸려면 Context 내부에 있는 Authentication을 새로운 Authentication으로 바꿔야 한다.

 

새로운 Authentication은 AuthenticationManager를 통해 UsernamePasswordAuthenticationToken을 생성하여 만들 수 있다. 위 코드에서 첫 번째 줄의 의미가 이것이다.

이렇게 만든 Authentication을 스프링 시큐리티의 Context에 있는 기존 Authentication과 바꾸는 것이 두 번째 줄이다.

 

회원탈퇴 또한 마찬가지로 DB에서 삭제만 하지 않고 세션을 종료해야 탈퇴했을 때 로그인이 풀린다.

 

 

다음 글

 

[Spring Boot] 게시판 프로젝트 - 05. 댓글 기능

이전 글 [Spring Boot] 게시판 프로젝트 - 04. 회원정보 수정이전 글 [Spring Boot] 게시판 프로젝트 - 03. 게시판 글 CRUD이전 글 { this.save(); }); }, save: function() { let d" data-og-host="pressky99.tistory.com" data-og-sourc

pressky99.tistory.com