header, footer 로고 추가.

로그인 페이지 이미지 추가.
사용자관리 개인정보 열람, 수정 기능 추가.
게시판 분류 관리 페이지 작업 후 사용자관리 페이지 권한 설정 작업 이어서 해야함.
master
강석 최 2021-12-06 17:12:41 +09:00
parent 57711f4001
commit c7f3f45ae3
22 changed files with 291 additions and 61 deletions

View File

@ -1,23 +1,31 @@
package com.dbnt.kcgfilemanager; package com.dbnt.kcgfilemanager;
import com.dbnt.kcgfilemanager.model.UserInfo; import com.dbnt.kcgfilemanager.model.UserInfo;
import com.dbnt.kcgfilemanager.service.CommonCodeService;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpSession;
import java.security.Principal; import java.security.Principal;
@RestController @RestController
@RequiredArgsConstructor
public class BaseController { public class BaseController {
private final CommonCodeService commonCodeService;
@GetMapping("/") @GetMapping("/")
public ModelAndView loginCheck(Principal principal) { public ModelAndView loginCheck(Principal principal, HttpSession session) {
ModelAndView mav = null; ModelAndView mav = null;
if(principal == null){ if(principal == null){
mav = new ModelAndView("redirect:/user/login"); mav = new ModelAndView("redirect:/user/login");
}else{ }else{
session.setAttribute("positionList", commonCodeService.selectCommonCodeValue("POSITION"));
session.setAttribute("departmentList", commonCodeService.selectCommonCodeValue("DEPARTMENT"));
if(((UserInfo)((UsernamePasswordAuthenticationToken) principal).getPrincipal()).getUserRole().indexOf("ADMIN")>0){ if(((UserInfo)((UsernamePasswordAuthenticationToken) principal).getPrincipal()).getUserRole().indexOf("ADMIN")>0){
mav = new ModelAndView("redirect:/admin/main"); mav = new ModelAndView("redirect:/admin/main");
}else{ }else{

View File

@ -5,16 +5,10 @@ import com.dbnt.kcgfilemanager.model.UserInfo;
import com.dbnt.kcgfilemanager.service.CommonCodeService; import com.dbnt.kcgfilemanager.service.CommonCodeService;
import com.dbnt.kcgfilemanager.service.UserInfoService; import com.dbnt.kcgfilemanager.service.UserInfoService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
@RestController @RestController
@RequiredArgsConstructor @RequiredArgsConstructor
@ -35,8 +29,6 @@ public class adminController {
userInfo.setQueryInfo(); userInfo.setQueryInfo();
ModelAndView mav = new ModelAndView("admin/userMgt"); ModelAndView mav = new ModelAndView("admin/userMgt");
mav.addObject("userInfoList", userInfoService.selectUserInfoList(userInfo)); mav.addObject("userInfoList", userInfoService.selectUserInfoList(userInfo));
mav.addObject("positionList", commonCodeService.selectCommonCodeValue("POSITION"));
mav.addObject("departmentList", commonCodeService.selectCommonCodeValue("DEPARTMENT"));
userInfo.setContentCnt(userInfoService.selectUserInfoListCnt(userInfo)); userInfo.setContentCnt(userInfoService.selectUserInfoListCnt(userInfo));
userInfo.setPaginationInfo(); userInfo.setPaginationInfo();
mav.addObject("searchParams", userInfo); mav.addObject("searchParams", userInfo);
@ -48,6 +40,17 @@ public class adminController {
return userInfoService.insertUserInfo(userInfo); return userInfoService.insertUserInfo(userInfo);
} }
@PostMapping("/updateUserInfo")
public String updateUserInfo(UserInfo userInfo){
return userInfoService.updateUserInfo(userInfo);
}
@GetMapping("/selectUserInfo")
public ModelAndView selectUserInfo(UserInfo userInfo){
ModelAndView mav = new ModelAndView("admin/userInfo");
mav.addObject("userInfo", userInfoService.selectUserInfo(userInfo));
return mav;
}
@GetMapping("/modifyRequest") @GetMapping("/modifyRequest")
public ModelAndView modifyRequest() { public ModelAndView modifyRequest() {
ModelAndView mav = new ModelAndView("admin/modifyRequest"); ModelAndView mav = new ModelAndView("admin/modifyRequest");

View File

@ -41,7 +41,7 @@ public class BaseModel {
int pageIndex = getPageIndex(); int pageIndex = getPageIndex();
int startNum = pageIndex - 2; int startNum = pageIndex - 2;
if(startNum < 0){ if(startNum <= 0){
startNum = 1; startNum = 1;
} }
setStartNum(startNum); setStartNum(startNum);

View File

@ -3,6 +3,7 @@ package com.dbnt.kcgfilemanager.model;
import lombok.*; import lombok.*;
import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate; import org.hibernate.annotations.DynamicUpdate;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
@ -38,7 +39,7 @@ public class UserInfo extends BaseModel implements UserDetails{
private int department; private int department;
@Column(name = "USER_ROLE") @Column(name = "USER_ROLE")
private String userRole; private String userRole;
@Column(name = "CREATE_DATE") @Column(name = "CREATE_DATE", updatable = false)
private LocalDateTime createDate; private LocalDateTime createDate;
@Transient @Transient

View File

@ -4,6 +4,7 @@ import com.dbnt.kcgfilemanager.mapper.UserInfoMapper;
import com.dbnt.kcgfilemanager.model.UserInfo; import com.dbnt.kcgfilemanager.model.UserInfo;
import com.dbnt.kcgfilemanager.repository.UserInfoRepository; import com.dbnt.kcgfilemanager.repository.UserInfoRepository;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.UsernameNotFoundException;
@ -12,6 +13,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.List; import java.util.List;
import java.util.Optional;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
@ -22,11 +24,26 @@ public class UserInfoService implements UserDetailsService {
@Transactional @Transactional
public String insertUserInfo(UserInfo userInfo){ public String insertUserInfo(UserInfo userInfo){
Pbkdf2PasswordEncoder passwordEncoder = new Pbkdf2PasswordEncoder(); userInfo.setPassword(convertPassword(userInfo.getPassword()));
userInfo.setPassword(passwordEncoder.encode(userInfo.getPassword()));
return userInfoRepository.save(userInfo).getUserId(); return userInfoRepository.save(userInfo).getUserId();
} }
@Transactional
public String updateUserInfo(UserInfo userInfo){
UserInfo targetUserInfo = userInfoRepository.findById(userInfo.getUserSeq()).orElse(null);
if(userInfo.getPassword() != null){
targetUserInfo.setPassword(convertPassword(userInfo.getPassword()));
}
targetUserInfo.setName(userInfo.getName());
targetUserInfo.setPosition(userInfo.getPosition());
targetUserInfo.setDepartment(userInfo.getDepartment());
targetUserInfo.setUserRole(userInfo.getUserRole());
return targetUserInfo.getUserId();
}
private String convertPassword(String password){
Pbkdf2PasswordEncoder passwordEncoder = new Pbkdf2PasswordEncoder();
return passwordEncoder.encode(password);
}
@Override @Override
public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException { public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
@ -40,4 +57,8 @@ public class UserInfoService implements UserDetailsService {
public Integer selectUserInfoListCnt(UserInfo userInfo) { public Integer selectUserInfoListCnt(UserInfo userInfo) {
return userInfoMapper.selectUserInfoListCnt(userInfo); return userInfoMapper.selectUserInfoListCnt(userInfo);
} }
public UserInfo selectUserInfo(UserInfo userInfo) {
return userInfoRepository.findById(userInfo.getUserSeq()).orElse(null);
}
} }

View File

@ -39,6 +39,7 @@
AND A.CREATE_DATE &lt;= #{endDate} AND A.CREATE_DATE &lt;= #{endDate}
</if> </if>
</where> </where>
ORDER BY CREATE_DATE DESC
LIMIT #{viewCnt} OFFSET #{firstIndex} LIMIT #{viewCnt} OFFSET #{firstIndex}
</select> </select>

View File

@ -1,4 +1,6 @@
#loginPage{
background-image: url("/img/img01.jpg");
}
.form-signin{ .form-signin{
width: 100%; width: 100%;
max-width: 330px; max-width: 330px;

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1013 KiB

View File

@ -104,8 +104,8 @@ function getValues(category){
data: {category: category}, data: {category: category},
type: 'GET', type: 'GET',
dataType:"html", dataType:"html",
success: function(data){ success: function(html){
$("#valueDiv").empty().append(data) $("#valueDiv").empty().append(html)
}, },
error:function(){ error:function(){

View File

@ -5,6 +5,24 @@ $(function(){
language: "ko" language: "ko"
}); });
}) })
$(document).on('click', '.userInfoTr', function (){
$(".userInfoCheckBox").prop('checked', false);
const target = $(this).find(".userInfoCheckBox")[0];
target.checked = true;
const selectedTab = $(".nav-tabs").find(".active")[0].id;
if(selectedTab === "infoTab"){
getUserInfo(target.value);
}else if(selectedTab === "categoryTab"){
getCategoryRole(target.value);
}
})
$(document).on('click', '#infoTab', function (){
getUserInfo(getUserSeq())
})
$(document).on('click', '#categoryTab', function (){
getCategoryRole(getUserSeq())
})
$(document).on('click', '.page-item', function (){ $(document).on('click', '.page-item', function (){
$("#pageIndex").val($(this).attr("data-pageindex")); $("#pageIndex").val($(this).attr("data-pageindex"));
@ -14,11 +32,20 @@ $(document).on('click', '.page-item', function (){
$(document).on('change', '#searchConditionSelector', function (){ $(document).on('change', '#searchConditionSelector', function (){
setSearchCondition(); setSearchCondition();
}) })
$(document).on('change', '#passwordUpdateFlag', function (){
const passwordDiv = $(".passwordDiv");
if(this.checked){
passwordDiv.show();
passwordDiv.find("input").removeAttr("disabled");
}else{
passwordDiv.hide();
passwordDiv.find("input").attr("disabled", "disabled");
}
})
$(document).on('click', '#saveBtn', function (){ $(document).on('click', '#saveBtn', function (){
if(valueCheck()){ if(valueCheck("userInfoInsert")){
if(confirm("저장하시겠습니까?")){ if(confirm("저장하시겠습니까?")){
const formData = new FormData($("#userInfoForm")[0]); const formData = new FormData($("#userInfoInsert")[0]);
$.ajax({ $.ajax({
type : 'POST', type : 'POST',
data : formData, data : formData,
@ -38,29 +65,71 @@ $(document).on('click', '#saveBtn', function (){
} }
}) })
function valueCheck(){ $(document).on('click', '#updateBtn', function (){
const password = $("#password").val(); if(valueCheck("userInfoUpdate")){
const passwordConfirm = $("#passwordConfirm").val(); if(confirm("저장하시겠습니까?")){
const formData = new FormData($("#userInfoUpdate")[0]);
$.ajax({
type : 'POST',
data : formData,
url : "/admin/updateUserInfo",
processData: false,
contentType: false,
success : function(data) {
alert("저장되었습니다.")
$(".userInfoCheckBox:checked").click();
},
error : function(xhr, status) {
}
})
}
}
})
function valueCheck(form){
const targetForm = $("#"+form);
const userId = targetForm.find("#userId").val();
const password = targetForm.find("#password");
const passwordConfirm = targetForm.find("#passwordConfirm");
const name = targetForm.find("#name").val()
let returnFlag = true; let returnFlag = true;
if(!$("#userId").val()){
if(!userId){
alert("아이디를 입력해주세요."); alert("아이디를 입력해주세요.");
returnFlag = false; returnFlag = false;
}else{
const idReg = /^[a-z]+[a-z0-9]{5,19}$/g;
if(!idReg.test(userId)){
returnFlag = false;
alert("아이디 조건이 맞지 않습니다.")
} }
if(!password){ }
if(!password[0].disabled && !password.val()){
alert("비밀번호를 입력해주세요."); alert("비밀번호를 입력해주세요.");
returnFlag = false; returnFlag = false;
} }
if(!passwordConfirm){ if(!password[0].disabled && !passwordConfirm.val()){
alert("비밀번호 확인을 입력해주세요."); alert("비밀번호 확인을 입력해주세요.");
returnFlag = false; returnFlag = false;
} }
if(returnFlag && password !== passwordConfirm){ if(!name){
alert("이름 입력해주세요.");
returnFlag = false;
}
if(returnFlag){
const passwordReg = /^(?=.*[a-zA-z])(?=.*[0-9])(?=.*[$`~!@$!%*#^?&\\(\\)\-_=+]).{8,16}$/;
if(!password[0].disabled){
if(!passwordReg.test(password.val())){
alert("비밀번호 조건이 맞지 않습니다.")
returnFlag = false;
}else{
if(password.val() !== passwordConfirm.val()){
alert("비밀번호가 같지 않습니다."); alert("비밀번호가 같지 않습니다.");
returnFlag = false; returnFlag = false;
} }
if(!$("#name").val()){ }
alert("이름 입력해주세요."); }
returnFlag = false;
} }
return returnFlag; return returnFlag;
} }
@ -83,5 +152,42 @@ function setSearchCondition(){
} }
} }
function formReset(){ function formReset(){
document.getElementById('userInfoForm').reset(); document.getElementById('userInfoInsert').reset();
}
function getUserSeq(){
return $(".userInfoCheckBox:checked").val();
}
function getUserInfo(userSeq){
if(userSeq !== undefined){
$.ajax({
url: '/admin/selectUserInfo',
data: {userSeq: userSeq},
type: 'GET',
dataType:"html",
success: function(html){
$("#userContent").empty().append(html)
},
error:function(){
}
});
}
}
function getCategoryRole(userSeq){
if(userSeq !== undefined){
/*$.ajax({
url: '/admin/codeValue',
data: {category: category},
type: 'GET',
dataType:"html",
success: function(data){
$("#valueDiv").empty().append(data)
},
error:function(){
}
});*/
}
} }

View File

@ -0,0 +1,70 @@
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<form id="userInfoUpdate" class="row-cols-auto p-3" action="#" th:action="@{/admin/updateUserInfo}" method="post">
<input type="hidden" name="userSeq" th:value="${userInfo.userSeq}">
<div class="mb-3 row">
<label for="userId" class="col-sm-4 col-form-label">아이디</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="userId" name="userId" th:value="${userInfo.userId}" readonly>
</div>
</div>
<div class="row">
<div class="col-sm-4"></div>
<div class="col-sm-auto form-check ms-3">
<input class="form-check-input" type="checkbox" id="passwordUpdateFlag">
<label class="form-check-label" for="passwordUpdateFlag">비밀번호 수정</label>
</div>
</div>
<div class="mb-3 row passwordDiv" style="display: none">
<label for="password" class="col-sm-4 col-form-label">비밀번호</label>
<div class="col-sm-6">
<input type="password" class="form-control" id="password" name="password" disabled>
</div>
<div class="col-sm-auto form-text mx-auto">
8~16자 사이의 알파벳, 숫자, 특수문자 조합을 입력해주세요.
</div>
</div>
<div class="mb-3 row passwordDiv" style="display: none">
<label for="passwordConfirm" class="col-sm-4 col-form-label">비밀번호 확인</label>
<div class="col-sm-6">
<input type="password" class="form-control" id="passwordConfirm" disabled>
</div>
</div>
<div class="mb-3 row">
<label for="name" class="col-sm-4 col-form-label">이름</label>
<div class="col-sm-6">
<input type="text" class="form-control" id="name" name="name" autocomplete="off" th:value="${userInfo.name}">
</div>
</div>
<div class="mb-3 row">
<label for="department" class="col-sm-4 col-form-label">부서</label>
<div class="col-sm-6">
<select class="form-select" id="department" name="department">
<th:block th:each="commonCode:${session.departmentList}">
<option th:value="${commonCode.codeSq}" th:text="${commonCode.value}" th:selected="${commonCode.codeSq == userInfo.department}"></option>
</th:block>
</select>
</div>
</div>
<div class="mb-3 row">
<label for="position" class="col-sm-4 col-form-label">직급</label>
<div class="col-sm-6">
<select class="form-select" id="position" name="position">
<th:block th:each="commonCode:${session.positionList}">
<option th:value="${commonCode.codeSq}" th:text="${commonCode.value}" th:selected="${commonCode.codeSq==userInfo.position}"></option>
</th:block>
</select>
</div>
</div>
<div class="mb-3 row">
<div class="col-sm-4"></div>
<div class="col-sm-auto form-check ms-3">
<input type="hidden" name="userRole" value="ROLE_MEMBER">
<input class="form-check-input" type="checkbox" value="ROLE_ADMIN" id="roleAdmin" name="userRole" th:checked="${#strings.contains(userInfo.userRole,'ADMIN')}">
<label class="form-check-label" for="roleAdmin">관리자 계정</label>
</div>
</div>
</form>
<div class="row-cols-6 gap-2 p-3">
<input type="button" class="btn btn-warning w-50" id="updateBtn" value="수정하기">
</div>

View File

@ -8,9 +8,7 @@
<script type="text/javascript" th:src="@{/js/admin/userMgt.js}"></script> <script type="text/javascript" th:src="@{/js/admin/userMgt.js}"></script>
</th:block> </th:block>
<div layout:fragment="content"> <div layout:fragment="content">
<main> <main class="pt-3">
<input type="hidden" name="_csrf_header" th:value="${_csrf.headerName}"/>
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<h4>사용자 관리</h4> <h4>사용자 관리</h4>
<div class="row mx-0"> <div class="row mx-0">
<div class="col-12 card text-center"> <div class="col-12 card text-center">
@ -83,7 +81,7 @@
<input type="checkbox" class="userInfoCheckBox" th:value="${userInfo.userSeq}"> <input type="checkbox" class="userInfoCheckBox" th:value="${userInfo.userSeq}">
</td> </td>
<td th:text="${userInfo.userId}"></td> <td th:text="${userInfo.userId}"></td>
<td th:text="${userInfo.name}"></td> <td th:text="|${userInfo.name}${#strings.contains(userInfo.userRole,'ADMIN')?'(관리자)':''}|"></td>
<td th:text="${userInfo.positionName}"></td> <td th:text="${userInfo.positionName}"></td>
<td th:text="${userInfo.departmentName}"></td> <td th:text="${userInfo.departmentName}"></td>
<td th:text="${#temporals.format(userInfo.createDate, 'yyyy-MM-dd')}"></td> <td th:text="${#temporals.format(userInfo.createDate, 'yyyy-MM-dd')}"></td>
@ -104,7 +102,7 @@
</li> </li>
</th:block> </th:block>
<th:block th:each="num : ${#numbers.sequence(searchParams.startNum, searchParams.endNum)}"> <th:block th:each="num : ${#numbers.sequence(searchParams.startNum, searchParams.endNum)}">
<li class="page-item" th:data-pageindex="${num}"> <li class="page-item" th:data-pageindex="${num}" th:classappend="${searchParams.pageIndex==num?'active':''}">
<a class="page-link" href="#" th:text="${num}"></a> <a class="page-link" href="#" th:text="${num}"></a>
</li> </li>
</th:block> </th:block>
@ -132,8 +130,10 @@
<button class="nav-link" id="categoryTab" data-bs-toggle="tab" type="button" role="tab">권한</button> <button class="nav-link" id="categoryTab" data-bs-toggle="tab" type="button" role="tab">권한</button>
</li> </li>
</ul> </ul>
<div class="tab-content" id="userContent"> <div class="tab-content border border-top-0" id="userContent">
<div class="py-5">
<h3>왼쪽 목록에서 선택해주세요.</h3>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -150,18 +150,24 @@
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form id="userInfoForm" action="#" th:action="@{/admin/}" method="post"> <form id="userInfoInsert" action="#" th:action="@{/admin/insertUserInfo}" method="post">
<div class="mb-3 row"> <div class="mb-3 row">
<label for="userId" class="col-sm-4 col-form-label">아이디</label> <label for="userId" class="col-sm-4 col-form-label">아이디</label>
<div class="col-sm-6"> <div class="col-sm-6">
<input type="text" class="form-control" id="userId" name="userId" autocomplete="off"> <input type="text" class="form-control" id="userId" name="userId" autocomplete="off">
</div> </div>
<div class="col-sm-auto form-text mx-auto">
6~20자 사이의 알파벳, 숫자를 입력해주세요.
</div>
</div> </div>
<div class="mb-3 row"> <div class="mb-3 row">
<label for="password" class="col-sm-4 col-form-label">비밀번호</label> <label for="password" class="col-sm-4 col-form-label">비밀번호</label>
<div class="col-sm-6"> <div class="col-sm-6">
<input type="password" class="form-control" id="password" name="password"> <input type="password" class="form-control" id="password" name="password">
</div> </div>
<div class="col-sm-auto form-text mx-auto">
8~16자 사이의 알파벳, 숫자 조합을 입력해주세요.
</div>
</div> </div>
<div class="mb-3 row"> <div class="mb-3 row">
<label for="passwordConfirm" class="col-sm-4 col-form-label">비밀번호 확인</label> <label for="passwordConfirm" class="col-sm-4 col-form-label">비밀번호 확인</label>
@ -179,7 +185,7 @@
<label for="department" class="col-sm-4 col-form-label">부서</label> <label for="department" class="col-sm-4 col-form-label">부서</label>
<div class="col-sm-6"> <div class="col-sm-6">
<select class="form-select" id="department" name="department"> <select class="form-select" id="department" name="department">
<th:block th:each="commonCode:${departmentList}"> <th:block th:each="commonCode:${session.departmentList}">
<option th:value="${commonCode.codeSq}" th:text="${commonCode.value}"></option> <option th:value="${commonCode.codeSq}" th:text="${commonCode.value}"></option>
</th:block> </th:block>
</select> </select>
@ -189,12 +195,20 @@
<label for="position" class="col-sm-4 col-form-label">직급</label> <label for="position" class="col-sm-4 col-form-label">직급</label>
<div class="col-sm-6"> <div class="col-sm-6">
<select class="form-select" id="position" name="position"> <select class="form-select" id="position" name="position">
<th:block th:each="commonCode:${positionList}"> <th:block th:each="commonCode:${session.positionList}">
<option th:value="${commonCode.codeSq}" th:text="${commonCode.value}"></option> <option th:value="${commonCode.codeSq}" th:text="${commonCode.value}"></option>
</th:block> </th:block>
</select> </select>
</div> </div>
</div> </div>
<div class="mb-3 row">
<div class="col-sm-4"></div>
<div class="col-sm-auto form-check ms-3">
<input type="hidden" name="userRole" value="ROLE_MEMBER">
<input class="form-check-input" type="checkbox" value="ROLE_ADMIN" id="roleAdmin" name="userRole">
<label class="form-check-label" for="roleAdmin">관리자 계정</label>
</div>
</div>
</form> </form>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">

View File

@ -2,15 +2,19 @@
<html lang="ko" <html lang="ko"
xmlns:th="http://www.thymeleaf.org" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"> xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<footer th:fragment="footerFragment" class="d-flex flex-wrap justify-content-between align-items-center py-3 mt-auto border-top"> <footer th:fragment="footerFragment" class="d-flex flex-wrap py-2 mt-auto border-top bg-dark">
<ul class="col-md-8 d-flex align-items-center list-unstyled d-flex"> <div class="row">
<li class="ms-3"><span class="text-muted">21995 인천광역시 연수구 해돋이로 130 해양경찰청</span></li> <div class="col-auto">
<li class="ms-3"><span class="text-muted">COPYRIGHT(C)2021 MINISTRY OF REPUBLIC ADMINISTRATION AND SECURITY. ALL RIGHT RESERVED</span></li> <img th:src="@{/img/f_logo.png}" alt="logo" title="logo" style="height: 50px;"/>
</ul> </div>
<ul class="nav col-md-4 justify-content-end list-unstyled d-flex"> <div class="col-auto row">
<!--<li class="ms-3"><a class="text-muted" href="#"><svg class="bi" width="24" height="24"><use xlink:href="#twitter"></use></svg></a></li> <div class="col-12">
<li class="ms-3"><a class="text-muted" href="#"><svg class="bi" width="24" height="24"><use xlink:href="#instagram"></use></svg></a></li> <span class="text-muted">21995 인천광역시 연수구 해돋이로 130 해양경찰청</span>
<li class="ms-3"><a class="text-muted" href="#"><svg class="bi" width="24" height="24"><use xlink:href="#facebook"></use></svg></a></li>--> </div>
</ul> <div class="col-12">
<span class="text-muted">COPYRIGHT(C)2021 MINISTRY OF REPUBLIC ADMINISTRATION AND SECURITY. ALL RIGHT RESERVED</span>
</div>
</div>
</div>
</footer> </footer>
</html> </html>

View File

@ -2,11 +2,11 @@
<html lang="ko" <html lang="ko"
xmlns:th="http://www.thymeleaf.org" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"> xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<header th:fragment="headerFragment" class="d-flex flex-wrap justify-content-center py-3 px-3 mb-4 border-bottom"> <header th:fragment="headerFragment" class="d-flex flex-wrap justify-content-center py-1 px-3 border-bottom">
<a href="/" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-dark text-decoration-none"> <a href="/" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-dark text-decoration-none">
<span class="fs-4">해양경찰청 파일관리 시스템</span> <img th:src="@{/img/logo.png}" alt="logo" title="logo">
<!--<span class="fs-4">해양경찰청 파일관리 시스템</span>-->
</a> </a>
<ul class="nav nav-pills" sec:authorize="isAuthenticated()"> <ul class="nav nav-pills" sec:authorize="isAuthenticated()">
<li class="nav-item"><a href="#" class="nav-link">개인정보</a></li> <li class="nav-item"><a href="#" class="nav-link">개인정보</a></li>
<li class="nav-item"><a href="/logout" class="nav-link">로그아웃</a></li> <li class="nav-item"><a href="/logout" class="nav-link">로그아웃</a></li>

View File

@ -2,10 +2,10 @@
<html lang="ko" <html lang="ko"
xmlns:th="http://www.thymeleaf.org" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"> xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<div class="mx-2" th:fragment="leftMenuFragment"> <div class="mx-2 pt-3" th:fragment="leftMenuFragment">
<div sec:authorize="hasRole('ROLE_ADMIN')"> <div sec:authorize="hasRole('ROLE_ADMIN')">
<div class="list-group py-2"> <div class="list-group py-2">
<a href="#" class="list-group-item list-group-item-action disabled">게시판 분류 관리</a> <a href="/admin/categoryMgt" class="list-group-item list-group-item-action">게시판 분류 관리</a>
<a href="/admin/userMgt" class="list-group-item list-group-item-action">사용자 관리</a> <a href="/admin/userMgt" class="list-group-item list-group-item-action">사용자 관리</a>
<a href="/admin/modifyRequest" class="list-group-item list-group-item-action">수정 요청</a> <a href="/admin/modifyRequest" class="list-group-item list-group-item-action">수정 요청</a>
<a href="#" class="list-group-item list-group-item-action disabled">통계</a> <a href="#" class="list-group-item list-group-item-action disabled">통계</a>

View File

@ -4,8 +4,8 @@
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/layout}"> layout:decorate="~{layout/layout}">
<div layout:fragment="content" class="form-signin text-center"> <div layout:fragment="content" class="h-100" id="loginPage">
<main> <div class="form-signin text-center mt-5 rounded bg-white">
<!-- Security config의 loginPage("url")와 action url과 동일하게 작성--> <!-- Security config의 loginPage("url")와 action url과 동일하게 작성-->
<form action="/user/login" method="post"> <form action="/user/login" method="post">
<!-- Spring Security가 적용되면 POST 방식으로 보내는 모든 데이터는 csrf 토큰 값이 필요 --> <!-- Spring Security가 적용되면 POST 방식으로 보내는 모든 데이터는 csrf 토큰 값이 필요 -->
@ -16,7 +16,7 @@
<!-- 파라미터명을 변경하고 싶을 경우 config class formlogin()에서 .usernameParameter("") 명시 --> <!-- 파라미터명을 변경하고 싶을 경우 config class formlogin()에서 .usernameParameter("") 명시 -->
<!--<img class="mb-4" th:src="@{/img/}" alt="" width="72" height="57">--> <!--<img class="mb-4" th:src="@{/img/}" alt="" width="72" height="57">-->
<h1 class="h3 mb-3 fw-normal">Please sign in</h1> <h1 class="h3 mb-3 fw-normal">로그인</h1>
<div class="form-floating"> <div class="form-floating">
<input type="text" class="form-control" id="username" name="username" placeholder="아이디"> <input type="text" class="form-control" id="username" name="username" placeholder="아이디">
<label for="username">아이디</label> <label for="username">아이디</label>
@ -32,6 +32,6 @@
</div> </div>
<button class="w-100 btn btn-lg btn-primary" type="submit">로그인</button> <button class="w-100 btn btn-lg btn-primary" type="submit">로그인</button>
</form> </form>
</main> </div>
</div> </div>
</html> </html>