feature/admin #5

Merged
gh.yeom merged 6 commits from feature/admin into main 2024-05-10 17:26:59 +09:00
16 changed files with 267 additions and 42 deletions
Showing only changes of commit c4e33cf22f - Show all commits

View File

@ -21,6 +21,7 @@ dependencies {
// thymeleaf
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.3.0'
// jpa

View File

@ -12,7 +12,7 @@ services:
environment:
- TZ=Asia/Seoul
- MARIADB_ROOT_PASSWORD=root
- MARIADB_DATABASE=login
- MARIADB_DATABASE=admin-system
- MARIADB_USER=admin
- MARIADB_PASSWORD=1234

View File

@ -1,22 +1,59 @@
package com.bpgroup.poc.admin.app.login;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import java.util.HashSet;
import java.util.Set;
@Getter
@ToString
public class LoginResult {
private Long id;
private String loginId;
private String name;
private String email;
public static LoginResult of(Long id, String loginId, String name, String email) {
private Set<MenuInfo> menus = new HashSet<>();
public static LoginResult of(Long id, String loginId, String name, String email, Set<MenuInfo> menus) {
LoginResult loginResult = new LoginResult();
loginResult.id = id;
loginResult.loginId = loginId;
loginResult.name = name;
loginResult.email = email;
loginResult.menus = menus;
return loginResult;
}
@Getter
@ToString
@EqualsAndHashCode
public static class MenuInfo {
private String menuGroupUri;
private String menuGroupName;
private Integer menuGroupSortOrder;
private String menuUri;
private String menuName;
private Integer menuSortOrder;
public static MenuInfo of(
String menuGroupUri,
String menuGroupName,
Integer menuGroupSortOrder,
String menuUri,
String menuName,
Integer menuSortOrder
) {
MenuInfo menuInfo = new MenuInfo();
menuInfo.menuGroupUri = menuGroupUri;
menuInfo.menuGroupName = menuGroupName;
menuInfo.menuGroupSortOrder = menuGroupSortOrder;
menuInfo.menuUri = menuUri;
menuInfo.menuName = menuName;
menuInfo.menuSortOrder = menuSortOrder;
return menuInfo;
}
}
}

View File

@ -7,11 +7,16 @@ import com.bpgroup.poc.admin.domain.admin.AdministratorRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
@Transactional
public class LoginService {
private final AdministratorRepository loginRepository;
@ -32,8 +37,27 @@ public class LoginService {
administrator.get().getId(),
administrator.get().getLoginId(),
administrator.get().getName(),
administrator.get().getLoginId()
administrator.get().getLoginId(),
getMenus(administrator.get())
);
}
private static LinkedHashSet<LoginResult.MenuInfo> getMenus(Administrator administrator) {
return administrator.getAdministratorRole().getRole().getRoleMenus().stream()
.map(roleMenu -> LoginResult.MenuInfo.of(
roleMenu.getMenu().getMenuGroup().getUri(),
roleMenu.getMenu().getMenuGroup().getName(),
roleMenu.getMenu().getMenuGroup().getSortOrder(),
roleMenu.getMenu().getUri(),
roleMenu.getMenu().getName(),
roleMenu.getMenu().getSortOrder()
))
.sorted(
Comparator
.comparingInt(LoginResult.MenuInfo::getMenuGroupSortOrder)
.thenComparingInt(LoginResult.MenuInfo::getMenuSortOrder)
)
.collect(Collectors.toCollection(LinkedHashSet::new));
}
}

View File

@ -1,14 +1,29 @@
package com.bpgroup.poc.admin.domain.admin;
import lombok.RequiredArgsConstructor;
import com.bpgroup.poc.admin.domain.BaseEntity;
import jakarta.persistence.*;
import lombok.Getter;
@RequiredArgsConstructor
public enum Menu {
ADMINISTRATOR_MANAGEMENT("/admin/management", "관리자 관리"),
ROLE_MANAGEMENT("/admin/role", "권한 관리"),
MENU_MANAGEMENT("/admin/menu", "메뉴 관리")
;
@Getter
@Entity
@Table(name = "menu")
public class Menu extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "uri", length = 100, nullable = false)
private String uri;
@Column(name = "name", length = 100, nullable = false)
private String name;
@Column(name = "sort_order", nullable = false)
private Integer sortOrder;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "menu_group_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
private MenuGroup menuGroup;
private final String uri;
private final String name;
}

View File

@ -0,0 +1,25 @@
package com.bpgroup.poc.admin.domain.admin;
import com.bpgroup.poc.admin.domain.BaseEntity;
import jakarta.persistence.*;
import lombok.Getter;
@Getter
@Entity
@Table(name = "menu_group")
public class MenuGroup extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "uri", length = 100, nullable = false)
private String uri;
@Column(name = "name", length = 100, nullable = false)
private String name;
@Column(name = "sort_order", nullable = false)
private Integer sortOrder;
}

View File

@ -0,0 +1,6 @@
package com.bpgroup.poc.admin.domain.admin;
import org.springframework.data.jpa.repository.JpaRepository;
public interface MenuRepository extends JpaRepository<Menu, Long> {
}

View File

@ -2,10 +2,12 @@ package com.bpgroup.poc.admin.domain.admin;
import com.bpgroup.poc.admin.domain.BaseEntity;
import jakarta.persistence.*;
import lombok.Getter;
import java.util.ArrayList;
import java.util.List;
import java.util.HashSet;
import java.util.Set;
@Getter
@Entity
@Table(name = "role")
public class Role extends BaseEntity {
@ -21,6 +23,6 @@ public class Role extends BaseEntity {
private String description;
@OneToMany(mappedBy = "role", fetch = FetchType.LAZY)
private List<RoleMenu> roleMenus = new ArrayList<>();
private Set<RoleMenu> roleMenus = new HashSet<>();
}

View File

@ -2,9 +2,11 @@ package com.bpgroup.poc.admin.domain.admin;
import com.bpgroup.poc.admin.domain.BaseEntity;
import jakarta.persistence.*;
import lombok.Getter;
@Table
@Entity(name = "role_menu")
@Getter
@Entity
@Table(name = "role_menu")
public class RoleMenu extends BaseEntity {
@Id
@ -15,8 +17,8 @@ public class RoleMenu extends BaseEntity {
@JoinColumn(name = "role_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
private Role role;
@Enumerated(EnumType.STRING)
@Column(name = "menu", length = 100, nullable = false)
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "menu_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
private Menu menu;
}

View File

@ -1,7 +1,11 @@
package com.bpgroup.poc.admin.security.authentication;
import com.bpgroup.poc.admin.app.login.LoginResult;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
@Getter
public class AuthenticationDetail {
@ -11,12 +15,74 @@ public class AuthenticationDetail {
private String name;
private String email;
private MultiValueMap<MenuOneDepth, MenuTwoDepth> menus = new LinkedMultiValueMap<>();
public static AuthenticationDetail from(LoginResult result) {
AuthenticationDetail authenticationDetail = new AuthenticationDetail();
authenticationDetail.id = result.getId();
authenticationDetail.loginId = result.getLoginId();
authenticationDetail.name = result.getName();
authenticationDetail.email = result.getEmail();
result.getMenus().forEach(menu -> authenticationDetail.menus.add(
MenuOneDepth.of(
menu.getMenuGroupUri(),
menu.getMenuGroupName(),
menu.getMenuGroupSortOrder()
),
MenuTwoDepth.of(
menu.getMenuUri(),
menu.getMenuName(),
menu.getMenuSortOrder()
)
));
return authenticationDetail;
}
/**
* EqualsAndHashCode annotattion이 없을 경우 MultiValueMap을 사용할 중복된 key를 찾지 못함
*/
@Getter
@ToString
@EqualsAndHashCode
private static class MenuOneDepth {
private String uri;
private String name;
private Integer sortOrder;
public static MenuOneDepth of(
String uri,
String name,
Integer sortOrder
) {
MenuOneDepth menu = new MenuOneDepth();
menu.uri = uri;
menu.name = name;
menu.sortOrder = sortOrder;
return menu;
}
}
@Getter
@ToString
private static class MenuTwoDepth {
private String uri;
private String name;
private Integer sortOrder;
public static MenuTwoDepth of(
String uri,
String name,
Integer sortOrder
) {
MenuTwoDepth menu = new MenuTwoDepth();
menu.uri = uri;
menu.name = name;
menu.sortOrder = sortOrder;
return menu;
}
}
}

View File

@ -0,0 +1,4 @@
package com.bpgroup.poc.admin.values;
public class AdministratorMenu {
}

View File

@ -4,12 +4,14 @@ import com.bpgroup.poc.admin.security.authentication.AuthenticationFailReason;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/login")
public class LoginController {
@GetMapping("/login")
@GetMapping
public String loginPage(
@RequestParam(required = false) AuthenticationFailReason error,
Model model

View File

@ -4,7 +4,7 @@ spring:
mode: always
datasource:
url: jdbc:mariadb://localhost:3307/login
url: jdbc:mariadb://localhost:3307/admin-system
username: admin
password: 1234
driver-class-name: org.mariadb.jdbc.Driver

View File

@ -1,6 +1,6 @@
spring:
application:
name: login
name: admin
profiles:
default: local

View File

@ -1,5 +1,6 @@
INSERT INTO `administrator` (`login_id`, `password`, `email`, `name`, `create_date`, `update_date`)
VALUES ('admin', '$2a$10$g6UOrQ/OS8o5r5CJk7C5juVFaItQ62U3EIn8zLPzkFplM3wVLvKZ2', 'admin@admin.com', '홍길동', CURDATE(), CURDATE());
VALUES ('admin', '$2a$10$g6UOrQ/OS8o5r5CJk7C5juVFaItQ62U3EIn8zLPzkFplM3wVLvKZ2', 'admin@admin.com', '홍길동', CURDATE(),
CURDATE());
INSERT INTO `role` (`name`, `description`, `create_date`, `update_date`)
VALUES ('SUPER_ADMIN', '최고 관리자', CURDATE(), CURDATE()),
@ -9,9 +10,47 @@ VALUES ('SUPER_ADMIN', '최고 관리자', CURDATE(), CURDATE()),
INSERT INTO `administrator_role` (`administrator_id`, `role_id`, `create_date`, `update_date`)
VALUES ('1', '1', CURDATE(), CURDATE());
INSERT INTO `role_menu` (`role_id`, `menu`, `create_date`, `update_date`)
VALUES ('1', 'ADMINISTRATOR_MANAGEMENT', CURDATE(), CURDATE()),
('1', 'ROLE_MANAGEMENT', CURDATE(), CURDATE()),
('1', 'MENU_MANAGEMENT', CURDATE(), CURDATE());
INSERT INTO `menu_group` (`uri`, `name`, `sort_order`, `create_date`, `update_date`)
VALUES ('/admin', '관리자 관리', 2, CURDATE(), CURDATE()),
('/settings', '설정', 3, CURDATE(), CURDATE()),
('/temp', '임시', 1, CURDATE(), CURDATE());
INSERT INTO `menu` (`uri`, `name`, `sort_order`, `menu_group_id`, `create_date`, `update_date`)
VALUES ('/admin/management', '관리자 관리', 1, 1, CURDATE(), CURDATE()),
('/admin/role', '권한 관리', 2, 1, CURDATE(), CURDATE()),
('/admin/temp01', '관리자 임시 1', 4, 1, CURDATE(), CURDATE()),
('/admin/temp02', '관리자 임시 2', 3, 1, CURDATE(), CURDATE())
;
INSERT INTO `menu` (`uri`, `name`, `sort_order`, `menu_group_id`, `create_date`, `update_date`)
VALUES ('/temp/temp01', '임시 01', 4, 3, CURDATE(), CURDATE()),
('/temp/temp02', '임시 02', 2, 3, CURDATE(), CURDATE()),
('/temp/temp03', '임시 03', 1, 3, CURDATE(), CURDATE()),
('/temp/temp04', '임시 04', 3, 3, CURDATE(), CURDATE())
;
INSERT INTO `menu` (`uri`, `name`, `sort_order`, `menu_group_id`, `create_date`, `update_date`)
VALUES ('/settings/code', '코드 관리', 1, 2, CURDATE(), CURDATE()),
('/settings/component', '컴포넌트', 1, 2, CURDATE(), CURDATE()),
('/settings/temp01', '세팅 임시 1', 4, 2, CURDATE(), CURDATE()),
('/settings/temp02', '세팅 임시 2', 3, 2, CURDATE(), CURDATE())
;
INSERT INTO `role_menu` (`role_id`, `menu_id`, `create_date`, `update_date`)
VALUES ('1', '1', CURDATE(), CURDATE()),
('1', '2', CURDATE(), CURDATE()),
('1', '3', CURDATE(), CURDATE()),
('1', '4', CURDATE(), CURDATE()),
('1', '5', CURDATE(), CURDATE()),
('1', '6', CURDATE(), CURDATE()),
('1', '7', CURDATE(), CURDATE()),
('1', '8', CURDATE(), CURDATE()),
('1', '9', CURDATE(), CURDATE()),
('1', '10', CURDATE(), CURDATE()),
('1', '11', CURDATE(), CURDATE()),
('1', '12', CURDATE(), CURDATE())
;

View File

@ -6,18 +6,20 @@
<nav class="menu_wrap flexitem">
<h2>메뉴</h2>
<button class="closeButton"><img th:src="@{/images/ico_close.svg}"></button>
<div th:if="${#authorization.expression('isAuthenticated()')}">
<div th:each="entry : ${#authentication.details.getMenus().entrySet()}">
<a class="nav_tit"
th:classappend="${pathInfo.activeClass('/admin')}">
관리자
th:classappend="${pathInfo.activeClass(entry.key.getUri())}"
th:text="${entry.key.getName()}">
</a>
<ul class="nav_section">
<li>
<a th:with="path='/admin/management'" th:href="${path}"
th:classappend="${pathInfo.activeClass(path)}">관리자 관리</a>
<a th:with="path='/admin/role'" th:href="${path}"
th:classappend="${pathInfo.activeClass(path)}">권한 관리</a>
<li th:each="menu : ${entry.value}">
<a th:with="path= ${menu.getUri()}" th:href="${path}"
th:classappend="${pathInfo.activeClass(path)}"><span th:text="${menu.getName()}"></span></a>
</li>
</ul>
</div>
</div>
</nav>
</th:block>
</body>