로그인시 비밀번호 n회 실패시 계정 잠금처리
This commit is contained in:
@@ -18,7 +18,17 @@
|
||||
<script language="javascript" >
|
||||
var url = '<c:url value="/common/acl/user/userMan.json" />';
|
||||
var url_view = '<c:url value="/common/acl/user/userMan.view" />';
|
||||
$(document).ready(function() {
|
||||
|
||||
function userStatusFormat (cellvalue){
|
||||
if ( cellvalue == '1' ){
|
||||
return '<%= localeMessage.getString("code.normal") %>';
|
||||
}else if ( cellvalue == '2' ) {
|
||||
return '<span style="color:red"><%= localeMessage.getString("code.lock") %></span>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
var gridPostData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
$('#grid').jqGrid({
|
||||
datatype:"json",
|
||||
@@ -28,6 +38,7 @@ $(document).ready(function() {
|
||||
colNames:['<%= localeMessage.getString("login.userId") %>',
|
||||
'<%= localeMessage.getString("user.name") %>',
|
||||
'<%= localeMessage.getString("user.roleName") %>',
|
||||
'<%= localeMessage.getString("user.status") %>',
|
||||
'<%= localeMessage.getString("user.createOn") %>',
|
||||
'<%= localeMessage.getString("user.depart") %>',
|
||||
'<%= localeMessage.getString("user.teamName") %>',
|
||||
@@ -37,6 +48,7 @@ $(document).ready(function() {
|
||||
{ name : 'USERID' , align:'center' ,sortable:false },
|
||||
{ name : 'USERNAME' , align:'center' },
|
||||
{ name : 'ROLEIDNFINAME' , align:'center' },
|
||||
{ name : 'STATUS' , align:'center', formatter: userStatusFormat },
|
||||
{ name : 'LASTAMNDYMS' , align:'center', formatter: timeStampFormat },
|
||||
{ name : 'DVSNNAME' , align:'center', hidden: true },
|
||||
{ name : 'TEAMNAME' , align:'center', hidden: true },
|
||||
|
||||
@@ -216,6 +216,7 @@ $(document).ready(function() {
|
||||
dataType:"json",
|
||||
data:{cmd: 'UPDATE_PASSWORD', userId : $("input[name=userId]").val()},
|
||||
success:function(json){
|
||||
$('select[name="status"]').val("1")
|
||||
alert(json.result);
|
||||
},
|
||||
error:function(e){
|
||||
@@ -277,10 +278,10 @@ $(document).ready(function() {
|
||||
<table class="table_row" cellspacing="0">
|
||||
<input type="hidden" name="jobclcd">
|
||||
<tr>
|
||||
<th style="width:180px;"><%= localeMessage.getString("login.userId") %></th><td ><input type="text" name="userId"/> </td>
|
||||
<th style="width:180px;"><%= localeMessage.getString("login.userId") %> <span class=\"required-mark\">*</span></th><td ><input type="text" name="userId"/> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><%= localeMessage.getString("user.name") %> </th><td><input type="text" name="userName" /></td>
|
||||
<th><%= localeMessage.getString("user.name") %> <span class=\"required-mark\">*</span></th><td><input type="text" name="userName" /></td>
|
||||
</tr>
|
||||
<tr class="datailT">
|
||||
<th><%= localeMessage.getString("user.auth") %> </th><td><input type="text" name="roleidnfiname" /></td>
|
||||
@@ -317,7 +318,6 @@ $(document).ready(function() {
|
||||
<td>
|
||||
<div class="select-style">
|
||||
<select name="status">
|
||||
<option value=""><%= localeMessage.getString("code.normal") %></option>
|
||||
<option value="1"><%= localeMessage.getString("code.normal") %></option>
|
||||
<option value="2"><%= localeMessage.getString("code.lock") %></option>
|
||||
</select>
|
||||
|
||||
@@ -41,8 +41,8 @@ import lombok.RequiredArgsConstructor;
|
||||
@Transactional(transactionManager = "transactionManagerForEMS")
|
||||
@RequiredArgsConstructor
|
||||
public class UserManService extends BaseService {
|
||||
|
||||
|
||||
|
||||
private static final String USER_STATUS_NORMAL = "1";
|
||||
|
||||
private final UserInfoService userInfoService;
|
||||
private final UserRoleService userRoleService;
|
||||
@@ -100,7 +100,8 @@ public class UserManService extends BaseService {
|
||||
|
||||
UserInfo userInfo = userUIMapper.toEntity(userUI);
|
||||
userInfo.setRoleidnfiname(CommonConstants.DEPT_DEVELOPER);
|
||||
|
||||
userInfo.setStatus(USER_STATUS_NORMAL);
|
||||
|
||||
String subfix = monitoringContext.getStringProperty(MonitoringContext.RMS_PASSWORD_INIT_SUBFIX, "@!");
|
||||
|
||||
try {
|
||||
@@ -133,6 +134,12 @@ public class UserManService extends BaseService {
|
||||
public void update(UserUI userUI) {
|
||||
UserInfo userInfo = userInfoService.getById(userUI.getUserId());
|
||||
userUIMapper.updateToEntity(userUI, userInfo);
|
||||
|
||||
// 계정상태를 정상으로 저장하면 로그인 실패횟수 초기화
|
||||
if (USER_STATUS_NORMAL.equals(userInfo.getStatus())) {
|
||||
userInfo.setLoginfailcount(0);
|
||||
}
|
||||
|
||||
userInfoService.save(userInfo);
|
||||
}
|
||||
|
||||
@@ -148,6 +155,11 @@ public class UserManService extends BaseService {
|
||||
UserInfo userInfo = userInfoService.getById(userUI.getUserId());
|
||||
userInfo.setPassword(DamoManager.getInstance().hash(DamoManager.SHA256, userUI.getUserId()+subfix));
|
||||
userUIMapper.updateToEntity(userUI, userInfo);
|
||||
|
||||
// 비밀번호 초기화 시 계정상태를 정상으로 변경하고 로그인 실패횟수 초기화
|
||||
userInfo.setLoginfailcount(0);
|
||||
userInfo.setStatus(USER_STATUS_NORMAL);
|
||||
|
||||
userInfoService.save(userInfo);
|
||||
}
|
||||
|
||||
|
||||
@@ -265,6 +265,9 @@ public interface MonitoringContext {
|
||||
|
||||
//비밀번호 초기화 접미사
|
||||
public static final String RMS_PASSWORD_INIT_SUBFIX = "rms.password.init.subfix";
|
||||
|
||||
// 최대 로그인 실패 허용횟수 (0이면 계정 잠금 미적용)
|
||||
public static final String RMS_PASSWORD_FAIL_COUNT = "rms.password.fail.count";
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -64,6 +64,9 @@ public class MainController implements InterceptorSkipController {
|
||||
private static final String REDIRECT_LOGIN_URL = "redirect:/emergency.jsp";
|
||||
private static final String REDIRECT_CHANGE_PASSWORD_URL = "redirect:/emergency.jsp";
|
||||
|
||||
private static final String USER_STATUS_LOCKED = "2";
|
||||
private static final int DEFAULT_MAX_LOGIN_FAIL_COUNT = 5;
|
||||
|
||||
private final LocaleMessage localeMessage;
|
||||
private final MonitoringContext monitoringContext;
|
||||
|
||||
@@ -160,7 +163,7 @@ public class MainController implements InterceptorSkipController {
|
||||
session.removeAttribute(RESULT_MSG);
|
||||
return LoginResponseDto.builder()
|
||||
.success(false)
|
||||
.errorMessage(errorMsg != null ? errorMsg : "로그인에 실패했습니다.")
|
||||
.errorMessage(toAlertMessage(errorMsg != null ? errorMsg : "로그인에 실패했습니다."))
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -345,10 +348,20 @@ public class MainController implements InterceptorSkipController {
|
||||
|
||||
//logger.info("사용자 계정 상태: " + userInfo.getStatus());
|
||||
|
||||
if ( userInfo.getStatus() != null && "2".equals(userInfo.getStatus()) ) {
|
||||
setLoginFailure(request, session, "login.checkstatus1",
|
||||
"login.checkstatus2");
|
||||
return null;
|
||||
if ( USER_STATUS_LOCKED.equals(userInfo.getStatus()) ) {
|
||||
int maxLoginFailCount = getMaxLoginFailCount();
|
||||
boolean lockedByFailCount = maxLoginFailCount > 0
|
||||
&& userInfo.getLoginfailcount() != null
|
||||
&& userInfo.getLoginfailcount() >= maxLoginFailCount;
|
||||
|
||||
if (lockedByFailCount) {
|
||||
setLoginFailure(request, session, "login.accountlocked1",
|
||||
"login.accountlocked2", String.valueOf(maxLoginFailCount));
|
||||
} else {
|
||||
setLoginFailure(request, session, "login.checkstatus1",
|
||||
"login.checkstatus2");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//logger.info("사용자 ip: " + userInfo.getAllowip());
|
||||
@@ -374,27 +387,29 @@ public class MainController implements InterceptorSkipController {
|
||||
/* 기존로직 - LDAP 사용 x */
|
||||
if (!userInfo.getPassword().equals(DamoManager.getInstance().hash(DamoManager.SHA256, dto.getPassword()))) {
|
||||
// password 다름.
|
||||
int failCount = increaseLoginFailCount(userInfo);
|
||||
setLoginFailure(request, session, "login.loginfail1",
|
||||
"login.loginfail2");
|
||||
"login.loginfail2", failCount);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
|
||||
|
||||
String password = dto.getPassword();
|
||||
|
||||
|
||||
password = password.replace("#","#");
|
||||
password = password.replace("&","&");
|
||||
|
||||
|
||||
/* LDAP */
|
||||
//if (!authenticate(userInfo.getUserid(), dto.getPassword())) {
|
||||
if (!authenticate(userInfo.getUserid(), password)) {
|
||||
// password 다름.
|
||||
int failCount = increaseLoginFailCount(userInfo);
|
||||
setLoginFailure(request, session, "login.loginfail1",
|
||||
"login.loginfail2");
|
||||
"login.loginfail2", failCount);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
session = request.getSession(true);
|
||||
session.setAttribute("userId", dto.getUserId());
|
||||
|
||||
@@ -432,7 +447,28 @@ public class MainController implements InterceptorSkipController {
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
// LDAP
|
||||
// 최대 로그인 실패 허용횟수 조회 (rms.password.fail.count, 0이면 계정 잠금 미적용)
|
||||
private int getMaxLoginFailCount() {
|
||||
return monitoringContext.getIntProperty(
|
||||
MonitoringContext.RMS_PASSWORD_FAIL_COUNT, DEFAULT_MAX_LOGIN_FAIL_COUNT);
|
||||
}
|
||||
|
||||
// 로그인 실패 횟수 증가, 임계치 도달 시 계정 잠금
|
||||
private int increaseLoginFailCount(UserInfo userInfo) {
|
||||
int failCount = (userInfo.getLoginfailcount() == null ? 0 : userInfo.getLoginfailcount()) + 1;
|
||||
userInfo.setLoginfailcount(failCount);
|
||||
|
||||
int maxLoginFailCount = getMaxLoginFailCount();
|
||||
if (maxLoginFailCount > 0 && failCount >= maxLoginFailCount) {
|
||||
userInfo.setStatus(USER_STATUS_LOCKED);
|
||||
logger.warn("로그인 " + maxLoginFailCount + "회 이상 실패로 계정 잠금 처리: " + userInfo.getUserid());
|
||||
}
|
||||
|
||||
userInfoService.save(userInfo);
|
||||
return failCount;
|
||||
}
|
||||
|
||||
// LDAP
|
||||
public boolean authenticate(String userId, String password) {
|
||||
String LDAP_URL = monitoringPropertyService.getPrpty2ValById("Monitoring", "ldap.domain");
|
||||
String NETBIOS = monitoringPropertyService.getPrpty2ValById("Monitoring", "ldap.netbios");
|
||||
@@ -496,6 +532,7 @@ public class MainController implements InterceptorSkipController {
|
||||
String lastLoginIp = IpUtil.getClientIp(request);
|
||||
userInfo.setLastloginyms(LocalDateTime.now());
|
||||
userInfo.setLastloginip(lastLoginIp);
|
||||
userInfo.setLoginfailcount(0);
|
||||
userInfoService.save(userInfo);
|
||||
}
|
||||
|
||||
@@ -556,12 +593,32 @@ public class MainController implements InterceptorSkipController {
|
||||
return "redirect:/common/errors/errorLogon.jsp";
|
||||
}
|
||||
|
||||
// HTML 개행 태그(<BR/> 등)를 JS alert용 개행문자로 변환
|
||||
private String toAlertMessage(String message) {
|
||||
return message.replaceAll("(?i)<br\\s*/?>", "\n");
|
||||
}
|
||||
|
||||
// 로그인 실패 시 로그 기록, 세션에 실패 메시지 설정
|
||||
private void setLoginFailure(HttpServletRequest request,
|
||||
HttpSession session, String logKey, String msgKey) {
|
||||
HttpSession session, String logKey, String msgKey,
|
||||
String... msgArgs) {
|
||||
UserAccessLogger.log(request, LOG_CATEGORY_LOGIN, "F",
|
||||
localeMessage.getString(logKey));
|
||||
session.setAttribute(RESULT_MSG, localeMessage.getString(msgKey));
|
||||
session.setAttribute(RESULT_MSG, localeMessage.getString(msgKey, msgArgs));
|
||||
}
|
||||
|
||||
// 로그인 실패 시 로그 기록, 세션에 실패 메시지(실패 횟수 포함) 설정
|
||||
private void setLoginFailure(HttpServletRequest request,
|
||||
HttpSession session, String logKey, String msgKey,
|
||||
int failCount) {
|
||||
UserAccessLogger.log(request, LOG_CATEGORY_LOGIN, "F",
|
||||
localeMessage.getString(logKey));
|
||||
String msg = localeMessage.getString(msgKey);
|
||||
int maxLoginFailCount = getMaxLoginFailCount();
|
||||
if (maxLoginFailCount > 0) {
|
||||
msg += " (" + failCount + "/" + maxLoginFailCount + ")";
|
||||
}
|
||||
session.setAttribute(RESULT_MSG, msg);
|
||||
}
|
||||
|
||||
// 서비스 타입 검증
|
||||
|
||||
@@ -1200,6 +1200,8 @@ login.failcountover2 = You have had three password failures. <B
|
||||
login.grantfail1 = Failed to initialize user credential information.
|
||||
login.grantfail2 = Logon Error:<BR>Failed to initialize user credential information.
|
||||
login.loginfail1 = Login failed. Please check your login information.
|
||||
login.accountlocked1 = Account locked due to {0} or more login failures. Please contact your administrator.
|
||||
login.accountlocked2 = Account locked due to {0} or more login failures.<BR/> Please contact your administrator.
|
||||
login.loginfail2 = Login failed.<BR/>Please check your login information.
|
||||
login.logonaccount = Logon account
|
||||
login.logout = Logout
|
||||
|
||||
@@ -1346,6 +1346,8 @@ login.failcountover2 = You have had three password failures. <B
|
||||
login.grantfail1 = Failed to initialize user credential information.
|
||||
login.grantfail2 = Logon Error:<BR>Failed to initialize user credential information.
|
||||
login.loginfail1 = Login failed. Please check your login information.
|
||||
login.accountlocked1 = Account locked due to {0} or more login failures. Please contact your administrator.
|
||||
login.accountlocked2 = Account locked due to {0} or more login failures.<BR/> Please contact your administrator.
|
||||
login.loginfail2 = Login failed.<BR/>Please check your login information.
|
||||
login.logonaccount = Logon account
|
||||
login.logout = Logout
|
||||
|
||||
@@ -1426,6 +1426,8 @@ login.loginfail1 = \uC0AC\uC6A9\uC790ID \uB610\uB294 \uBE44
|
||||
login.loginfail2 = \uC0AC\uC6A9\uC790ID \uB610\uB294 \uBE44\uBC00\uBC88\uD638 \uC785\uB825 \uC2E4\uD328\uC785\uB2C8\uB2E4.<BR/> \uB85C\uADF8\uC778\uC815\uBCF4\uB97C \uD655\uC778\uD558\uC138\uC694.
|
||||
login.checkstatus1 = \uC7A5\uAE30(90\uC77C)\uBBF8\uC0AC\uC6A9 \uACC4\uC815\uC785\uB2C8\uB2E4. \uB2F4\uB2F9\uC790\uC5D0\uAC8C \uBB38\uC758\uD558\uC138\uC694.
|
||||
login.checkstatus2 = \uC7A5\uAE30(90\uC77C)\uBBF8\uC0AC\uC6A9 \uACC4\uC815\uC785\uB2C8\uB2E4.<BR/> \uB2F4\uB2F9\uC790\uC5D0\uAC8C \uBB38\uC758\uD558\uC138\uC694.
|
||||
login.accountlocked1 = \uB85C\uADF8\uC778 {0}\uD68C \uC774\uC0C1 \uC2E4\uD328\uB85C \uACC4\uC815\uC774 \uC7A0\uACBC\uC2B5\uB2C8\uB2E4. \uAD00\uB9AC\uC790\uC5D0\uAC8C \uBB38\uC758\uD558\uC138\uC694.
|
||||
login.accountlocked2 = \uB85C\uADF8\uC778 {0}\uD68C \uC774\uC0C1 \uC2E4\uD328\uB85C \uACC4\uC815\uC774 \uC7A0\uACBC\uC2B5\uB2C8\uB2E4.<BR/> \uAD00\uB9AC\uC790\uC5D0\uAC8C \uBB38\uC758\uD558\uC138\uC694.
|
||||
login.logonaccount = \uB85C\uADF8\uC628\uACC4\uC815
|
||||
login.logout = \uB85C\uADF8\uC544\uC6C3
|
||||
login.password = \uBE44\uBC00\uBC88\uD638
|
||||
|
||||
Reference in New Issue
Block a user