Index: ssts-web/src/main/webapp/WEB-INF/spring/applicationContext-disinfectsystem-service.xml
===================================================================
diff -u -r40302 -r40542
--- ssts-web/src/main/webapp/WEB-INF/spring/applicationContext-disinfectsystem-service.xml (.../applicationContext-disinfectsystem-service.xml) (revision 40302)
+++ ssts-web/src/main/webapp/WEB-INF/spring/applicationContext-disinfectsystem-service.xml (.../applicationContext-disinfectsystem-service.xml) (revision 40542)
@@ -3250,4 +3250,14 @@
class="com.forgon.wechat.service.UserInfoOfProjectManagerImpl">
+
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: forgon-core/src/main/java/com/forgon/security/model/IpLoginLockRecord.java
===================================================================
diff -u
--- forgon-core/src/main/java/com/forgon/security/model/IpLoginLockRecord.java (revision 0)
+++ forgon-core/src/main/java/com/forgon/security/model/IpLoginLockRecord.java (revision 40542)
@@ -0,0 +1,83 @@
+package com.forgon.security.model;
+
+import java.util.Date;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Index;
+import javax.persistence.Table;
+
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+import org.hibernate.annotations.DynamicInsert;
+import org.hibernate.annotations.DynamicUpdate;
+
+/**
+ * IP登录锁定记录
+ * 同一IP多次登录失败后,锁定IP,不允许登录
+ * GZSZYY-119【登录管理】新增多个登录功能改进(IP登录失败锁定次数,验证码刷新规则修改)
+ */
+@Entity
+@DynamicInsert(false)
+@DynamicUpdate(true)
+@Table(name = "IpLoginLockRecord", indexes = {@Index(columnList = "ipAddress", name = "ip_lck_ip_index")})
+@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
+public class IpLoginLockRecord {
+
+ /**
+ * id
+ */
+ private Long id;
+
+ /**
+ * ip地址
+ */
+ private String ipAddress;
+
+ /**
+ * 登录失败次数
+ */
+ private Integer failCount = 0;
+
+ /**
+ * 锁定截止时间
+ */
+ private Date lockEndDateTime;
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getIpAddress() {
+ return ipAddress;
+ }
+
+ public void setIpAddress(String ipAddress) {
+ this.ipAddress = ipAddress;
+ }
+
+ public Integer getFailCount() {
+ return failCount;
+ }
+
+ public void setFailCount(Integer failCount) {
+ this.failCount = failCount;
+ }
+
+ public Date getLockEndDateTime() {
+ return lockEndDateTime;
+ }
+
+ public void setLockEndDateTime(Date lockEndDateTime) {
+ this.lockEndDateTime = lockEndDateTime;
+ }
+
+}
Index: forgon-core/src/main/java/com/forgon/security/service/IpLoginLockRecordManagerImpl.java
===================================================================
diff -u
--- forgon-core/src/main/java/com/forgon/security/service/IpLoginLockRecordManagerImpl.java (revision 0)
+++ forgon-core/src/main/java/com/forgon/security/service/IpLoginLockRecordManagerImpl.java (revision 40542)
@@ -0,0 +1,100 @@
+package com.forgon.security.service;
+
+import java.util.Date;
+
+import javax.servlet.http.HttpServletRequest;
+
+import net.sf.json.JSONObject;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.forgon.runwithtrans.model.RunWithTransNewTask;
+import com.forgon.runwithtrans.service.RunWithTransNewManager;
+import com.forgon.security.model.IpLoginLockRecord;
+import com.forgon.tools.hibernate.BasePoManagerImpl;
+import com.forgon.tools.util.ConfigUtils;
+import com.forgon.tools.util.ForgonDateUtils;
+
+public class IpLoginLockRecordManagerImpl extends BasePoManagerImpl implements IpLoginLockRecordManager {
+
+ private static Logger logger = Logger.getLogger(IpLoginLockRecordManagerImpl.class);
+
+ @Autowired
+ private RunWithTransNewManager runWithTransNewManager;
+
+ @Override
+ public void isLockedIP(HttpServletRequest request) {
+
+ if(request == null){
+ return;
+ }
+
+ String ip = request.getRemoteAddr();
+ if(StringUtils.isBlank(ip)){
+ return;
+ }
+ //连续登录失败的次数,达到这个次数后,账号会被锁定。如果没配置,则缺省值为6,即连续6次失败,会被锁定。(连续2次及以下登录失败只提示用户名或密码错误,不提示可以尝登录失败次数)
+ int loginFailuresCountOfIP = 0;
+ //锁定的时间,单位为分钟。如果没配置,则缺省值为180,即3个小时。
+ int lockTimeInMinutesOfIP = 0;
+ String loginSecurirtyConfig = ConfigUtils.getSystemSetConfigByName("loginSecurirtyConfig");
+ if(StringUtils.isNotBlank(loginSecurirtyConfig)){
+ try {
+ JSONObject json = JSONObject.fromObject(loginSecurirtyConfig);
+ loginFailuresCountOfIP = json.optInt("loginFailuresCountOfIP", 0);
+ lockTimeInMinutesOfIP = json.optInt("lockTimeInMinutesOfIP", 0);
+ } catch (Exception e) {
+ }
+ }
+ if(loginFailuresCountOfIP == 0 || lockTimeInMinutesOfIP == 0){
+ //不开启IP登录失败锁定的配置项
+ return;
+ }
+ IpLoginLockRecord ipLoginLockRecord = this.getIpLoginLockRecordByIp(ip);
+ if(ipLoginLockRecord == null){
+ return;
+ }
+ if(ipLoginLockRecord.getLockEndDateTime() == null){
+ return;
+ }
+
+ if(ipLoginLockRecord.getLockEndDateTime().after(new Date())){
+ String messageAfterLocked = "您已连续多次身份验证失败,请%s后再进行尝试!";
+ String logonFailNoticeMessage = String.format(messageAfterLocked, ForgonDateUtils.safelyFormatDate(ipLoginLockRecord.getLockEndDateTime(), ForgonDateUtils.SIMPLEDATEFORMAT_YYYYMMDDHHMM, ""));
+ request.getSession().setAttribute("message", logonFailNoticeMessage);
+ logger.error(logonFailNoticeMessage + "(IP登录失败次数限制)");
+ throw new RuntimeException(logonFailNoticeMessage);
+ }
+ }
+
+ @Override
+ public void clearIpLoginFailCount(String ip) {
+ if(StringUtils.isEmpty(ip)){
+ return;
+ }
+ objectDao.excuteSQL(String.format("delete from %s where ipAddress = '%s'", IpLoginLockRecord.class.getSimpleName(), ip));
+ }
+
+ @Override
+ public IpLoginLockRecord getIpLoginLockRecordByIp(String ip) {
+ if(StringUtils.isBlank(ip)){
+ return null;
+ }
+
+ return this.getFirst("ipAddress", ip);
+ }
+
+ @Override
+ public void recordIpLoginFailRecordWithTransNewManager(IpLoginLockRecord ipLoginLockRecord) {
+ runWithTransNewManager.runWith_TRANS_NEW(new RunWithTransNewTask() {
+ @Override
+ public void runTask() {
+ objectDao.saveOrUpdate(ipLoginLockRecord);
+ }
+
+ });
+ }
+
+}
Index: ssts-web/src/main/java/com/forgon/disinfectsystem/security/userdetails/DaoUserDetailSSTSImpl.java
===================================================================
diff -u -r38081 -r40542
--- ssts-web/src/main/java/com/forgon/disinfectsystem/security/userdetails/DaoUserDetailSSTSImpl.java (.../DaoUserDetailSSTSImpl.java) (revision 38081)
+++ ssts-web/src/main/java/com/forgon/disinfectsystem/security/userdetails/DaoUserDetailSSTSImpl.java (.../DaoUserDetailSSTSImpl.java) (revision 40542)
@@ -43,10 +43,12 @@
import com.forgon.disinfectsystem.common.CssdUtils;
import com.forgon.disinfectsystem.entity.basedatamanager.supplyroomconfig.SupplyRoomConfig;
import com.forgon.disinfectsystem.schedule.service.ScheduleInformationManager;
+import com.forgon.security.model.IpLoginLockRecord;
import com.forgon.security.model.Role;
import com.forgon.security.model.User;
import com.forgon.security.model.UserAttribute;
import com.forgon.security.model.UserLogonRecord;
+import com.forgon.security.service.IpLoginLockRecordManager;
import com.forgon.security.service.RoleManager;
import com.forgon.security.service.SSOAuthenticationService;
import com.forgon.security.service.UserManager;
@@ -85,6 +87,8 @@
private OrgUnitManager orgUnitManager;
@Resource
private ScheduleInformationManager scheduleInformationManager;
+ @Resource
+ private IpLoginLockRecordManager ipLoginLockRecordManager;
/**
* 单点登录service,此处不能用@Resource或@Autowired注解,因为这个bean所在的project不是任何项目都会依赖的,没有依赖时会报ClassNotFound
@@ -146,6 +150,11 @@
if(requestAttributes != null){
request = ((ServletRequestAttributes)requestAttributes).getRequest();
}
+ //判断当前登录IP是否被锁定(GZSZYY-119【登录管理】新增多个登录功能改进(IP登录失败锁定次数,验证码刷新规则修改))
+ if(request != null){
+ ipLoginLockRecordManager.isLockedIP(request);
+ }
+
// oracle用户名区分大小写,广东省中医院的用户名的英文均为大写,采用的是oracle数据库,
// 因此不再转换为小写的用户名
//username = username.toLowerCase();
@@ -194,6 +203,8 @@
}
//插入登录记录
userManager.insertUserLogonRecord(userLogonRecord);
+ //记录ip登录失败记录(GZSZYY-119【登录管理】新增多个登录功能改进(IP登录失败锁定次数,验证码刷新规则修改))
+ recordLoginFailIp(request);
throw new UsernameNotFoundException("user not found in database");
}else{
//如果帐号存在,则删除session会话的消息
@@ -206,8 +217,10 @@
logger.error("User with login: " + username
+ " is disabled ");
if(request != null){
- request.getSession().setAttribute("message", "帐号" + username + "状态为停用.");
+ request.getSession().setAttribute("message", messageCommon);
}
+ //记录ip登录失败记录(GZSZYY-119【登录管理】新增多个登录功能改进(IP登录失败锁定次数,验证码刷新规则修改))
+ recordLoginFailIp(request);
throw new DisabledException("该用户已被停用!");
}
@@ -232,8 +245,8 @@
}
//连续登录失败**次后开始提醒
int seriesLogonFailBeginNoticeTimes = 3;
- String messageCanTryLogon = "您还可以尝试登录%s次,请再次检查用户名、密码是否正确!";
- String messageAfterLocked = "您已连续多次身份验证失败,请%s后再进行尝试!";
+ String messageCanTryLogon = username + "还可以尝试登录%s次,请再次检查用户名、密码是否正确!";
+ String messageAfterLocked = username + "已连续多次身份验证失败,请%s后再进行尝试!";
Date now = new Date();
//锁定的截止时间
Date lockEndDate = currentLoginedUser.getLockEndDate();
@@ -244,12 +257,15 @@
userLogonRecord.setLogonName(username);
userLogonRecord.setPassword(j_password);
messageAfterLocked = String.format(messageAfterLocked, ForgonDateUtils.safelyFormatDate(lockEndDate, ForgonDateUtils.SIMPLEDATEFORMAT_YYYYMMDDHHMM, ""));
+ logger.info(messageAfterLocked);
if(request != null){
- request.getSession().setAttribute("message", messageAfterLocked);
+ request.getSession().setAttribute("message", messageCommon);
userLogonRecord.setIp(request.getRemoteAddr());
}
//插入登录记录
userManager.insertUserLogonRecord(userLogonRecord);
+ //如果开启IP限制,优先提示IP锁定信息
+ recordLoginFailIp(request);
throw new RuntimeException(messageAfterLocked);
}else{
//截止时间过了,则清除该用户的锁定截止时间字段的值
@@ -269,6 +285,9 @@
String j_passwordAfterRsaDecrypt = RSAEncrypt.decrypt(j_password);
j_passwordRsaMd5 = CoderEncryption.encryptMD5ForSpringSecurity(j_passwordAfterRsaDecrypt);
} catch (Exception e) {
+ logger.info("对密码进行MD5加密时出错!" + e.getMessage());
+ //记录ip登录失败记录(GZSZYY-119【登录管理】新增多个登录功能改进(IP登录失败锁定次数,验证码刷新规则修改))
+ recordLoginFailIp(request);
throw new RuntimeException("对密码进行MD5加密时出错!" + e.getMessage());
}
UserLogonRecord userLogonRecord = new UserLogonRecord();
@@ -290,7 +309,7 @@
//判断剩余可尝试次数是否大于0(即连续登录失败次数是否超过5次时)
int canTryLogonTimes = allowLogonFailTimes - (seriesLogonFailTimes + 1);
if(canTryLogonTimes > 0){
- logonFailNoticeMessage = String.format(messageCanTryLogon, canTryLogonTimes);
+ logger.info(String.format(messageCanTryLogon, canTryLogonTimes));
userLogonRecord.setSucc(UserLogonRecord.SUCC_FALSE);
}else if(canTryLogonTimes == 0){
userLogonRecord.setSucc(UserLogonRecord.SUCC_FALSE);
@@ -299,7 +318,7 @@
nextLockEndDate = cal1.getTime();
currentLoginedUser.setLockEndDate(nextLockEndDate);
userManager.modifyUserLockEndDateWithTransNewManager(currentLoginedUser);
- logonFailNoticeMessage = String.format(messageAfterLocked, ForgonDateUtils.safelyFormatDate(nextLockEndDate, ForgonDateUtils.SIMPLEDATEFORMAT_YYYYMMDDHHMM, ""));
+ logger.info(String.format(messageAfterLocked, ForgonDateUtils.safelyFormatDate(nextLockEndDate, ForgonDateUtils.SIMPLEDATEFORMAT_YYYYMMDDHHMM, "")));
}else{
//正常不会进到这个else(因为连续登录失败次数超过5次时时,会进入前面194行的lockEndDate判断的分支里)
}
@@ -312,6 +331,8 @@
}
//插入登录记录
userManager.insertUserLogonRecord(userLogonRecord);
+ //记录ip登录失败记录(GZSZYY-119【登录管理】新增多个登录功能改进(ip登录失败锁定次数,验证码刷新规则修改))
+ recordLoginFailIp(request);
throw new RuntimeException(logonFailNoticeMessage);
}else if(StringUtils.equals(password, j_passwordRsaMd5)){
password = j_passwordMd5;
@@ -400,6 +421,8 @@
//登录成功后,修改用户的最后在线时间FSSZYY-37
userManager.updateUserLastOnlineTime(currentLoginedUser.getId(), new Date(), false);
+ //登录成功后,清除IP登录失败次数GZSZYY-119
+ ipLoginLockRecordManager.clearIpLoginFailCount(request == null ? null : request.getRemoteAddr());
UserDetails acegiUser = new UserContainsSessionUser(
authenticationUserName,password,
@@ -410,6 +433,77 @@
}
/**
+ * 登录失败后,记录IP锁定记录,及返回提示信息
+ * @param request 请求
+ */
+ private void recordLoginFailIp(HttpServletRequest request) {
+
+ if(request == null){
+ return;
+ }
+ String ip = request.getRemoteAddr();
+ if(StringUtils.isBlank(ip)){
+ return;
+ }
+ //连续登录失败的次数,达到这个次数后,账号会被锁定。(连续2次及以下登录失败只提示用户名或密码错误,不提示可以尝登录失败次数)
+ int loginFailuresCountOfIP = 0;
+ //锁定的时间,单位为分钟。
+ int lockTimeInMinutesOfIP = 0;
+ String loginSecurirtyConfig = ConfigUtils.getSystemSetConfigByName("loginSecurirtyConfig");
+ if(StringUtils.isNotBlank(loginSecurirtyConfig)){
+ try {
+ JSONObject json = JSONObject.fromObject(loginSecurirtyConfig);
+ loginFailuresCountOfIP = json.optInt("loginFailuresCountOfIP", 0);
+ lockTimeInMinutesOfIP = json.optInt("lockTimeInMinutesOfIP", 0);
+ } catch (Exception e) {}
+ }
+ if(loginFailuresCountOfIP == 0 || lockTimeInMinutesOfIP == 0){
+ //不开启IP登录失败锁定的配置项
+ return;
+ }
+
+ //连续登录失败3次后开始提醒
+ int seriesLogonFailBeginNoticeTimes = loginFailuresCountOfIP > 3 ? 3 : loginFailuresCountOfIP / 2;
+ String messageCanTryLogon = "您还可以尝试登录%s次,请再次检查用户名、密码是否正确!";
+ String messageAfterLocked = "您已连续多次身份验证失败,请%s后再进行尝试!";
+ IpLoginLockRecord ipLoginLockRecord = ipLoginLockRecordManager.getIpLoginLockRecordByIp(ip);
+ if(ipLoginLockRecord == null){
+ ipLoginLockRecord = new IpLoginLockRecord();
+ ipLoginLockRecord.setIpAddress(ip);
+ ipLoginLockRecord.setFailCount(1);
+ }else{
+ ipLoginLockRecord.setFailCount(ipLoginLockRecord.getFailCount()+1);
+ }
+
+ //过了锁定截止时间后,自动解锁后的第一次登录就登录失败时,需要重新计算登录失败时间
+ if(ipLoginLockRecord.getLockEndDateTime() != null && !ipLoginLockRecord.getLockEndDateTime().after(new Date())){
+ ipLoginLockRecord.setFailCount(1);
+ ipLoginLockRecord.setLockEndDateTime(null);
+ }
+
+ //登录失败提示信息
+ String logonFailNoticeMessage = "";
+ if(ipLoginLockRecord.getFailCount() < seriesLogonFailBeginNoticeTimes){
+ logonFailNoticeMessage = "用户名或者密码错误,请重新输入!";
+ }else if(ipLoginLockRecord.getFailCount() < loginFailuresCountOfIP){
+ logonFailNoticeMessage = String.format(messageCanTryLogon, loginFailuresCountOfIP - ipLoginLockRecord.getFailCount());
+ }else{
+ Calendar cal1 = Calendar.getInstance();
+ cal1.add(Calendar.MINUTE, lockTimeInMinutesOfIP);
+ ipLoginLockRecord.setLockEndDateTime(cal1.getTime());
+ logonFailNoticeMessage = String.format(messageAfterLocked, ForgonDateUtils.safelyFormatDate(ipLoginLockRecord.getLockEndDateTime(), ForgonDateUtils.SIMPLEDATEFORMAT_YYYYMMDDHHMM, ""));
+ }
+ //保存IP锁定记录
+ ipLoginLockRecordManager.recordIpLoginFailRecordWithTransNewManager(ipLoginLockRecord);
+ if(StringUtils.isBlank(logonFailNoticeMessage)){
+ logonFailNoticeMessage = "用户名或者密码错误,请重新输入!";
+ }
+ logger.error(String.format("%s,loginFailuresCountOfIP = %s,lockTimeInMinutesOfIP = %s ", logonFailNoticeMessage, loginFailuresCountOfIP, lockTimeInMinutesOfIP));
+ request.getSession().setAttribute("message", logonFailNoticeMessage);
+ throw new RuntimeException(logonFailNoticeMessage);
+ }
+
+ /**
* 获取当前登录科室的角色GYEY-765
* @param currentLoginedUser
* @return
Index: forgon-core/src/main/java/com/forgon/security/service/IpLoginLockRecordManager.java
===================================================================
diff -u
--- forgon-core/src/main/java/com/forgon/security/service/IpLoginLockRecordManager.java (revision 0)
+++ forgon-core/src/main/java/com/forgon/security/service/IpLoginLockRecordManager.java (revision 40542)
@@ -0,0 +1,38 @@
+package com.forgon.security.service;
+
+import javax.servlet.http.HttpServletRequest;
+
+import com.forgon.security.model.IpLoginLockRecord;
+import com.forgon.tools.hibernate.BasePoManager;
+
+/**
+ * IP登录锁定记录的manager(GZSZYY-119)
+ */
+public interface IpLoginLockRecordManager extends BasePoManager {
+
+ /**
+ * 判断IP是否被锁定,不允许登录
+ * @param request 请求
+ */
+ public void isLockedIP(HttpServletRequest request);
+
+ /**
+ * 清除登录成功的ip的登录失败次数
+ * @param ip ip地址
+ */
+ public void clearIpLoginFailCount(String ip);
+
+ /**
+ * 根据ip查询IP锁定记录
+ * @param ip ip地址
+ * @return IP锁定记录
+ */
+ public IpLoginLockRecord getIpLoginLockRecordByIp(String ip);
+
+ /**
+ * 记录Ip登录失败记录
+ * @param ipLoginLockRecord IP锁定记录
+ */
+ public void recordIpLoginFailRecordWithTransNewManager(IpLoginLockRecord ipLoginLockRecord);
+
+}
Index: ssts-web/src/main/webapp/disinfectsystem/config/gzszyy/config.js
===================================================================
diff -u -r39843 -r40542
--- ssts-web/src/main/webapp/disinfectsystem/config/gzszyy/config.js (.../config.js) (revision 39843)
+++ ssts-web/src/main/webapp/disinfectsystem/config/gzszyy/config.js (.../config.js) (revision 40542)
@@ -176,5 +176,7 @@
//启用设备维护分组管理功能
enableGroupManagementOfDeviceMaintenanceFunction:true,
//关闭自定义表单功能
-closeFormDefinition:false
+closeFormDefinition:false,
+//登录安全配置
+loginSecurirtyConfig: {"loginFailuresCountOfIP":999, "lockTimeInMinutesOfIP":1}
}
\ No newline at end of file
Index: ssts-web/src/main/webapp/disinfectsystem/config/gzszyy/menu/menuconfigure.json
===================================================================
diff -u -r40383 -r40542
--- ssts-web/src/main/webapp/disinfectsystem/config/gzszyy/menu/menuconfigure.json (.../menuconfigure.json) (revision 40383)
+++ ssts-web/src/main/webapp/disinfectsystem/config/gzszyy/menu/menuconfigure.json (.../menuconfigure.json) (revision 40542)
@@ -2660,6 +2660,17 @@
}
},
{
+ "requestName": "用户列表dwr接口",
+ "URI": "/dwr/call/plaincall/UserTableManager.findOrgUserTableList.dwr",
+ "parameter": { },
+ "operationIds": [
+ "System_User_Select"
+ ],
+ "systemSetConfig": {
+ "moduleWhiteList": "userManage"
+ }
+ },
+ {
"requestName": "权限管理",
"URI": "/systemmanage/roleList.jsp",
"parameter": { },
@@ -2669,6 +2680,15 @@
"systemSetConfig": { }
},
{
+ "requestName": "权限管理角色列表dwr接口",
+ "URI": "/dwr/call/plaincall/RoleTableManager.findAll.dwr",
+ "parameter": { },
+ "operationIds": [
+ "System_Role_Select"
+ ],
+ "systemSetConfig": { }
+ },
+ {
"requestName": "日志管理",
"URI": "/log/loggrid.jsp",
"parameter": { },