Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |
Tags
- 상수
- 시스템 환경 변수 편집
- 컴파일
- 인텔리제이 한글 깨짐 해결법
- IntelliJ IDEA
- JAVA객체지향
- Java
- 집합관계
- 연관관계
- Java데이터 타입
- 메서드 오버로딩
- Thread
- 포함관계
- 자바 멀티스레딩
- multi-threading
- 반복문
- While
- 생성자
- for문
- continue문
- OPP개념
- JAVA기초
- this예약어
- java변수
- 인텔리제이 기초 설정
- function
- 접근제어지시자
- break문
- 메서드
- 형 변환
Archives
- Today
- Total
최원종의 개발 블로그
V11 - 2 역할 기반 접근 제어 본문
로그인 시 권한 정보 함께 조회로 수정
SQL문
select u.*, r.role
from user_tb u
left join user_role_tb r on u.id = r.user_id
WHERE u.username = 'admin' AND u.password = '1234';
UserService (로그인 및 회원가입 로직 수정)
/user/Repository.java
// 사용자명과 비밀번호로 사용자 조회(로그인용) + Role 정보 한번에 조회
@Query("""
SELECT distinct u FROM User u
LEFT JOIN FETCH u.roles r
WHERE u.username = :username AND u.password = :password
""")
Optional<User> findByUsernameAndPasswordWithRoles(@Param("username") String username,
@Param("password") String password);
회원가입 메서드 수정
UserService 코드⬇️
추가 코드
// 기본 권한 추가 (일반 사용자로 설정)
user.addRole(Role.USER);
더보기
// http://192.168.4.101:8080/join-form (강사 서버 컴퓨터 주소)
/**
* 회원 가입 처리
* @param joinDTO (사용자 회원가입 요청 정보)
* @return User (저장된 사용자 정보)
*/
@Transactional
public User 회원가입(UserRequest.JoinDTO joinDTO) {
log.info("회원가입 서비스 시작");
// 회원가입시 사용자 이름 중복 체크
userRepository.findByUsername(joinDTO.getUsername()).ifPresent(user -> {
log.warn("회원가입 실패 - 중복된 사용자명 : {}", user.getUsername());
throw new Exception400("이미 존재하는 사용자 이름입니다");
});
// 프로필 이미지 저장 기능 구현 (선택 사항 임)
String profileImageFilename = null;
if(joinDTO.getProfileImage() != null && joinDTO.getProfileImage().isEmpty() == false) {
try {
// 이미지 파일이 맞는지 검증
if(FileUtil.isImageFile(joinDTO.getProfileImage()) == false) {
throw new Exception400("이미지 파일만 업로드 가능합니다");
}
profileImageFilename = FileUtil.saveFile(joinDTO.getProfileImage(), FileUtil.IMAGES_DIR);
} catch (Exception e) {
// 디스크 공간 없거나, 권한 없음
throw new Exception500("프로필 이미지 저장 실패");
}
}
// 코드 수정
User user = joinDTO.toEntity(profileImageFilename);
// 기본 권한 추가 (일반 사용자로 설정)
user.addRole(Role.USER);
return userRepository.save(user);
}
WebMvcConfig 코드( /admin/**도 로그인 필수로 추가)⬇️
더보기
package com.tenco.blog._core.config;
import com.tenco.blog._core.interceptor.AdminInterceptor;
import com.tenco.blog._core.interceptor.LoginInterceptor;
import com.tenco.blog._core.interceptor.SessionInterceptor;
import com.tenco.blog._core.util.FileUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.nio.file.Paths;
// 자바 코드로 스프링 부트 설정 파일을 다둘 수 있다.
// @Component
@Configuration // IoC 대상 - 하나 이상의 IoC 처리를 하고 싶을 때 사용 한다.
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired // DI 처리
private LoginInterceptor loginInterceptor;
@Autowired // DI 처리
private SessionInterceptor sessionInterceptor;
@Autowired
private AdminInterceptor adminInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 화면에 SessionUser 정보를 내려줄 사용 됨.
registry.addInterceptor(sessionInterceptor)
.addPathPatterns("/**"); // 모든 URL 요청서 동작 함
// 인증 처리 인터셉터 동작 함
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/board/**", "/user/**", "/reply/**", "/admin/**")
.excludePathPatterns(
// 로그인 관련(인증이 필요 없는 페이지)
"/login-form", // 로그인 화면 요청 시
"/join-form", // 회원 가입 화면 요청 시
"/logout", // 로그아웃
// 게시글 조회 관련 (인증 없이도 볼 수 있는 페이지)
"/board/list", // 게시글 목록 화면 요청 시
"/" , // 메인 페이지
"/index" , // 메인 페이지
"/board/{id:\\d+}", // 게시글 상세보기( 숫자 ID만 허용)
// 정적 리소스 (CSS, JS, 이미지 등)
"/css/**", // CSS 파일 제외
"/js/**", // JS 파일 제외
"/images/**", // 이미지 파일 제외
"/favicon.ico", // 파비콘 제외
// H2 데이터베시스 콘솔( 개발 환경용)
"/h2-console/**" // H2 콘솔 접근
);
// 관리자 페이지 요청이 들어 왔을 때 1단계 로그인 여부 확인, 2단계 Role 확인해서
// ADMIN 일 경우만 관리자 페이지로 이동 가능하게 처리
registry.addInterceptor(adminInterceptor).addPathPatterns("/admin/**");
}
// 정적 리소스 핸들러 설정
// 외부 사용자가 내 서버 컴퓨터에 특정 경로를 바로 확인을 할 수 있게 한다면
// 보안상 취약 할 수 있습니다. ( 사용자에게는 가짜 경로를 보여주고 내부에서는
// 실정 경로를 찾을 수 있도록 처리 하는 기법(보안상)
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
String externalPath = Paths.get(FileUtil.IMAGES_DIR).toString();
registry.addResourceHandler("/images/**")
// file: 추가 하기
.addResourceLocations("file:" + externalPath);
}
}
AdminInterceptor ( 관리자 권한 체크 생성)
package com.tenco.blog._core.interceptor;
import com.tenco.blog._core.errors.Exception401;
import com.tenco.blog._core.errors.Exception403;
import com.tenco.blog._core.util.Define;
import com.tenco.blog.user.User;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
public class AdminInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 세션에서 로그인 사용자 정보 조회
// 단, AdminInterceptor 동작하기 전에 LoginInterceptor 가 먼저 선언이 되어 동작하기 된다.
// 즉 로그인은 보장 되어 있음 ! (순서 중요)
HttpSession session = request.getSession();
User sessionUser = (User) session.getAttribute(Define.SESSION_USER);
if(sessionUser == null) {
throw new Exception401("로그인이 필요합니다");
}
if(!sessionUser.isAdmin()) {
throw new Exception403("관리자 권한이 필요합니다");
}
return true;
}
}
AdminController ( 관리자 전용 컨트롤러 생성)
package com.tenco.blog.admin;
import com.tenco.blog._core.errors.Exception403;
import com.tenco.blog._core.util.Define;
import com.tenco.blog.user.User;
import jakarta.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class AdminController {
@GetMapping("/admin/dashboard")
public String dashboardPage(HttpSession session, Model model) {
User sessionUser = (User) session.getAttribute(Define.SESSION_USER);
model.addAttribute("admin", sessionUser);
return "admin/dashboard";
}
}
헤더 메뉴 및 관리자 대시보드 뷰
src/main/resources/templates/layout/header.mustache 코드
더보기
<!DOCTYPE html>
<html lang="en">
<head>
<title>Blog</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
</head>
<body class="d-flex flex-column min-vh-100">
<nav class="navbar navbar-expand-sm bg-dark navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="/">Tencoding</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#collapsibleNavbar">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse d-flex justify-content-between" id="collapsibleNavbar">
<ul class="navbar-nav">
{{#sessionUser}}
<li class="nav-item">
<a class="nav-link" href="/board/save-form">글쓰기</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/user/detail">마이페이지</a>
</li>
{{#admin}}
<li class="nav-item">
<a class="nav-link" href="/admin/dashboard">관리자</a>
</li>
{{/admin}}
<li class="nav-item">
<a class="nav-link" href="/logout">로그아웃</a>
</li>
{{/sessionUser}}
{{^sessionUser}}
<li class="nav-item">
<a class="nav-link" href="/join-form">회원가입</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/login-form">로그인</a>
</li>
{{/sessionUser}}
</ul>
<ul class="navbar-nav ml-auto">
{{#sessionUser}}
<li class="nav-item d-flex">
<span class="badge bg-info">{{roleDisplay}}</span>
<span class="badge bg-info">{{username}} 님</span>
</li>
{{/sessionUser}}
</ul>
</div>
</div>
</nav>
관리자 대시보드 뷰

src/main/resources/templates/admin/dashboard.mustache 코드
{{> layout/header}}
<div class="container p-5 flex-grow-1">
<div class="card">
<div class="card-header">관리자페이지 - 대쉬보드</div>
<div class="card-body">
<h3>여기는 ROLE ADMIN 접근 가능한 페이지 입니다</h3>
<p>{{admin.username}} 님 반갑습니다</p>
</div>
</div>
</div>
{{> layout/footer}}'Spring boot 입문' 카테고리의 다른 글
| V12 - 2 카카오 소셜 로그인 (0) | 2026.05.26 |
|---|---|
| V12-1 Oauth 2.0 (카카오 소셜 로그인) (0) | 2026.05.26 |
| V11 - 1 역할 기반 접근 제어 (0) | 2026.05.26 |
| V10-5 운영체제 별 경로 설정 (0) | 2026.05.22 |
| V10-4 프로필 이미지 수정 하기 (0) | 2026.05.22 |