개인 공부

Spring Security를 활용한 사용자 권한 기반 접근 제어

orin602 2024. 12. 24. 00:00

저번 글의 Spring Security를 활용한 로그인 이후로 이번에는 관리자 페이지를 구현하면서 Role에 따라 접근 가능한 페이지/기능을 제어하는 방법을 공부하고 기록해 보려고 합니다..

 

오류만 안난다면.... 금방 끝나겠지!?

 

+++ 이전 게시글에서 수정 +++

MemberController에서 "/main" 매핑을 삭제, MainController 생성

package com.demo.controller;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;


@Controller
public class MainController {
	
	// 메인페이지
	@GetMapping("/main")
	public String mainPage(Model model) {
		// 사용자 권한 확인
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		if(authentication != null && authentication.getAuthorities() != null) {
			boolean isAdmin = authentication.getAuthorities().stream()
					.anyMatch(auth -> auth.getAuthority().equals("ROLE_ADMIN"));
			model.addAttribute("isAdmin", isAdmin);
		}
		return "main"; // main.html 반환
	}
}
  • SecurityContextHolder를 통한 인증 정보 가져오기.
    SecurityContextHolder.getContext().getAuthentication(); : 현재 로그인한 사용자의 인증정보를 가져와서
    Authentication 객체로 저장.
  • authentication != null && authentication.getAuthorities() != null : 인증된 사용자인지 확인 && 사용자가 권한 정보를 제대로 가지고 있는지 확인
  •  권한 확인 로직
    authentication.getAuthorities() : 현재 사용자가 가진 권한 목록을 반환하고, .stream().anyMatch() 를 사용해 사용자가 가진 권한 중 .getAuthority().equals("ROLE_ADMIN") 를 통해 "ROLE_ADMIN"인지 확인.
  • boolean isAdmin : 권한 확인 로직을 통해서 isAdmin이 ROLE_ADMIN 권한이 있는지 나타낸다.

main.html 수정

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>메인 페이지</title>
</head>
<body>
<div class="main">
	<h1>메인 화면</h1>
	<a th:href="@{/logout}" class="logout">로그아웃</a>
    
	<div th:if="${isAdmin}">
		<a th:href="@{/admin/adminMain}" class="page">관리자페이지</a>
	</div>
    
	<div th:unless="${isAdmin}">
		<a th:href="@{/mypage}" class="page">마이페이지</a>
	</div>
</div>
</body>
</html>

 

++ 관리자용 사용자를 추가 후 테스트 ++

데이터베이스에 관리자용 사용자 추가
일반 사용자로 로그인
관리자로 로그인

 

관리자 페이지 생성

AdminController, adminMain.html

package com.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/admin") // 공통 경로 설정
public class AdminController {

	// 관리자 페이지
	@GetMapping("/adminMain")
	public String adminMainView(Model model) {
		return "admin/adminMain";
	}
}
  • @RequestMapping("/admin")
    • 공통 경로 설정 : 컨트롤러 내부의 모든 요청 메서드 경로 앞에 공통적으로 추가된다. ((ex) 관리자 페이지의 요청 메서드 경로는 /admin 이지만, 실제 매핑 경로는 /admin/adminMain이 됨.)
    • 중복 제거 : 경로를 반복적으로 작성하지 않아도 되므로 코드의 가독성유지보수성이 좋아진다.
      (컨트롤러에서 여러 메서드가 /admin을 기준으로 동작하는 경우, 메서드마다 경로를 매번 /admin/으로 시작하지 않아도 됨.)
    • 그룹화 : 관련된 URL 경로를 하나의 컨트롤러로 그룹화할 때 유용합니다. ((ex) 관리자 관련 기능은 모두 /admin 경로 하위에서 관리할 수 있다.)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>관리자 메인 페이지</title>
</head>
<body>
<div class="admin">
	<h1>관리자 페이지</h1>
	<a th:href="@{/logout}" class="logout">로그아웃</a>
	
	<div class="category">
		<a th:href="@{/admin/member}" class="page">회원관리</a>
		<a th:href="@{/admin/aaa}" class="page">미정</a>
		<a th:href="@{/admin/bbb}" class="page">미정</a>
	</div>
</div>
</body>
</html>

관리자 페이지

회원관리 매핑, HTML 작성

// 회원 관리 페이지
@GetMapping("/member")
public String memberView(Model model) {
    // 전체 회원 조회
    List<Member> members = memberService.getAllMembers();
    model.addAttribute("members", members);

    return "admin/member";
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>회원 관리</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script th:src="@{/js/admin.js}"></script>
</head>
<body>
<div class="member">
	<h1>회원 관리</h1>
	<a th:href="@{/admin/adminMain}" class="page">관리자 페이지</a>
	<a th:href="@{/admin/aaa}" class="page">미정</a>
	<a th:href="@{/admin/aaa}" class="page">미정</a>
    
	<!-- 회원 id 검색 -->
	<div class="search">
		<label for="searchMember"></label>
		<input type="text" id="searchMember" placeholder="회원 검색 ( ID를 입력하세요 )">
	</div>
    
	<!-- 회원 리스트 -->
	<div class="member-list" th:if="${#lists.isEmpty(members)}">
		<h3>회원이 없습니다..</h3>
	</div>
	<div class="member-list" th:unless="${#lists.isEmpty(members)}">
		<table>
			<thead>
				<tr>
					<th>No.</th>
					<th>아이디</th>
					<th>이름</th>
					<th>역할</th>
				</tr>
			</thead>
			<tbody id="memberTableBody">
				<tr th:each="member : ${members}">
					<td th:text="${member.memberSeq}"></td>
					<td th:text="${member.id}"></td>
					<td th:text="${member.name}"></td>
					<td th:text="${member.roles}"></td>
				</tr>
			</tbody>
		</table>
	</div>
</div>
</body>
</html>

 

(id 검색) 스크립트

// 검색 입력에 따른 실시간 필터링 기능
$(document).ready(function() {
    $('#searchMember').on('input', function() {
        const searchValue = $(this).val().toLowerCase(); // 입력값을 소문자로 변환
        $('#memberTableBody tr').filter(function() {
            // 각 행의 ID 값을 가져와 검색어 포함 여부를 확인
            const memberId = $(this).find('td:nth-child(2)').text().toLowerCase();
            $(this).toggle(memberId.indexOf(searchValue) > -1);
        });
    });
});

 

id 검색

 

'개인 공부' 카테고리의 다른 글

Spring Security를 활용한 로그인!?!?  (0) 2024.12.21
MVC 패턴이 뭔데...  (1) 2024.12.18