Contents
머스태치에서 사용가능한 세션 정보 저장해두기SecurityConfig 수정하기
package shop.mtcoding.blog._core.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration // 컴퍼넌트 스캔
public class SecurityConfig {
@Bean
public BCryptPasswordEncoder encoder(){
return new BCryptPasswordEncoder();
}
@Bean
public WebSecurityCustomizer ignore(){
return w -> w.ignoring().requestMatchers("/board/*", "/static/**", "/h2-console/**");
}
@Bean
SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.csrf(c -> c.disable());
http.authorizeHttpRequests(a -> {
a.requestMatchers("/user/updateForm", "/board/**").authenticated()
.anyRequest().permitAll();
});
http.formLogin(f -> {
f.loginPage("/loginForm").loginProcessingUrl("/login").defaultSuccessUrl("/").failureUrl("/loginForm");
});
return http.build();
}
}
username 조회 메서드 만들기
public User findByUsername(String username) {
Query query = em.createNativeQuery("select * from user_tb where username=?", User.class);
query.setParameter(1, username);
try {
User user = (User) query.getSingleResult();
return user;
}catch (Exception e){
return null;
}
}
MyLoginUser객체 만들기
package shop.mtcoding.blog._core.config.security;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import shop.mtcoding.blog.user.User;
import java.util.Collection;
// 세션에 저장되는 오브젝트
@RequiredArgsConstructor
public class MyLoginUser implements UserDetails {
private final User user;
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
}
MyLoginService 구현하기
package shop.mtcoding.blog._core.config.security;
import lombok.RequiredArgsConstructor;
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 shop.mtcoding.blog.user.User;
import shop.mtcoding.blog.user.UserRepository;
// POST, /login, x-www-form-urlencoded, 키값이 username, password
@RequiredArgsConstructor
@Service
public class MyLoginService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("loadUserByUsername : "+username);
User user = userRepository.findByUsername(username);
if(user == null){
System.out.println("user는 null");
return null;
}else{
System.out.println("user를 찾았어요");
return new MyLoginUser(user); // SecurityContextHolder 저장
}
}
}
EncodeTest 작성 후 해쉬값 확인

해쉬값 data.sql에 붙여넣기

SecurityConfig
@Bean
public BCryptPasswordEncoder encoder(){
return new BCryptPasswordEncoder();
}
로그인 테스트 해보기
BoardController
@GetMapping("/")
public String index(HttpServletRequest request, @AuthenticationPrincipal MyLoginUser myLoginUser) {
System.out.println("로그인 되었나? : "+myLoginUser.getUsername());
List<Board> boardList = boardRepository.findAll();
request.setAttribute("boardList", boardList);
return "index";
}
로그인 했을 때 확인 가능

1. 사용자가 로그인 요청을 보냅니다.
2. 스프링 시큐리티는 이 요청을 가로채어 사용자가 제공한 인증 정보(예: 사용자 이름과 비밀번호)를 검증합니다.
3. 인증이 성공하면, 스프링 시큐리티는 보안 컨텍스트에 사용자의 정보를 저장하고, 인증된 사용자로써의 권한을 부여합니다.
4. 이 정보는 주로 세션에 저장되어 후속 요청에서 사용됩니다. 스프링 시큐리티는 세션에 저장된 인증 정보를 이용해 요청이 들어올 때마다 사용자를 식별하고 권한을 확인합니다.
5. 새로운 요청이 들어오면, 스프링 시큐리티는 이 요청에 대한 인증 헤더, 쿠키 또는 다른 인증 수단을 확인하여 보안 컨텍스트를 복원하고, 사용자를 인증합니다.
6. 만약 인증에 실패하면, 해당 요청은 보호된 자원에 접근할 수 없게 됩니다.
머스태치에서 사용가능한 세션 정보 저장해두기
이유는 ~~~~~~~~~~~~~~~~~~~~~~~~ 세션 - > SPRING_SECURITY_CONTEXT -> SecurityContext 객체 -> Authentication -> MyLoginUser(UserDetails) -> User
기존 컨트롤러 인증 부분 수정하기
UserController
@GetMapping("/user/updateForm")
public String updateForm(HttpServletRequest request, @AuthenticationPrincipal MyLoginUser myLoginUser) {
User user = userRepository.findByUsername(myLoginUser.getUsername());
request.setAttribute("user", user);
return "user/updateForm";
}
인증체크부분 삭제



package shop.mtcoding.blog.board;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import shop.mtcoding.blog._core.config.security.MyLoginUser;
import shop.mtcoding.blog.user.User;
import shop.mtcoding.blog.user.UserRepository;
import shop.mtcoding.blog.user.UserRequest;
import java.util.HashMap;
import java.util.List;
@RequiredArgsConstructor
@Controller
public class BoardController {
private final HttpSession session;
private final BoardRepository boardRepository;
// ?title=제목1&content=내용1
// title=제목1&content=내용1
@PostMapping("/board/{id}/update")
public String update(@PathVariable int id, BoardRequest.UpdateDTO requestDTO, @AuthenticationPrincipal MyLoginUser myLoginUser){
// 권한 체크
Board board = boardRepository.findById(id);
if (board.getUserId() != myLoginUser.getUser().getId()) {
return "error/403";
}
// 핵심 로직
// update board_tb set title = ?, content = ? where id = ?;
boardRepository.update(requestDTO, id);
return "redirect:/board/"+id;
}
@GetMapping("/board/{id}/updateForm")
public String updateForm(@PathVariable int id, HttpServletRequest request, @AuthenticationPrincipal MyLoginUser myLoginUser){
// 모델 위임 (id로 board를 조회)
Board board = boardRepository.findById(id);
// 권한 체크
if (board.getUserId() != myLoginUser.getUser().getId()) {
return "error/403";
}
// 가방에 담기
request.setAttribute("board", board);
return "board/updateForm";
}
@PostMapping("/board/{id}/delete")
public String delete(@PathVariable int id, HttpServletRequest request, @AuthenticationPrincipal MyLoginUser myLoginUser) {
// 2. 권한 없으면 나가
Board board = boardRepository.findById(id);
if (board.getUserId() != myLoginUser.getUser().getId()) {
request.setAttribute("status", 403);
request.setAttribute("msg", "게시글을 삭제할 권한이 없습니다");
return "error/40x";
}
boardRepository.deleteById(id);
return "redirect:/";
}
@PostMapping("/board/save")
public String save(BoardRequest.SaveDTO requestDTO, HttpServletRequest request, @AuthenticationPrincipal MyLoginUser myLoginUser) {
// 2. 바디 데이터 확인 및 유효성 검사
System.out.println(requestDTO);
if (requestDTO.getTitle().length() > 30) {
request.setAttribute("status", 400);
request.setAttribute("msg", "title의 길이가 30자를 초과해서는 안되요");
return "error/40x"; // BadRequest
}
// 3. 모델 위임
// insert into board_tb(title, content, user_id, created_at) values(?,?,?, now());
boardRepository.save(requestDTO, myLoginUser.getUser().getId());
return "redirect:/";
}
@GetMapping("/")
public String index(HttpServletRequest request) {
List<Board> boardList = boardRepository.findAll();
request.setAttribute("boardList", boardList);
return "index";
}
// /board/saveForm 요청(Get)이 온다
@GetMapping("/board/saveForm")
public String saveForm() {
return "board/saveForm";
}
@GetMapping("/board/{id}")
public String detail(@PathVariable int id, HttpServletRequest request, @AuthenticationPrincipal MyLoginUser myLoginUser) {
// 1. 모델 진입 - 상세보기 데이터 가져오기
BoardResponse.DetailDTO responseDTO = boardRepository.findByIdWithUser(id);
boolean pageOwner;
if (myLoginUser == null) {
pageOwner = false;
} else {
int 게시글작성자번호 = responseDTO.getUserId();
int 로그인한사람의번호 = myLoginUser.getUser().getId();
pageOwner = 게시글작성자번호 == 로그인한사람의번호;
}
request.setAttribute("board", responseDTO);
request.setAttribute("pageOwner", pageOwner);
return "board/detail";
}
}
회원가입 패스워드 인코딩
private final BCryptPasswordEncoder passwordEncoder;
@PostMapping("/join")
public String join(UserRequest.JoinDTO requestDTO){
System.out.println(requestDTO);
String rawPassword = requestDTO.getPassword();
String endPassword = passwordEncoder.encode(rawPassword);
userRepository.save(requestDTO); // 모델에 위임하기
return "redirect:/loginForm";
}
Share article