개인정보 페이지 비밀번호 변경 기능 추가.

master
강석 최 2021-12-24 11:40:54 +09:00
parent c51c1649ad
commit 2f8fc492f8
9 changed files with 158 additions and 51 deletions

View File

@ -34,7 +34,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() // 페이지 권한 설정 http.authorizeRequests() // 페이지 권한 설정
.antMatchers("/board/**").hasRole(Role.USER.name()) // USER, ADMIN 접근 허용 .antMatchers("/board/**").hasRole(Role.USER.name()) // USER, ADMIN 접근 허용
.antMatchers("/info").hasRole(Role.USER.name()) // USER, ADMIN 접근 허용 .antMatchers("/info/**").hasRole(Role.USER.name()) // USER, ADMIN 접근 허용
.antMatchers("/admin/**").hasRole(Role.ADMIN.name()) // ADMIN만 접근 허용 .antMatchers("/admin/**").hasRole(Role.ADMIN.name()) // ADMIN만 접근 허용
.antMatchers("/user/login").permitAll() // 로그인 페이지는 권한 없이 접근 허용 .antMatchers("/user/login").permitAll() // 로그인 페이지는 권한 없이 접근 허용
.and() // 로그인 설정 .and() // 로그인 설정

View File

@ -1,20 +1,15 @@
package com.dbnt.kcgfilemanager.controller; package com.dbnt.kcgfilemanager.controller;
import com.dbnt.kcgfilemanager.model.CategoryRole;
import com.dbnt.kcgfilemanager.model.UserInfo; import com.dbnt.kcgfilemanager.model.UserInfo;
import com.dbnt.kcgfilemanager.service.BoardCategoryService; import com.dbnt.kcgfilemanager.service.BoardCategoryService;
import com.dbnt.kcgfilemanager.service.CategoryRoleService;
import com.dbnt.kcgfilemanager.service.CommonCodeService; import com.dbnt.kcgfilemanager.service.CommonCodeService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.annotation.AuthenticationPrincipal;
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 javax.servlet.http.HttpSession;
import java.security.Principal;
@RestController @RestController
@RequiredArgsConstructor @RequiredArgsConstructor
@ -22,18 +17,17 @@ public class BaseController {
private final CommonCodeService commonCodeService; private final CommonCodeService commonCodeService;
private final BoardCategoryService boardCategoryService; private final BoardCategoryService boardCategoryService;
private final CategoryRoleService categoryRoleService;
@GetMapping("/") @GetMapping("/")
public ModelAndView loginCheck(Principal principal, HttpSession session) { public ModelAndView loginCheck(@AuthenticationPrincipal UserInfo loginUser, HttpSession session) {
ModelAndView mav = null; ModelAndView mav = null;
if(principal == null){ if(loginUser == null){
mav = new ModelAndView("redirect:/user/login"); mav = new ModelAndView("redirect:/user/login");
}else{ }else{
session.setAttribute("positionList", commonCodeService.selectCommonCodeValue("POSITION")); session.setAttribute("positionList", commonCodeService.selectCommonCodeValue("POSITION"));
session.setAttribute("departmentList", commonCodeService.selectCommonCodeValue("DEPARTMENT")); session.setAttribute("departmentList", commonCodeService.selectCommonCodeValue("DEPARTMENT"));
session.setAttribute("categoryList", boardCategoryService.selectBoardCategoryAll(null, 1)); session.setAttribute("categoryList", boardCategoryService.selectBoardCategoryAll(null, 1));
if(((UserInfo)((UsernamePasswordAuthenticationToken) principal).getPrincipal()).getUserRole().indexOf("ADMIN")>0){ if(loginUser.getUserRole().indexOf("ADMIN")>0){
mav = new ModelAndView("redirect:/admin/main"); mav = new ModelAndView("redirect:/admin/main");
}else{ }else{
mav = new ModelAndView("redirect:/board/main"); mav = new ModelAndView("redirect:/board/main");
@ -67,16 +61,4 @@ public class BaseController {
ModelAndView mav = new ModelAndView("login/denied"); ModelAndView mav = new ModelAndView("login/denied");
return mav; return mav;
} }
@GetMapping("/info")
public ModelAndView goMyInfo(@AuthenticationPrincipal UserInfo loginUser) {
ModelAndView mav = new ModelAndView("user/myInfo");
mav.addObject("loginUser", loginUser);
if(!loginUser.getUserRole().contains("ADMIN")){
CategoryRole categoryRole = new CategoryRole();
categoryRole.setUserSeq(loginUser.getUserSeq());
mav.addObject("categorySeqList", categoryRoleService.selectCategorySeqListToUser(categoryRole));
}
return mav;
}
} }

View File

@ -0,0 +1,36 @@
package com.dbnt.kcgfilemanager.controller;
import com.dbnt.kcgfilemanager.model.CategoryRole;
import com.dbnt.kcgfilemanager.model.UserInfo;
import com.dbnt.kcgfilemanager.service.CategoryRoleService;
import com.dbnt.kcgfilemanager.service.UserInfoService;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
@RestController
@RequiredArgsConstructor
@RequestMapping("/info")
public class InfoController {
private final CategoryRoleService categoryRoleService;
private final UserInfoService userInfoService;
@GetMapping("/myInfo")
public ModelAndView myInfo(@AuthenticationPrincipal UserInfo loginUser) {
ModelAndView mav = new ModelAndView("user/myInfo");
mav.addObject("loginUser", loginUser);
if(!loginUser.getUserRole().contains("ADMIN")){
CategoryRole categoryRole = new CategoryRole();
categoryRole.setUserSeq(loginUser.getUserSeq());
mav.addObject("categorySeqList", categoryRoleService.selectCategorySeqListToUser(categoryRole));
}
return mav;
}
@PutMapping("/passwordModify")
public String passwordModify(@AuthenticationPrincipal UserInfo loginUser, UserInfo modifyInfo){
return userInfoService.updatePassword(loginUser, modifyInfo);
}
}

View File

@ -44,6 +44,8 @@ public class UserInfo extends BaseModel implements UserDetails{
@Column(name = "USER_STATUS") @Column(name = "USER_STATUS")
private String userStatus; private String userStatus;
@Transient
private String modifyPassword;
@Transient @Transient
private String positionName; private String positionName;
@Transient @Transient

View File

@ -39,6 +39,17 @@ public class UserInfoService implements UserDetailsService {
return targetUserInfo.getUserId(); return targetUserInfo.getUserId();
} }
public String updatePassword(UserInfo loginUser, UserInfo modifyInfo){
Pbkdf2PasswordEncoder passwordEncoder = new Pbkdf2PasswordEncoder();
if(passwordEncoder.matches(modifyInfo.getPassword(), loginUser.getPassword())){
loginUser.setPassword(convertPassword(modifyInfo.getModifyPassword()));
userInfoRepository.save(loginUser);
return "OK";
}else{
return "passwordNotMatch";
}
}
private String convertPassword(String password){ private String convertPassword(String password){
Pbkdf2PasswordEncoder passwordEncoder = new Pbkdf2PasswordEncoder(); Pbkdf2PasswordEncoder passwordEncoder = new Pbkdf2PasswordEncoder();
return passwordEncoder.encode(password); return passwordEncoder.encode(password);

View File

@ -8,6 +8,62 @@ $(document).on('click', '#moveRightBtn', function (){
$(document).on('click', '#moveLeftBtn', function (){ $(document).on('click', '#moveLeftBtn', function (){
moveCategorySelectBody(-1); moveCategorySelectBody(-1);
}) })
$(document).on('click', '#savePasswordBtn', function (){
if(passwordCheck()){
const formData = new FormData($("#modifyPasswordForm")[0]);
$.ajax({
type : 'PUT',
data : formData,
url : "/info/passwordModify",
processData: false,
contentType: false,
success : function(result) {
if(result==="OK"){
alert("수정되었습니다.");
$("#passwordModifyModal").find(".btn-close").click();
}else if(result==="passwordNotMatch"){
alert("현재 비밀번호가 맞지 않습니다.");
}
},
error : function(xhr, status) {
}
})
}
})
function passwordCheck(){
let returnFlag = true;
const password = $("#password");
const modifyPassword =$("#modifyPassword");
const passwordConfirm = $("#passwordConfirm");
if(!password.val()){
alert("비밀번호를 입력해주세요.");
returnFlag = false;
}
if(!modifyPassword.val()){
alert("새 비밀번호를 입력해주세요.");
returnFlag = false;
}
if(!passwordConfirm.val()){
alert("비밀번호 확인을 입력해주세요.");
returnFlag = false;
}
if(returnFlag){
const passwordReg = /^(?=.*[a-zA-z])(?=.*[0-9])(?=.*[$`~!@$!%*#^?&\\(\\)\-_=+]).{8,16}$/;
if(!passwordReg.test(modifyPassword.val())){
alert("비밀번호 조건이 맞지 않습니다.")
returnFlag = false;
}else{
if(modifyPassword.val() !== passwordConfirm.val()){
alert("비밀번호가 같지 않습니다.");
returnFlag = false;
}
}
}
return returnFlag;
}
function moveCategorySelectBody(direction){ function moveCategorySelectBody(direction){
const categorySelectBody = $("#categorySelectBody"); const categorySelectBody = $("#categorySelectBody");

View File

@ -2,8 +2,8 @@
<html lang="ko" xmlns:th="http://www.thymeleaf.org"> <html lang="ko" xmlns:th="http://www.thymeleaf.org">
<div class="p-3"> <div class="p-3">
<div class="row justify-content-between"> <div class="row justify-content-between">
<div class="col-auto"><button type="button" class="btn btn-warning" id="moveLeftBtn"><i class="bi bi-arrow-left"></i></button></div> <div class="col-auto"><button type="button" class="btn btn-info" id="moveLeftBtn"><i class="bi bi-arrow-left"></i></button></div>
<div class="col-auto"><button type="button" class="btn btn-warning" id="moveRightBtn"><i class="bi bi-arrow-right"></i></button></div> <div class="col-auto"><button type="button" class="btn btn-info" id="moveRightBtn"><i class="bi bi-arrow-right"></i></button></div>
</div> </div>
<div class="row overflow-auto flex-nowrap" id="categorySelectBody"> <div class="row overflow-auto flex-nowrap" id="categorySelectBody">
<th:block th:each="depth1:${session.categoryList}"> <th:block th:each="depth1:${session.categoryList}">

View File

@ -8,7 +8,7 @@
<!--<span class="fs-4">해양경찰청 파일관리 시스템</span>--> <!--<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="/info" class="nav-link">개인정보</a></li> <li class="nav-item"><a href="/info/myInfo" 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>
</ul> </ul>
</header> </header>

View File

@ -11,8 +11,8 @@
<div class="col-auto"><h4>개인정보</h4></div> <div class="col-auto"><h4>개인정보</h4></div>
<div class="col-auto"> <div class="col-auto">
<th:block th:if="${!#strings.contains(loginUser.userRole,'ADMIN')}"> <th:block th:if="${!#strings.contains(loginUser.userRole,'ADMIN')}">
<button class="btn btn-success"> 요청 현황</button> <button class="btn btn-success mb-2"> 요청 현황</button>
<button class="btn btn-warning"> 수정 요청</button> <button class="btn btn-warning mb-2"> 수정 요청</button>
</th:block> </th:block>
</div> </div>
</div> </div>
@ -70,19 +70,24 @@
<input type="text" readonly class="form-control-plaintext" id="createDate" th:value="${#temporals.format(loginUser.createDate, 'yyyy-MM-dd')}"> <input type="text" readonly class="form-control-plaintext" id="createDate" th:value="${#temporals.format(loginUser.createDate, 'yyyy-MM-dd')}">
</div> </div>
</div> </div>
<div class="mb-3 row justify-content-center">
<div class="col-auto">
<button class="btn btn-warning" id="passwordModifyBtn" data-bs-toggle="modal" data-bs-target="#passwordModifyModal">비밀번호 변경</button>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<th:block th:if="${!#strings.contains(loginUser.userRole,'ADMIN')}"> <th:block th:if="${!#strings.contains(loginUser.userRole,'ADMIN')}">
<div class="col-8"> <div class="col-8">
<h5 class="ps-3">권한정보</h5> <h5 class="ps-3">작성 권한</h5>
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<div class="p-3"> <div class="p-3">
<div class="row justify-content-between"> <div class="row justify-content-between">
<div class="col-auto"><button type="button" class="btn btn-warning" id="moveLeftBtn"><i class="bi bi-arrow-left"></i></button></div> <div class="col-auto"><button type="button" class="btn btn-info" id="moveLeftBtn"><i class="bi bi-arrow-left"></i></button></div>
<div class="col-auto"><button type="button" class="btn btn-warning" id="moveRightBtn"><i class="bi bi-arrow-right"></i></button></div> <div class="col-auto"><button type="button" class="btn btn-info" id="moveRightBtn"><i class="bi bi-arrow-right"></i></button></div>
</div> </div>
<div class="row overflow-auto flex-nowrap" id="categorySelectBody"> <div class="row overflow-auto flex-nowrap" id="categorySelectBody">
<th:block th:each="depth1:${session.categoryList}"> <th:block th:each="depth1:${session.categoryList}">
@ -110,27 +115,6 @@
</th:block> </th:block>
</th:block> </th:block>
</th:block> </th:block>
<!--<th:block th:each="depth2:${depth1.childCategoryList}">
<tr th:classappend="${#lists.contains(categorySeqList, depth2.categorySeq)}?'bg-success bg-opacity-25':''">
<td th:text="${depth2.categoryName}"></td>
<td></td>
<td></td>
</tr>
<th:block th:each="depth3:${depth2.childCategoryList}">
<tr th:classappend="${#lists.contains(categorySeqList, depth3.categorySeq)}?'bg-success bg-opacity-25':''">
<td></td>
<td th:text="${depth3.categoryName}"></td>
<td></td>
</tr>
<th:block th:each="depth4:${depth3.childCategoryList}">
<tr th:classappend="${#lists.contains(categorySeqList, depth4.categorySeq)}?'bg-success bg-opacity-25':''">
<td></td>
<td></td>
<td th:text="${depth4.categoryName}"></td>
</tr>
</th:block>
</th:block>
</th:block>-->
</tbody> </tbody>
</table> </table>
</div> </div>
@ -146,5 +130,41 @@
</div> </div>
</div> </div>
</main> </main>
<div class="modal fade" id="passwordModifyModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="passwordModifyModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="passwordModifyModalLabel">비밀번호 변경</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body mx-4">
<form th:action="@{/info/passwordModify}" method="post" id="modifyPasswordForm">
<div class="mb-3 row">
<label for="password" class="col-sm-4 col-form-label text-end">현재 비밀번호</label>
<div class="col-sm-7">
<input type="password" class="form-control" id="password" name="password">
</div>
</div>
<div class="mb-3 row">
<label for="modifyPassword" class="col-sm-4 col-form-label text-end">새 비밀번호</label>
<div class="col-sm-7">
<input type="password" class="form-control" id="modifyPassword" name="modifyPassword">
</div>
</div>
<div class="mb-3 row">
<label for="passwordConfirm" class="col-sm-4 col-form-label text-end">새 비밀번호 확인</label>
<div class="col-sm-7">
<input type="password" class="form-control" id="passwordConfirm">
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">닫기</button>
<button type="button" class="btn btn-primary" id="savePasswordBtn">저장</button>
</div>
</div>
</div>
</div>
</div> </div>
</html> </html>