스프링부트 강좌 61강(블로그 프로젝트) - 회원수정 2
세션값을 변경하기
스프링 시큐리티에서 약간 까다롭다.
유저 서비스에서 회원수정이라는 단어가 종료될 때
userService.회원수정(user) 함수가 호출이 되고 종료가 되는 타이밍
세션값을 변경하는 방법은 시큐리티가 어떻게 로그인이 되는지 그 로그인이 될때 세션이 어떻게 만들어지는지에 대한 기본 개념이 필요하다.
Authentication라는 객체가 들어가면 들어간 순간부터 세션에 값이 저장된다.
필요한 곳에서
@GetMapping("/user/updateForm")
public String updateForm(@AuthenticationPrincipal PrincipalDetail principal) {
return "user/updateForm";
}
principal는 Authentication라는 객체를 가져오는 것이다.
그럼 Authentication라는 것은 어떻게 만들어 질까?
사용자가 로그인 요청을 하게 되면 AuthenticationFilter를 거치게 된다. 로그인 요청이 오면 이 필터가 제일 먼저 작동한다. 그래서 로그인 요청을 할때는 httpbody에 무엇을 담고 오냐? username과 password를 당연히 가져올 것이다.
이 두가지를 갖고 UsernamePasswordAuthenticationToken을 만든다.
이 토큰을 갖고 AuthenticationManager이라는 얘가 그냥 username과 password를 받으면 안되고,
AuthenticationToken을 UserDetailService에게 던진다. 그럼 UserDetailService는 username을 가지고 데이터베이스에서 해당 유저 네임으로 있는지 확인을 하고 있으면 Authentication 객체를 만든다. 없으면 만들지 않는다.
PrincipalDetailService.java
package com.yuri.blog.config.auth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.yuri.blog.model.User;
import com.yuri.blog.repository.UserRepository;
@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 이 안들어오고 Username만 들어왔는데요?
Username만 들어와서 해당 유저로 디비에 사용자가 있는지 확인하고 있으면 return new PrincipalDetail(principal); PrincipalDetail에다가 던져서 세션이 만들어 지는 것이다.
그럼 패스워드는?..password는 인코딩을 따로 해야 한다. 1234 가 들어왔을 때 AuthenticationManagerBuilder 가 작동해서 Manger가 passwordEncoder를 들고 있다.
어떤 것으로 암호화 되어야 하는지 알고 있다. 따라서 Manger는 userDetail 서비스로는 username만 날리고 password는 자기가 관리를 한다.
관리를 해서 인코드 해서 해당 유저를 찾고 (해당 유저를 찾는 것은 UserDetailService)가 한다.
password 비교는 Manger가 해준다. 디크팁트 인코드로 인코드 해서 비교를 한다.
AuthenticationManger 가 pasword를 인코딩해서 비교하고 userDetailService에서 해당 user를 찾아서 해당 유저가 있는지를 확인을 해서 세션에 저장하는 역할을 한다.
<다시 정리>
세션이라는 메모리 공간이 있다. 세션에다가 유저 정보를 저장한다. 일반적으로 jsp 나 기존 스프링으로 세션 저장을 할때는
session.s
여기다가 직접 user오브젝트를 집어넣는다. 하지만 스프링 시큐리티는 그러헥 하지 말고 세션에 어떤 특정 공간을 만들어서 스프링 시큐리티 컨텍스트라고 부른다.
여기다가 저장할 수 있는게 유저 오브젝트가 아니라 Authentication라는 객체를 저장해서 관리한다.
이 객체는 누가 만들냐면 AuthenticationManger라는 얘가 만들어준다. Manger가 Authentication 객체를 만들기 위해서는 유저 네임과 패스워드를 알아야 데이터베이스와 비교해보고 확인을 할 것이다.
첫번째로는 사용자가 로그인 요청을 한다. 로그인 요청을 하게 되면
username: lala
password:1234
를 날리면 필터가 가로챈다. 필터는 usernamePasswordAuthenticationToken을 만들어준다. usernamePasswordAuthenticationToken는 lala라는 username와 1234를 통해 만들어진다.
왜 굳이 만들까? 이 토큰을 만들어 Manger에게 던지면 Manger가 세션을 만들어주기 때문이다.
세션을 만들어주기 위한 조건이 있다. Manger는 username, lala 를 userDetailService에게 던진다.
우리가 들고 있는 userDetailService는 PrincipalDetailService이다. 이게 UserDetailsService를 implements하고 있으니까 UserDetailsService가 PrincipalDetailService라고 생각하면 된다.
username만 던져서 바꿔준다.
id와 password를 받는다. 그럼 필터가 낚아채서 토큰을 만든다. 이 토큰은 lala라는 아이디와 1234라는 패스워드를 토대로 만듦. 그리고 이 토큰을 Manger에게 던진다. 던지는 목적은 Authentication 객체를 만들기 위해서이다. 그럼 이 객체를 만들기 위해서는 조건이 필요하다. 일단 lala라는 아이디가 데이터베이스에 있는지 확인해야 한다. lala라는 아이디가 있음을 확인하면 비밀번호까지 체크한다.
비밀번호도 1234 로 체크하면 안된다. 디크립트로 인코딩해서 해쉬로 암호화 된것을 확인한다. 확인하고 정확하게 있을 경우 Authentication 객체를 만든다. 만들어서 세션에 저장한다. 이 세션에 시큐리티 컨텍스트에 있는 유저 오브젝트를 저장하지 못하니까 (Authentication 객체만 저장할 수 있음)
Authentication 객체를 만들어서 세션에 저장하기 위한 흐름이다. 강제로 세션을 만들기 위해서는 이 로직을 타야 한다. 내가 Authentication 객체를 만들어서 강제로 세션에 집어넣으면 된다. 그것을 해볼 것이다.
-이 글은 유투버 겟인데어의 스프링 부트 강좌를 바탕으로 정리한 내용입니다.-