From 4f67f93c285f31cf3f856fd9db4c89d57ac9cc47 Mon Sep 17 00:00:00 2001 From: "Crystal.Sea" Date: Mon, 24 Aug 2020 23:27:29 +0800 Subject: [PATCH] PasswordPolicyValidator PasswordPolicyValidator --- ReleaseNotes.txt | 11 +- build.gradle | 1 + .../authn/RealmAuthenticationProvider.java | 2 +- .../realm/AbstractAuthenticationRealm.java | 259 +--- .../jdbc/DefaultJdbcAuthenticationRealm.java | 4 +- .../ApplicationAutoConfiguration.java | 18 + .../org/maxkey/constants/ConstantsStatus.java | 2 + .../maxkey/crypto/password/PasswordGen.java | 8 +- .../persistence/db/LoginHistoryService.java | 43 + .../maxkey/persistence/db/LoginService.java | 184 +++ .../db/PasswordPolicyValidator.java | 316 +++++ .../persistence/db/top_weak_password.txt | 1100 +++++++++++++++++ .../org/maxkey/crypto/PasswordGenTest.java | 7 +- .../password/PasswordPolicyValidatorTest.java | 31 + maxkey-core/src/test/resources/log4j2.xml | 37 + .../persistence/service/UserInfoService.java | 13 + .../maxkey/web/contorller/SafeController.java | 2 +- 17 files changed, 1807 insertions(+), 231 deletions(-) create mode 100644 maxkey-core/src/main/java/org/maxkey/persistence/db/LoginHistoryService.java create mode 100644 maxkey-core/src/main/java/org/maxkey/persistence/db/LoginService.java create mode 100644 maxkey-core/src/main/java/org/maxkey/persistence/db/PasswordPolicyValidator.java create mode 100644 maxkey-core/src/main/resources/org/maxkey/persistence/db/top_weak_password.txt create mode 100644 maxkey-core/src/test/java/org/maxkey/crypto/password/PasswordPolicyValidatorTest.java create mode 100644 maxkey-core/src/test/resources/log4j2.xml diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index 9d5850f82..b0143f9e3 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -1,8 +1,13 @@ MaxKey v 2.2.0 GA 2020/**/** *(MAXKEY-200801) 官方网站内容调整,初步增加英文版支持 - *(MAXKEY-200802) CAS协议增加自定义参数回传 - *(MAXKEY-200803) 优化开发集成指南 - *(MAXKEY-200804) 删除冗余的文件和文件夹 + *(MAXKEY-200802) 密码策略优化 + *(MAXKEY-200803) CAS协议增加自定义参数回传 + *(MAXKEY-200804) 优化开发集成指南 + *(MAXKEY-200805) 删除冗余的文件和文件夹 + *(MAXKEY-200806) CAS适配器支持 + *(MAXKEY-200807) Maven版本支持 + *(MAXKEY-200808) CAS spring boot demo + MaxKey v 2.1.0 GA 2020/08/01 diff --git a/build.gradle b/build.gradle index 24f284909..4b13ecb61 100644 --- a/build.gradle +++ b/build.gradle @@ -291,6 +291,7 @@ subprojects { compile group: 'org.apache.santuario', name: 'xmlsec', version: '1.5.8' compile group: 'org.ogce', name: 'xpp3', version: '1.1.6' compile group: 'com.thoughtworks.xstream', name: 'xstream', version: '1.4.10' + compile group: 'org.passay', name: 'passay', version: '1.6.0' //local jars compile fileTree(dir: "${rootDir}/maxkey-lib/", include: '*.jar') diff --git a/maxkey-core/src/main/java/org/maxkey/authn/RealmAuthenticationProvider.java b/maxkey-core/src/main/java/org/maxkey/authn/RealmAuthenticationProvider.java index f189d0418..da41f3832 100644 --- a/maxkey-core/src/main/java/org/maxkey/authn/RealmAuthenticationProvider.java +++ b/maxkey-core/src/main/java/org/maxkey/authn/RealmAuthenticationProvider.java @@ -68,7 +68,7 @@ public class RealmAuthenticationProvider extends AbstractAuthenticationProvider tftcaptchaValid(auth.getOtpCaptcha(),auth.getAuthType(),userInfo); - authenticationRealm.passwordPolicyValid(userInfo); + authenticationRealm.getPasswordPolicyValidator().passwordPolicyValid(userInfo); authenticationRealm.passwordMatches(userInfo, auth.getPassword()); authenticationRealm.grantAuthority(userInfo); diff --git a/maxkey-core/src/main/java/org/maxkey/authn/realm/AbstractAuthenticationRealm.java b/maxkey-core/src/main/java/org/maxkey/authn/realm/AbstractAuthenticationRealm.java index b932d25bf..eb69ca68e 100644 --- a/maxkey-core/src/main/java/org/maxkey/authn/realm/AbstractAuthenticationRealm.java +++ b/maxkey-core/src/main/java/org/maxkey/authn/realm/AbstractAuthenticationRealm.java @@ -17,27 +17,17 @@ package org.maxkey.authn.realm; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Types; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.servlet.http.HttpServletResponse; -import org.joda.time.DateTime; -import org.joda.time.Duration; -import org.joda.time.format.DateTimeFormat; import org.maxkey.authn.support.rememberme.AbstractRemeberMeService; -import org.maxkey.constants.ConstantsLoginType; -import org.maxkey.constants.ConstantsPasswordSetType; -import org.maxkey.constants.ConstantsStatus; import org.maxkey.domain.Groups; -import org.maxkey.domain.PasswordPolicy; import org.maxkey.domain.UserInfo; -import org.maxkey.persistence.db.PasswordPolicyRowMapper; -import org.maxkey.persistence.db.UserInfoRowMapper; +import org.maxkey.persistence.db.LoginHistoryService; +import org.maxkey.persistence.db.PasswordPolicyValidator; +import org.maxkey.persistence.db.LoginService; import org.maxkey.util.DateUtils; -import org.maxkey.util.StringUtils; import org.maxkey.web.WebConstants; import org.maxkey.web.WebContext; import org.slf4j.Logger; @@ -45,10 +35,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; /** * AbstractAuthenticationRealm. @@ -58,35 +45,18 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority; public abstract class AbstractAuthenticationRealm { private static Logger _logger = LoggerFactory.getLogger(AbstractAuthenticationRealm.class); - private static final String LOCK_USER_UPDATE_STATEMENT = "UPDATE MXK_USERINFO SET ISLOCKED = ? , UNLOCKTIME = ? WHERE ID = ?"; - - private static final String UNLOCK_USER_UPDATE_STATEMENT = "UPDATE MXK_USERINFO SET ISLOCKED = ? , UNLOCKTIME = ? WHERE ID = ?"; - - private static final String BADPASSWORDCOUNT_UPDATE_STATEMENT = "UPDATE MXK_USERINFO SET BADPASSWORDCOUNT = ? , BADPASSWORDTIME = ? WHERE ID = ?"; - - private static final String BADPASSWORDCOUNT_RESET_UPDATE_STATEMENT = "UPDATE MXK_USERINFO SET BADPASSWORDCOUNT = ? , ISLOCKED = ? ,UNLOCKTIME = ? WHERE ID = ?"; - - private static final String HISTORY_LOGIN_INSERT_STATEMENT = "INSERT INTO MXK_HISTORY_LOGIN (ID , SESSIONID , UID , USERNAME , DISPLAYNAME , LOGINTYPE , MESSAGE , CODE , PROVIDER , SOURCEIP , BROWSER , PLATFORM , APPLICATION , LOGINURL )VALUES( ? , ? , ? , ? , ?, ? , ? , ?, ? , ? , ?, ? , ? , ?)"; - - private static final String LOGIN_USERINFO_UPDATE_STATEMENT = "UPDATE MXK_USERINFO SET LASTLOGINTIME = ? , LASTLOGINIP = ? , LOGINCOUNT = ?, ONLINE = " - + UserInfo.ONLINE.ONLINE + " WHERE ID = ?"; - - private static final String LOGOUT_USERINFO_UPDATE_STATEMENT = "UPDATE MXK_USERINFO SET LASTLOGOFFTIME = ? , ONLINE = " - + UserInfo.ONLINE.OFFLINE + " WHERE ID = ?"; - - private static final String HISTORY_LOGOUT_UPDATE_STATEMENT = "UPDATE MXK_HISTORY_LOGIN SET LOGOUTTIME = ? WHERE SESSIONID = ?"; - - private static final String GROUPS_SELECT_STATEMENT = "SELECT DISTINCT G.ID,G.NAME FROM MXK_USERINFO U,`MXK_GROUPS` G,MXK_GROUP_MEMBER GM WHERE U.ID = ? AND U.ID=GM.MEMBERID AND GM.GROUPID=G.ID "; - - private static final String DEFAULT_USERINFO_SELECT_STATEMENT = "SELECT * FROM MXK_USERINFO WHERE USERNAME = ?"; - - private static final String PASSWORD_POLICY_SELECT_STATEMENT = "SELECT ID,MINLENGTH,MAXLENGTH,LOWERCASE,UPPERCASE,DIGITS,SPECIALCHAR,ATTEMPTS,DURATION,EXPIRATION,USERNAME,SIMPLEPASSWORDS FROM MXK_PASSWORD_POLICY "; - - protected PasswordPolicy passwordPolicy; - protected JdbcTemplate jdbcTemplate; - + protected boolean provisioning; + + @Autowired + protected PasswordPolicyValidator passwordPolicyValidator; + + @Autowired + protected LoginService loginService; + + @Autowired + protected LoginHistoryService loginHistoryService; @Autowired @Qualifier("remeberMeService") @@ -103,70 +73,16 @@ public abstract class AbstractAuthenticationRealm { this.jdbcTemplate = jdbcTemplate; } - public PasswordPolicy getPasswordPolicy() { - if (passwordPolicy == null) { - passwordPolicy = jdbcTemplate.queryForObject(PASSWORD_POLICY_SELECT_STATEMENT, - new PasswordPolicyRowMapper()); - _logger.debug("query PasswordPolicy : " + passwordPolicy); - } - return passwordPolicy; + public PasswordPolicyValidator getPasswordPolicyValidator() { + return passwordPolicyValidator; } - public boolean passwordPolicyValid(UserInfo userInfo) { - /* - * check login attempts fail times - */ - if (userInfo.getBadPasswordCount() >= getPasswordPolicy().getAttempts()) { - _logger.debug("PasswordPolicy : " + passwordPolicy); - _logger.debug("login Attempts is " + userInfo.getBadPasswordCount()); - lockUser(userInfo); - - throw new BadCredentialsException( - WebContext.getI18nValue("login.error.attempts") + " " + userInfo.getBadPasswordCount()); - } - - if (userInfo.getPasswordSetType() != ConstantsPasswordSetType.PASSWORD_NORMAL) { - WebContext.getSession().setAttribute(WebConstants.CURRENT_LOGIN_USER_PASSWORD_SET_TYPE, - userInfo.getPasswordSetType()); - return true; - } else { - WebContext.getSession().setAttribute(WebConstants.CURRENT_LOGIN_USER_PASSWORD_SET_TYPE, - ConstantsPasswordSetType.PASSWORD_NORMAL); - } - - /* - * check password is Expired,if Expiration equals 0,not need check - */ - if (getPasswordPolicy().getExpiration() > 0) { - - String passwordLastSetTimeString = userInfo.getPasswordLastSetTime().substring(0, 19); - _logger.info("last password set date " + passwordLastSetTimeString); - - DateTime currentdateTime = new DateTime(); - DateTime changePwdDateTime = DateTime.parse(passwordLastSetTimeString, - DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")); - Duration duration = new Duration(changePwdDateTime, currentdateTime); - int intDuration = Integer.parseInt(duration.getStandardDays() + ""); - _logger.debug("validate duration " + intDuration); - _logger.debug("validate result " + (intDuration <= getPasswordPolicy().getExpiration())); - if (intDuration > getPasswordPolicy().getExpiration()) { - WebContext.getSession().setAttribute(WebConstants.CURRENT_LOGIN_USER_PASSWORD_SET_TYPE, - ConstantsPasswordSetType.PASSWORD_EXPIRED); - } - } - - return true; + public LoginService getUserInfoLoginService() { + return loginService; } public UserInfo loadUserInfo(String username, String password) { - List listUserInfo = jdbcTemplate.query(DEFAULT_USERINFO_SELECT_STATEMENT, new UserInfoRowMapper(), - username); - UserInfo userInfo = null; - if (listUserInfo != null && listUserInfo.size() > 0) { - userInfo = listUserInfo.get(0); - } - _logger.debug("load UserInfo : " + userInfo); - return userInfo; + return loginService.loadUserInfo(username, password); } public abstract boolean passwordMatches(UserInfo userInfo, String password); @@ -179,90 +95,9 @@ public abstract class AbstractAuthenticationRealm { } } - /** - * 閿佸畾鐢ㄦ埛锛歩slock锛�1 鐢ㄦ埛瑙i攣 2 鐢ㄦ埛閿佸畾 - * - * @param userInfo - */ - public void lockUser(UserInfo userInfo) { - try { - if (userInfo != null && StringUtils.isNotEmpty(userInfo.getId())) { - jdbcTemplate.update(LOCK_USER_UPDATE_STATEMENT, - new Object[] { ConstantsStatus.LOCK, new Date(), userInfo.getId() }, - new int[] { Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR }); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * 閿佸畾鐢ㄦ埛锛歩slock锛�1 鐢ㄦ埛瑙i攣 2 鐢ㄦ埛閿佸畾 - * - * @param userInfo - */ - public void unlockUser(UserInfo userInfo) { - try { - if (userInfo != null && StringUtils.isNotEmpty(userInfo.getId())) { - jdbcTemplate.update(UNLOCK_USER_UPDATE_STATEMENT, - new Object[] { ConstantsStatus.ACTIVE, new Date(), userInfo.getId() }, - new int[] { Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR }); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * 閲嶇疆閿欒瀵嗙爜娆℃暟鍜岃В閿佺敤鎴� - * - * @param userInfo - */ - public void resetBadPasswordCountAndLockout(UserInfo userInfo) { - try { - if (userInfo != null && StringUtils.isNotEmpty(userInfo.getId())) { - jdbcTemplate.update(BADPASSWORDCOUNT_RESET_UPDATE_STATEMENT, - new Object[] { 0, ConstantsStatus.ACTIVE, new Date(), userInfo.getId() }, - new int[] { Types.INTEGER, Types.INTEGER, Types.TIMESTAMP, Types.VARCHAR }); - } - } catch (Exception e) { - e.printStackTrace(); - _logger.error(e.getMessage()); - } - } - - /** - * 鏇存柊閿欒瀵嗙爜娆℃暟 - * - * @param userInfo - */ - public void setBadPasswordCount(UserInfo userInfo) { - try { - if (userInfo != null && StringUtils.isNotEmpty(userInfo.getId())) { - int badPasswordCount = userInfo.getBadPasswordCount() + 1; - userInfo.setBadPasswordCount(badPasswordCount); - jdbcTemplate.update(BADPASSWORDCOUNT_UPDATE_STATEMENT, - new Object[] { badPasswordCount, new Date(), userInfo.getId() }, - new int[] { Types.INTEGER, Types.TIMESTAMP, Types.VARCHAR }); - insertLoginHistory(userInfo, ConstantsLoginType.LOCAL, "", "xe00000004", "password error"); - } - } catch (Exception e) { - e.printStackTrace(); - _logger.error(e.getMessage()); - } - } public List queryGroups(UserInfo userInfo) { - List listGroups = jdbcTemplate.query(GROUPS_SELECT_STATEMENT, new RowMapper() { - public Groups mapRow(ResultSet rs, int rowNum) throws SQLException { - Groups group = new Groups(rs.getString("ID"), rs.getString("NAME"), 0); - - return group; - } - }, userInfo.getId()); - - _logger.debug("list Groups " + listGroups); - return listGroups; + return loginService.queryGroups(userInfo); } /** @@ -272,18 +107,7 @@ public abstract class AbstractAuthenticationRealm { * @return ArrayList */ public ArrayList grantAuthority(UserInfo userInfo) { - // query roles for user - List listGroups = queryGroups(userInfo); - - // set role for spring security - ArrayList grantedAuthority = new ArrayList(); - grantedAuthority.add(new SimpleGrantedAuthority("ROLE_USER")); - for (Groups group : listGroups) { - grantedAuthority.add(new SimpleGrantedAuthority(group.getId())); - } - _logger.debug("Authority : " + grantedAuthority); - - return grantedAuthority; + return loginService.grantAuthority(userInfo); } /** @@ -296,10 +120,10 @@ public abstract class AbstractAuthenticationRealm { * @param message */ public boolean insertLoginHistory(UserInfo userInfo, String type, String provider, String code, String message) { - Date loginDate = new Date(); String sessionId = WebContext.genId(); WebContext.setAttribute(WebConstants.CURRENT_USER_SESSION_ID, sessionId); - String ipAddress = WebContext.getRequestIpAddress(); + userInfo.setLastLoginTime(DateUtils.formatDateTime(new Date())); + userInfo.setLastLoginIp(WebContext.getRequestIpAddress()); String platform = ""; String browser = ""; String userAgent = WebContext.getRequest().getHeader("User-Agent"); @@ -337,43 +161,38 @@ public abstract class AbstractAuthenticationRealm { } - jdbcTemplate.update(HISTORY_LOGIN_INSERT_STATEMENT, - new Object[] { WebContext.genId(), sessionId, userInfo.getId(), userInfo.getUsername(), - userInfo.getDisplayName(), type, message, code, provider, ipAddress, browser, platform, - "Browser", loginDate }, - new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, - Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, - Types.VARCHAR, Types.TIMESTAMP }); - - userInfo.setLastLoginTime(DateUtils.formatDateTime(loginDate)); - - jdbcTemplate.update(LOGIN_USERINFO_UPDATE_STATEMENT, - new Object[] { loginDate, ipAddress, userInfo.getLoginCount() + 1, userInfo.getId() }, - new int[] { Types.TIMESTAMP, Types.VARCHAR, Types.INTEGER, Types.VARCHAR }); + loginHistoryService.login(userInfo,sessionId, type, message, code, provider, browser, platform); + + loginService.setLastLoginInfo(userInfo); return true; } + /** + * logout user and remove RemeberMe token + * @param response + * @return + */ public boolean logout(HttpServletResponse response) { if (isAuthenticated()) { Object sessionIdAttribute = WebContext.getAttribute(WebConstants.CURRENT_USER_SESSION_ID); UserInfo userInfo = WebContext.getUserInfo(); - Date logoutDateTime = new Date(); + userInfo.setLastLogoffTime(DateUtils.formatDateTime(new Date())); + if (sessionIdAttribute != null) { remeberMeService.removeRemeberMe(response); - jdbcTemplate.update(HISTORY_LOGOUT_UPDATE_STATEMENT, - new Object[] { logoutDateTime, sessionIdAttribute.toString() }, - new int[] { Types.TIMESTAMP, Types.VARCHAR }); + loginHistoryService.logoff(userInfo.getLastLogoffTime(), sessionIdAttribute.toString()); } - - jdbcTemplate.update(LOGOUT_USERINFO_UPDATE_STATEMENT, new Object[] { logoutDateTime, userInfo.getId() }, - new int[] { Types.TIMESTAMP, Types.VARCHAR }); - + + loginService.setLastLogoffInfo(userInfo); + _logger.debug("Session " + WebContext.getAttribute(WebConstants.CURRENT_USER_SESSION_ID) + ", user " - + userInfo.getUsername() + " Logout, datetime " + DateUtils.toUtc(logoutDateTime) + " ."); + + userInfo.getUsername() + " Logout, datetime " + userInfo.getLastLogoffTime() + " ."); } return true; } + + } diff --git a/maxkey-core/src/main/java/org/maxkey/authn/realm/jdbc/DefaultJdbcAuthenticationRealm.java b/maxkey-core/src/main/java/org/maxkey/authn/realm/jdbc/DefaultJdbcAuthenticationRealm.java index 53c052ec4..552222aab 100644 --- a/maxkey-core/src/main/java/org/maxkey/authn/realm/jdbc/DefaultJdbcAuthenticationRealm.java +++ b/maxkey-core/src/main/java/org/maxkey/authn/realm/jdbc/DefaultJdbcAuthenticationRealm.java @@ -18,6 +18,7 @@ package org.maxkey.authn.realm.jdbc; import org.maxkey.authn.realm.AbstractAuthenticationRealm; +import org.maxkey.constants.ConstantsLoginType; import org.maxkey.crypto.password.PasswordReciprocal; import org.maxkey.domain.UserInfo; import org.maxkey.web.WebContext; @@ -58,7 +59,8 @@ public class DefaultJdbcAuthenticationRealm extends AbstractAuthenticationRealm passwordMatches = passwordEncoder.matches(password,userInfo.getPassword()); _logger.debug("passwordvalid : " + passwordMatches); if (!passwordMatches) { - setBadPasswordCount(userInfo); + passwordPolicyValidator.setBadPasswordCount(userInfo); + insertLoginHistory(userInfo, ConstantsLoginType.LOCAL, "", "xe00000004", "password error"); throw new BadCredentialsException(WebContext.getI18nValue("login.error.password")); } return passwordMatches; diff --git a/maxkey-core/src/main/java/org/maxkey/autoconfigure/ApplicationAutoConfiguration.java b/maxkey-core/src/main/java/org/maxkey/autoconfigure/ApplicationAutoConfiguration.java index f9bbae184..486eeb1ac 100644 --- a/maxkey-core/src/main/java/org/maxkey/autoconfigure/ApplicationAutoConfiguration.java +++ b/maxkey-core/src/main/java/org/maxkey/autoconfigure/ApplicationAutoConfiguration.java @@ -34,6 +34,7 @@ import org.maxkey.crypto.keystore.KeyStoreLoader; import org.maxkey.crypto.password.PasswordReciprocal; import org.maxkey.crypto.password.SM3PasswordEncoder; import org.maxkey.crypto.password.StandardPasswordEncoder; +import org.maxkey.persistence.db.PasswordPolicyValidator; import org.maxkey.persistence.redis.RedisConnectionFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,6 +59,8 @@ import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder; import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder; +import org.maxkey.persistence.db.LoginService; +import org.maxkey.persistence.db.LoginHistoryService; @Configuration @@ -126,6 +129,21 @@ public class ApplicationAutoConfiguration implements InitializingBean { return new DataSourceTransactionManager(dataSource); } + @Bean(name = "passwordPolicyValidator") + public PasswordPolicyValidator passwordPolicyValidator(JdbcTemplate jdbcTemplate) { + return new PasswordPolicyValidator(jdbcTemplate); + } + + @Bean(name = "loginService") + public LoginService LoginService(JdbcTemplate jdbcTemplate) { + return new LoginService(jdbcTemplate); + } + @Bean(name = "loginHistoryService") + public LoginHistoryService loginHistoryService(JdbcTemplate jdbcTemplate) { + return new LoginHistoryService(jdbcTemplate); + } + + /** * Authentication Password Encoder . * @return diff --git a/maxkey-core/src/main/java/org/maxkey/constants/ConstantsStatus.java b/maxkey-core/src/main/java/org/maxkey/constants/ConstantsStatus.java index ac743f961..af3c3e32d 100644 --- a/maxkey-core/src/main/java/org/maxkey/constants/ConstantsStatus.java +++ b/maxkey-core/src/main/java/org/maxkey/constants/ConstantsStatus.java @@ -43,5 +43,7 @@ public final class ConstantsStatus { public static final int STOP = 12; public static final int APPROVED = 13; + + public static final int QUITED = 14; } diff --git a/maxkey-core/src/main/java/org/maxkey/crypto/password/PasswordGen.java b/maxkey-core/src/main/java/org/maxkey/crypto/password/PasswordGen.java index ead3f7cde..a0cf7f37a 100644 --- a/maxkey-core/src/main/java/org/maxkey/crypto/password/PasswordGen.java +++ b/maxkey-core/src/main/java/org/maxkey/crypto/password/PasswordGen.java @@ -34,16 +34,19 @@ public class PasswordGen { public static String CHAR_DEFAULT = CHAR_LOWERCASE + CHAR_NUMBERS + CHAR_UPPERCASE; private Random random = new Random(); public static int DEFAULT_LENGTH = 8; + private int length; public PasswordGen() { - + length = DEFAULT_LENGTH; } public String gen() { - return gen(DEFAULT_LENGTH); + this.length = DEFAULT_LENGTH; + return gen(length); } public String gen(int length) { + this.length = length; return gen(CHAR_DEFAULT, length); } @@ -61,6 +64,7 @@ public class PasswordGen { password.append(gen(CHAR_NUMBERS, numbers)); password.append(gen(CHAR_UPPERCASE, upperCase)); password.append(gen(CHAR_SPECIAL, special)); + password.append(gen(CHAR_DEFAULT, length - lowerCase - upperCase - numbers -special)); // random generator String by sequence password return gen(password.toString(), password.length()); } diff --git a/maxkey-core/src/main/java/org/maxkey/persistence/db/LoginHistoryService.java b/maxkey-core/src/main/java/org/maxkey/persistence/db/LoginHistoryService.java new file mode 100644 index 000000000..12511d7f6 --- /dev/null +++ b/maxkey-core/src/main/java/org/maxkey/persistence/db/LoginHistoryService.java @@ -0,0 +1,43 @@ +package org.maxkey.persistence.db; + +import java.sql.Types; + +import org.maxkey.domain.UserInfo; +import org.maxkey.web.WebContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.JdbcTemplate; + +public class LoginHistoryService { + private static Logger _logger = LoggerFactory.getLogger(LoginHistoryService.class); + + private static final String HISTORY_LOGIN_INSERT_STATEMENT = "INSERT INTO MXK_HISTORY_LOGIN (ID , SESSIONID , UID , USERNAME , DISPLAYNAME , LOGINTYPE , MESSAGE , CODE , PROVIDER , SOURCEIP , BROWSER , PLATFORM , APPLICATION , LOGINURL )VALUES( ? , ? , ? , ? , ?, ? , ? , ?, ? , ? , ?, ? , ? , ?)"; + + private static final String HISTORY_LOGOUT_UPDATE_STATEMENT = "UPDATE MXK_HISTORY_LOGIN SET LOGOUTTIME = ? WHERE SESSIONID = ?"; + + protected JdbcTemplate jdbcTemplate; + + public LoginHistoryService(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + public void login(UserInfo userInfo,String sessionId, + String type, String message, String code, String provider,String browser, String platform) { + jdbcTemplate.update(HISTORY_LOGIN_INSERT_STATEMENT, + new Object[] { WebContext.genId(), sessionId, userInfo.getId(), userInfo.getUsername(), + userInfo.getDisplayName(), type, message, code, provider, userInfo.getLastLoginIp(), browser, platform, + "Browser", userInfo.getLastLoginTime() }, + new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, + Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, + Types.VARCHAR, Types.TIMESTAMP }); + + + } + + public void logoff(String lastLogoffTime,String sessionId) { + _logger.debug(" sessionId " +sessionId +" , lastlogofftime " + lastLogoffTime); + jdbcTemplate.update(HISTORY_LOGOUT_UPDATE_STATEMENT, + new Object[] { lastLogoffTime, sessionId }, + new int[] { Types.TIMESTAMP, Types.VARCHAR }); + } +} diff --git a/maxkey-core/src/main/java/org/maxkey/persistence/db/LoginService.java b/maxkey-core/src/main/java/org/maxkey/persistence/db/LoginService.java new file mode 100644 index 000000000..62e2ce3b1 --- /dev/null +++ b/maxkey-core/src/main/java/org/maxkey/persistence/db/LoginService.java @@ -0,0 +1,184 @@ +package org.maxkey.persistence.db; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.maxkey.constants.ConstantsStatus; +import org.maxkey.domain.Groups; +import org.maxkey.domain.UserInfo; +import org.maxkey.util.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; + +public class LoginService { + private static Logger _logger = LoggerFactory.getLogger(LoginService.class); + + private static final String LOCK_USER_UPDATE_STATEMENT = "UPDATE MXK_USERINFO SET ISLOCKED = ? , UNLOCKTIME = ? WHERE ID = ?"; + + private static final String UNLOCK_USER_UPDATE_STATEMENT = "UPDATE MXK_USERINFO SET ISLOCKED = ? , UNLOCKTIME = ? WHERE ID = ?"; + + private static final String BADPASSWORDCOUNT_UPDATE_STATEMENT = "UPDATE MXK_USERINFO SET BADPASSWORDCOUNT = ? , BADPASSWORDTIME = ? WHERE ID = ?"; + + private static final String BADPASSWORDCOUNT_RESET_UPDATE_STATEMENT = "UPDATE MXK_USERINFO SET BADPASSWORDCOUNT = ? , ISLOCKED = ? ,UNLOCKTIME = ? WHERE ID = ?"; + + private static final String LOGIN_USERINFO_UPDATE_STATEMENT = "UPDATE MXK_USERINFO SET LASTLOGINTIME = ? , LASTLOGINIP = ? , LOGINCOUNT = ?, ONLINE = " + + UserInfo.ONLINE.ONLINE + " WHERE ID = ?"; + + private static final String LOGOUT_USERINFO_UPDATE_STATEMENT = "UPDATE MXK_USERINFO SET LASTLOGOFFTIME = ? , ONLINE = " + + UserInfo.ONLINE.OFFLINE + " WHERE ID = ?"; + + private static final String GROUPS_SELECT_STATEMENT = "SELECT DISTINCT G.ID,G.NAME FROM MXK_USERINFO U,`MXK_GROUPS` G,MXK_GROUP_MEMBER GM WHERE U.ID = ? AND U.ID=GM.MEMBERID AND GM.GROUPID=G.ID "; + + private static final String DEFAULT_USERINFO_SELECT_STATEMENT = "SELECT * FROM MXK_USERINFO WHERE USERNAME = ?"; + + protected JdbcTemplate jdbcTemplate; + + public LoginService(){ + + } + + public LoginService(JdbcTemplate jdbcTemplate){ + this.jdbcTemplate=jdbcTemplate; + } + + public UserInfo loadUserInfo(String username, String password) { + List listUserInfo = jdbcTemplate.query(DEFAULT_USERINFO_SELECT_STATEMENT, new UserInfoRowMapper(), + username); + UserInfo userInfo = null; + if (listUserInfo != null && listUserInfo.size() > 0) { + userInfo = listUserInfo.get(0); + } + _logger.debug("load UserInfo : " + userInfo); + return userInfo; + } + + + + /** + * 閿佸畾鐢ㄦ埛锛歩slock锛�1 鐢ㄦ埛瑙i攣 2 鐢ㄦ埛閿佸畾 + * + * @param userInfo + */ + public void lockUser(UserInfo userInfo) { + try { + if (userInfo != null && StringUtils.isNotEmpty(userInfo.getId())) { + jdbcTemplate.update(LOCK_USER_UPDATE_STATEMENT, + new Object[] { ConstantsStatus.LOCK, new Date(), userInfo.getId() }, + new int[] { Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR }); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 閿佸畾鐢ㄦ埛锛歩slock锛�1 鐢ㄦ埛瑙i攣 2 鐢ㄦ埛閿佸畾 + * + * @param userInfo + */ + public void unlockUser(UserInfo userInfo) { + try { + if (userInfo != null && StringUtils.isNotEmpty(userInfo.getId())) { + jdbcTemplate.update(UNLOCK_USER_UPDATE_STATEMENT, + new Object[] { ConstantsStatus.ACTIVE, new Date(), userInfo.getId() }, + new int[] { Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR }); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * reset BadPasswordCount And Lockout + * + * @param userInfo + */ + public void resetBadPasswordCountAndLockout(UserInfo userInfo) { + try { + if (userInfo != null && StringUtils.isNotEmpty(userInfo.getId())) { + jdbcTemplate.update(BADPASSWORDCOUNT_RESET_UPDATE_STATEMENT, + new Object[] { 0, ConstantsStatus.ACTIVE, new Date(), userInfo.getId() }, + new int[] { Types.INTEGER, Types.INTEGER, Types.TIMESTAMP, Types.VARCHAR }); + } + } catch (Exception e) { + e.printStackTrace(); + _logger.error(e.getMessage()); + } + } + + /** + * if login password is error ,BadPasswordCount++ and set bad date + * + * @param userInfo + */ + public void setBadPasswordCount(UserInfo userInfo) { + try { + if (userInfo != null && StringUtils.isNotEmpty(userInfo.getId())) { + int badPasswordCount = userInfo.getBadPasswordCount() + 1; + userInfo.setBadPasswordCount(badPasswordCount); + jdbcTemplate.update(BADPASSWORDCOUNT_UPDATE_STATEMENT, + new Object[] { badPasswordCount, new Date(), userInfo.getId() }, + new int[] { Types.INTEGER, Types.TIMESTAMP, Types.VARCHAR }); + } + } catch (Exception e) { + e.printStackTrace(); + _logger.error(e.getMessage()); + } + } + + public List queryGroups(UserInfo userInfo) { + List listGroups = jdbcTemplate.query(GROUPS_SELECT_STATEMENT, new RowMapper() { + public Groups mapRow(ResultSet rs, int rowNum) throws SQLException { + Groups group = new Groups(rs.getString("ID"), rs.getString("NAME"), 0); + + return group; + } + }, userInfo.getId()); + + _logger.debug("list Groups " + listGroups); + return listGroups; + } + + /** + * grant Authority by userinfo + * + * @param userInfo + * @return ArrayList + */ + public ArrayList grantAuthority(UserInfo userInfo) { + // query roles for user + List listGroups = queryGroups(userInfo); + + // set role for spring security + ArrayList grantedAuthority = new ArrayList(); + grantedAuthority.add(new SimpleGrantedAuthority("ROLE_USER")); + for (Groups group : listGroups) { + grantedAuthority.add(new SimpleGrantedAuthority(group.getId())); + } + _logger.debug("Authority : " + grantedAuthority); + + return grantedAuthority; + } + + + public void setLastLoginInfo(UserInfo userInfo) { + jdbcTemplate.update(LOGIN_USERINFO_UPDATE_STATEMENT, + new Object[] { userInfo.getLastLoginTime(), userInfo.getLastLoginIp(), userInfo.getLoginCount() + 1, userInfo.getId() }, + new int[] { Types.TIMESTAMP, Types.VARCHAR, Types.INTEGER, Types.VARCHAR }); + } + + + public void setLastLogoffInfo(UserInfo userInfo) { + jdbcTemplate.update(LOGOUT_USERINFO_UPDATE_STATEMENT, new Object[] { userInfo.getLastLogoffTime(), userInfo.getId() }, + new int[] { Types.TIMESTAMP, Types.VARCHAR }); + + } +} diff --git a/maxkey-core/src/main/java/org/maxkey/persistence/db/PasswordPolicyValidator.java b/maxkey-core/src/main/java/org/maxkey/persistence/db/PasswordPolicyValidator.java new file mode 100644 index 000000000..6119c7fe3 --- /dev/null +++ b/maxkey-core/src/main/java/org/maxkey/persistence/db/PasswordPolicyValidator.java @@ -0,0 +1,316 @@ +package org.maxkey.persistence.db; + +import java.io.InputStreamReader; +import java.sql.Types; +import java.util.ArrayList; +import java.util.Date; + +import org.ehcache.UserManagedCache; +import org.ehcache.config.builders.ExpiryPolicyBuilder; +import org.ehcache.config.builders.UserManagedCacheBuilder; +import org.joda.time.DateTime; +import org.joda.time.Duration; +import org.joda.time.format.DateTimeFormat; +import org.maxkey.constants.ConstantsPasswordSetType; +import org.maxkey.constants.ConstantsProperties; +import org.maxkey.constants.ConstantsStatus; +import org.maxkey.constants.ConstantsTimeInterval; +import org.maxkey.domain.PasswordPolicy; +import org.maxkey.domain.UserInfo; +import org.maxkey.util.StringUtils; +import org.maxkey.web.WebConstants; +import org.maxkey.web.WebContext; +import org.passay.CharacterRule; +import org.passay.DictionaryRule; +import org.passay.EnglishCharacterData; +import org.passay.LengthRule; +import org.passay.PasswordData; +import org.passay.PasswordValidator; +import org.passay.Rule; +import org.passay.RuleResult; +import org.passay.UsernameRule; +import org.passay.WhitespaceRule; +import org.passay.dictionary.Dictionary; +import org.passay.dictionary.DictionaryBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.ClassPathResource; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.security.authentication.BadCredentialsException; + +public class PasswordPolicyValidator { + private static Logger _logger = LoggerFactory.getLogger(PasswordPolicyValidator.class); + + public static final String topWeakPasswordPropertySource = + "classpath:/org/maxkey/persistence/db/top_weak_password.txt"; + protected static final UserManagedCache passwordPolicyStore = + UserManagedCacheBuilder.newUserManagedCacheBuilder(String.class, PasswordPolicy.class) + .withExpiry( + ExpiryPolicyBuilder.timeToLiveExpiration( + java.time.Duration.ofMinutes(ConstantsTimeInterval.ONE_HOUR) + ) + ) + .build(true); + + protected PasswordPolicy passwordPolicy; + + ArrayList passwordPolicyRuleList; + + protected JdbcTemplate jdbcTemplate; + + private static final String PASSWORD_POLICY_KEY = "PASSWORD_POLICY_KEY"; + private static final String LOCK_USER_UPDATE_STATEMENT = "UPDATE MXK_USERINFO SET ISLOCKED = ? , UNLOCKTIME = ? WHERE ID = ?"; + + private static final String PASSWORD_POLICY_SELECT_STATEMENT = "SELECT ID,MINLENGTH,MAXLENGTH,LOWERCASE,UPPERCASE,DIGITS,SPECIALCHAR,ATTEMPTS,DURATION,EXPIRATION,USERNAME,SIMPLEPASSWORDS FROM MXK_PASSWORD_POLICY "; + + private static final String UNLOCK_USER_UPDATE_STATEMENT = "UPDATE MXK_USERINFO SET ISLOCKED = ? , UNLOCKTIME = ? WHERE ID = ?"; + + private static final String BADPASSWORDCOUNT_UPDATE_STATEMENT = "UPDATE MXK_USERINFO SET BADPASSWORDCOUNT = ? , BADPASSWORDTIME = ? WHERE ID = ?"; + + private static final String BADPASSWORDCOUNT_RESET_UPDATE_STATEMENT = "UPDATE MXK_USERINFO SET BADPASSWORDCOUNT = ? , ISLOCKED = ? ,UNLOCKTIME = ? WHERE ID = ?"; + + public PasswordPolicyValidator() { + } + + public PasswordPolicyValidator(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + + public PasswordPolicy getPasswordPolicy() { + passwordPolicy = passwordPolicyStore.get(PASSWORD_POLICY_KEY); + + if (passwordPolicy == null) { + passwordPolicy = jdbcTemplate.queryForObject(PASSWORD_POLICY_SELECT_STATEMENT, + new PasswordPolicyRowMapper()); + _logger.debug("query PasswordPolicy : " + passwordPolicy); + passwordPolicyStore.put(PASSWORD_POLICY_KEY,passwordPolicy); + + passwordPolicyRuleList = new ArrayList(); + passwordPolicyRuleList.add(new WhitespaceRule()); + passwordPolicyRuleList.add(new LengthRule(passwordPolicy.getMinLength(), passwordPolicy.getMaxLength())); + + if(passwordPolicy.getUpperCase()>0) { + passwordPolicyRuleList.add(new CharacterRule(EnglishCharacterData.UpperCase, passwordPolicy.getUpperCase())); + } + if(passwordPolicy.getLowerCase()>0) { + passwordPolicyRuleList.add(new CharacterRule(EnglishCharacterData.LowerCase, passwordPolicy.getLowerCase())); + } + if(passwordPolicy.getDigits()>0) { + passwordPolicyRuleList.add(new CharacterRule(EnglishCharacterData.Digit, passwordPolicy.getDigits())); + } + if(passwordPolicy.getSpecialChar()>0) { + passwordPolicyRuleList.add(new CharacterRule(EnglishCharacterData.Special, passwordPolicy.getSpecialChar())); + } + if(passwordPolicy.getUsername()>0) { + passwordPolicyRuleList.add(new UsernameRule()); + } + + if(passwordPolicy.getSimplePasswords().length()>0 ) { + try { + ClassPathResource dictFile= + new ClassPathResource( + ConstantsProperties.classPathResource(topWeakPasswordPropertySource)); + + Dictionary dictionary =new DictionaryBuilder().addReader(new InputStreamReader(dictFile.getInputStream())).build(); + passwordPolicyRuleList.add(new DictionaryRule(dictionary)); + }catch(Exception e) { + e.printStackTrace(); + } + } + } + return passwordPolicy; + } + + /** + * validator . + * @param userInfo + * @return boolean + */ + public boolean validator(UserInfo userInfo) { + + String password = userInfo.getPassword(); + String username = userInfo.getUsername(); + + if(password.equals("") || password==null){ + _logger.debug("password is Empty "); + return false; + } + + getPasswordPolicy(); + + PasswordValidator validator = new PasswordValidator(passwordPolicyRuleList); + + RuleResult result = validator.validate(new PasswordData(username,password)); + + if (result.isValid()) { + System.out.println("Password is valid"); + } else { + System.out.println("Invalid password:"); + for (String msg : validator.getMessages(result)) { + System.out.println(msg); + } + } + + return true; + } + + + /** + * passwordPolicyValid . + * @param userInfo + * @return boolean + */ + public boolean passwordPolicyValid(UserInfo userInfo) { + + getPasswordPolicy(); + + /* + * check login attempts fail times + */ + if (userInfo.getBadPasswordCount() >= passwordPolicy.getAttempts()) { + _logger.debug("PasswordPolicy : " + passwordPolicy); + _logger.debug("login Attempts is " + userInfo.getBadPasswordCount()); + lockUser(userInfo); + + throw new BadCredentialsException( + userInfo.getUsername() + " " + + WebContext.getI18nValue("login.error.attempts") + " " + + userInfo.getBadPasswordCount() + ); + } + + //locked + if(userInfo.getIsLocked()==ConstantsStatus.LOCK) { + throw new BadCredentialsException( + userInfo.getUsername()+ " "+ + WebContext.getI18nValue("login.error.locked") + ); + } + // inactive + if(userInfo.getStatus()!=ConstantsStatus.ACTIVE) { + throw new BadCredentialsException( + userInfo.getUsername()+ " status "+ + userInfo.getStatus() + + WebContext.getI18nValue("login.error.inactive") + ); + } + + if (userInfo.getPasswordSetType() != ConstantsPasswordSetType.PASSWORD_NORMAL) { + WebContext.getSession().setAttribute(WebConstants.CURRENT_LOGIN_USER_PASSWORD_SET_TYPE, + userInfo.getPasswordSetType()); + return true; + } else { + WebContext.getSession().setAttribute(WebConstants.CURRENT_LOGIN_USER_PASSWORD_SET_TYPE, + ConstantsPasswordSetType.PASSWORD_NORMAL); + } + + /* + * check password is Expired,Expiration is Expired date ,if Expiration equals 0,not need check + * + */ + if (passwordPolicy.getExpiration() > 0) { + + String passwordLastSetTimeString = userInfo.getPasswordLastSetTime().substring(0, 19); + _logger.info("last password set date " + passwordLastSetTimeString); + + DateTime currentdateTime = new DateTime(); + DateTime changePwdDateTime = DateTime.parse(passwordLastSetTimeString, + DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")); + Duration duration = new Duration(changePwdDateTime, currentdateTime); + int intDuration = Integer.parseInt(duration.getStandardDays() + ""); + _logger.debug("validate duration " + intDuration); + _logger.debug("validate result " + (intDuration <= passwordPolicy.getExpiration())); + if (intDuration > passwordPolicy.getExpiration()) { + WebContext.getSession().setAttribute(WebConstants.CURRENT_LOGIN_USER_PASSWORD_SET_TYPE, + ConstantsPasswordSetType.PASSWORD_EXPIRED); + } + } + + return true; + } + + + /** + * lockUser + * + * @param userInfo + */ + public void lockUser(UserInfo userInfo) { + try { + if (userInfo != null && StringUtils.isNotEmpty(userInfo.getId())) { + jdbcTemplate.update(LOCK_USER_UPDATE_STATEMENT, + new Object[] { ConstantsStatus.LOCK, new Date(), userInfo.getId() }, + new int[] { Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR }); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + + /** + * unlockUser + * + * @param userInfo + */ + public void unlockUser(UserInfo userInfo) { + try { + if (userInfo != null && StringUtils.isNotEmpty(userInfo.getId())) { + jdbcTemplate.update(UNLOCK_USER_UPDATE_STATEMENT, + new Object[] { ConstantsStatus.ACTIVE, new Date(), userInfo.getId() }, + new int[] { Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR }); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * reset BadPasswordCount And Lockout + * + * @param userInfo + */ + public void resetBadPasswordCountAndLockout(UserInfo userInfo) { + try { + if (userInfo != null && StringUtils.isNotEmpty(userInfo.getId())) { + jdbcTemplate.update(BADPASSWORDCOUNT_RESET_UPDATE_STATEMENT, + new Object[] { 0, ConstantsStatus.ACTIVE, new Date(), userInfo.getId() }, + new int[] { Types.INTEGER, Types.INTEGER, Types.TIMESTAMP, Types.VARCHAR }); + } + } catch (Exception e) { + e.printStackTrace(); + _logger.error(e.getMessage()); + } + } + + /** + * if login password is error ,BadPasswordCount++ and set bad date + * + * @param userInfo + */ + public void setBadPasswordCount(UserInfo userInfo) { + try { + if (userInfo != null && StringUtils.isNotEmpty(userInfo.getId())) { + int badPasswordCount = userInfo.getBadPasswordCount() + 1; + userInfo.setBadPasswordCount(badPasswordCount); + jdbcTemplate.update(BADPASSWORDCOUNT_UPDATE_STATEMENT, + new Object[] { badPasswordCount, new Date(), userInfo.getId() }, + new int[] { Types.INTEGER, Types.TIMESTAMP, Types.VARCHAR }); + + } + } catch (Exception e) { + e.printStackTrace(); + _logger.error(e.getMessage()); + } + } + + + + public void setPasswordPolicy(PasswordPolicy passwordPolicy) { + this.passwordPolicy = passwordPolicy; + } + + + +} diff --git a/maxkey-core/src/main/resources/org/maxkey/persistence/db/top_weak_password.txt b/maxkey-core/src/main/resources/org/maxkey/persistence/db/top_weak_password.txt new file mode 100644 index 000000000..00abe43c4 --- /dev/null +++ b/maxkey-core/src/main/resources/org/maxkey/persistence/db/top_weak_password.txt @@ -0,0 +1,1100 @@ +123456 +password +12345678 +qwerty +123456789 +12345 +1234 +111111 +1234567 +dragon +123123 +baseball +abc123 +football +monkey +letmein +696969 +shadow +master +666666 +qwertyuiop +123321 +mustang +1234567890 +michael +654321 +pussy +superman +1qaz2wsx +7777777 +fuckyou +121212 +000000 +qazwsx +123qwe +killer +trustno1 +jordan +jennifer +zxcvbnm +asdfgh +hunter +buster +soccer +harley +batman +andrew +tigger +sunshine +iloveyou +fuckme +2000 +charlie +robert +thomas +hockey +ranger +daniel +starwars +klaster +112233 +george +asshole +computer +michelle +jessica +pepper +1111 +zxcvbn +555555 +11111111 +131313 +freedom +777777 +pass +fuck +maggie +159753 +aaaaaa +ginger +princess +joshua +cheese +amanda +summer +love +ashley +6969 +nicole +chelsea +biteme +matthew +access +yankees +987654321 +dallas +austin +thunder +taylor +matrix +william +corvette +hello +martin +heather +secret +fucker +merlin +diamond +1234qwer +gfhjkm +hammer +silver +222222 +88888888 +anthony +justin +test +bailey +q1w2e3r4t5 +patrick +internet +scooter +orange +11111 +golfer +cookie +richard +samantha +bigdog +guitar +jackson +whatever +mickey +chicken +sparky +snoopy +maverick +phoenix +camaro +sexy +peanut +morgan +welcome +falcon +cowboy +ferrari +samsung +andrea +smokey +steelers +joseph +mercedes +dakota +arsenal +eagles +melissa +boomer +booboo +spider +nascar +monster +tigers +yellow +xxxxxx +123123123 +gateway +marina +diablo +bulldog +qwer1234 +compaq +purple +hardcore +banana +junior +hannah +123654 +porsche +lakers +iceman +money +cowboys +987654 +london +tennis +999999 +ncc1701 +coffee +scooby +0000 +miller +boston +q1w2e3r4 +fuckoff +brandon +yamaha +chester +mother +forever +johnny +edward +333333 +oliver +redsox +player +nikita +knight +fender +barney +midnight +please +brandy +chicago +badboy +iwantu +slayer +rangers +charles +angel +flower +bigdaddy +rabbit +wizard +bigdick +jasper +enter +rachel +chris +steven +winner +adidas +victoria +natasha +1q2w3e4r +jasmine +winter +prince +panties +marine +ghbdtn +fishing +cocacola +casper +james +232323 +raiders +888888 +marlboro +gandalf +asdfasdf +crystal +87654321 +12344321 +sexsex +golden +blowme +bigtits +8675309 +panther +lauren +angela +bitch +spanky +thx1138 +angels +madison +winston +shannon +mike +toyota +blowjob +jordan23 +canada +sophie +Password +apples +dick +tiger +razz +123abc +pokemon +qazxsw +55555 +qwaszx +muffin +johnson +murphy +cooper +jonathan +liverpoo +david +danielle +159357 +jackie +1990 +123456a +789456 +turtle +horny +abcd1234 +scorpion +qazwsxedc +101010 +butter +carlos +password1 +dennis +slipknot +qwerty123 +booger +asdf +1991 +black +startrek +12341234 +cameron +newyork +rainbow +nathan +john +1992 +rocket +viking +redskins +butthead +asdfghjkl +1212 +sierra +peaches +gemini +doctor +wilson +sandra +helpme +qwertyui +victor +florida +dolphin +pookie +captain +tucker +blue +liverpool +theman +bandit +dolphins +maddog +packers +jaguar +lovers +nicholas +united +tiffany +maxwell +zzzzzz +nirvana +jeremy +suckit +stupid +porn +monica +elephant +giants +jackass +hotdog +rosebud +success +debbie +mountain +444444 +xxxxxxxx +warrior +1q2w3e4r5t +q1w2e3 +123456q +albert +metallic +lucky +azerty +7777 +shithead +alex +bond007 +alexis +1111111 +samson +5150 +willie +scorpio +bonnie +gators +benjamin +voodoo +driver +dexter +2112 +jason +calvin +freddy +212121 +creative +12345a +sydney +rush2112 +1989 +asdfghjk +red123 +bubba +4815162342 +passw0rd +trouble +gunner +happy +fucking +gordon +legend +jessie +stella +qwert +eminem +arthur +apple +nissan +bullshit +bear +america +1qazxsw2 +nothing +parker +4444 +rebecca +qweqwe +garfield +01012011 +beavis +69696969 +jack +asdasd +december +2222 +102030 +252525 +11223344 +magic +apollo +skippy +315475 +girls +kitten +golf +copper +braves +shelby +godzilla +beaver +fred +tomcat +august +buddy +airborne +1993 +1988 +lifehack +qqqqqq +brooklyn +animal +platinum +phantom +online +xavier +darkness +blink182 +power +fish +green +789456123 +voyager +police +travis +12qwaszx +heaven +snowball +lover +abcdef +00000 +pakistan +007007 +walter +playboy +blazer +cricket +sniper +hooters +donkey +willow +loveme +saturn +therock +redwings +bigboy +pumpkin +trinity +williams +tits +nintendo +digital +destiny +topgun +runner +marvin +guinness +chance +bubbles +testing +fire +november +minecraft +asdf1234 +lasvegas +sergey +broncos +cartman +private +celtic +birdie +little +cassie +babygirl +donald +beatles +1313 +dickhead +family +12121212 +school +louise +gabriel +eclipse +fluffy +147258369 +lol123 +explorer +beer +nelson +flyers +spencer +scott +lovely +gibson +doggie +cherry +andrey +snickers +buffalo +pantera +metallica +member +carter +qwertyu +peter +alexande +steve +bronco +paradise +goober +5555 +samuel +montana +mexico +dreams +michigan +cock +carolina +yankee +friends +magnum +surfer +poopoo +maximus +genius +cool +vampire +lacrosse +asd123 +aaaa +christin +kimberly +speedy +sharon +carmen +111222 +kristina +sammy +racing +ou812 +sabrina +horses +0987654321 +qwerty1 +pimpin +baby +stalker +enigma +147147 +star +poohbear +boobies +147258 +simple +bollocks +12345q +marcus +brian +1987 +qweasdzxc +drowssap +hahaha +caroline +barbara +dave +viper +drummer +action +einstein +bitches +genesis +hello1 +scotty +friend +forest +010203 +hotrod +google +vanessa +spitfire +badger +maryjane +friday +alaska +1232323q +tester +jester +jake +champion +billy +147852 +rock +hawaii +badass +chevy +420420 +walker +stephen +eagle1 +bill +1986 +october +gregory +svetlana +pamela +1984 +music +shorty +westside +stanley +diesel +courtney +242424 +kevin +porno +hitman +boobs +mark +12345qwert +reddog +frank +qwe123 +popcorn +patricia +aaaaaaaa +1969 +teresa +mozart +buddha +anderson +paul +melanie +abcdefg +security +lucky1 +lizard +denise +3333 +a12345 +123789 +ruslan +stargate +simpsons +scarface +eagle +123456789a +thumper +olivia +naruto +1234554321 +general +cherokee +a123456 +vincent +Usuckballz1 +spooky +qweasd +cumshot +free +frankie +douglas +death +1980 +loveyou +kitty +kelly +veronica +suzuki +semperfi +penguin +mercury +liberty +spirit +scotland +natalie +marley +vikings +system +sucker +king +allison +marshall +1979 +098765 +qwerty12 +hummer +adrian +1985 +vfhbyf +sandman +rocky +leslie +antonio +98765432 +4321 +softball +passion +mnbvcxz +bastard +passport +horney +rascal +howard +franklin +bigred +assman +alexander +homer +redrum +jupiter +claudia +55555555 +141414 +zaq12wsx +shit +patches +nigger +cunt +raider +infinity +andre +54321 +galore +college +russia +kawasaki +bishop +77777777 +vladimir +money1 +freeuser +wildcats +francis +disney +budlight +brittany +1994 +00000000 +sweet +oksana +honda +domino +bulldogs +brutus +swordfis +norman +monday +jimmy +ironman +ford +fantasy +9999 +7654321 +PASSWORD +hentai +duncan +cougar +1977 +jeffrey +house +dancer +brooke +timothy +super +marines +justice +digger +connor +patriots +karina +202020 +molly +everton +tinker +alicia +rasdzv3 +poop +pearljam +stinky +naughty +colorado +123123a +water +test123 +ncc1701d +motorola +ireland +asdfg +slut +matt +houston +boogie +zombie +accord +vision +bradley +reggie +kermit +froggy +ducati +avalon +6666 +9379992 +sarah +saints +logitech +chopper +852456 +simpson +madonna +juventus +claire +159951 +zachary +yfnfif +wolverin +warcraft +hello123 +extreme +penis +peekaboo +fireman +eugene +brenda +123654789 +russell +panthers +georgia +smith +skyline +jesus +elizabet +spiderma +smooth +pirate +empire +bullet +8888 +virginia +valentin +psycho +predator +arizona +134679 +mitchell +alyssa +vegeta +titanic +christ +goblue +fylhtq +wolf +mmmmmm +kirill +indian +hiphop +baxter +awesome +people +danger +roland +mookie +741852963 +1111111111 +dreamer +bambam +arnold +1981 +skipper +serega +rolltide +elvis +changeme +simon +1q2w3e +lovelove +fktrcfylh +denver +tommy +mine +loverboy +hobbes +happy1 +alison +nemesis +chevelle +cardinal +burton +wanker +picard +151515 +tweety +michael1 +147852369 +12312 +xxxx +windows +turkey +456789 +1974 +vfrcbv +sublime +1975 +galina +bobby +newport +manutd +daddy +american +alexandr +1966 +victory +rooster +qqq111 +madmax +electric +bigcock +a1b2c3 +wolfpack +spring +phpbb +lalala +suckme +spiderman +eric +darkside +classic +raptor +123456789q +hendrix +1982 +wombat +avatar +alpha +zxc123 +crazy +hard +england +brazil +1978 +01011980 +wildcat +polina +freepass +123456789 +a123456 +123456 +a123456789 +1234567890 +woaini1314 +qq123456 +abc123456 +123456a +123456789a +147258369 +zxcvbnm +987654321 +12345678910 +abc123 +qq123456789 +123456789. +7708801314520 +woaini +5201314520 +q123456 +123456abc +1233211234567 +123123123 +123456. +0123456789 +asd123456 +aa123456 +135792468 +q123456789 +abcd123456 +12345678900 +woaini520 +woaini123 +zxcvbnm123 +1111111111111111 +w123456 +aini1314 +abc123456789 +111111 +woaini521 +qwertyuiop +1314520520 +1234567891 +qwe123456 +asd123 +000000 +1472583690 +1357924680 +789456123 +123456789abc +z123456 +1234567899 +aaa123456 +abcd1234 +www123456 +123456789q +123abc +qwe123 +w123456789 +7894561230 +123456qq +zxc123456 +123456789qq +1111111111 +111111111 +0000000000000000 +1234567891234567 +qazwsxedc +qwerty +123456.. +zxc123 +asdfghjkl +0000000000 +1234554321 +123456q +123456aa +9876543210 +110120119 +qaz123456 +qq5201314 +123698745 +5201314 +000000000 +as123456 +123123 +5841314520 +z123456789 +52013145201314 +a123123 +caonima +a5201314 +wang123456 +abcd123 +123456789.. +woaini1314520 +123456asd +aa123456789 +741852963 +a12345678 \ No newline at end of file diff --git a/maxkey-core/src/test/java/org/maxkey/crypto/PasswordGenTest.java b/maxkey-core/src/test/java/org/maxkey/crypto/PasswordGenTest.java index 384eebd18..4809a32bc 100644 --- a/maxkey-core/src/test/java/org/maxkey/crypto/PasswordGenTest.java +++ b/maxkey-core/src/test/java/org/maxkey/crypto/PasswordGenTest.java @@ -28,10 +28,11 @@ public class PasswordGenTest { public static void main(String[] args) { // TODO Auto-generated method stub PasswordGen gen=new PasswordGen(); + System.out.println(gen.gen(2,2,2,1)); for(int i=1;i<100;i++){ - System.out.println(gen.gen()); - System.out.println(gen.gen(6)); - System.out.println(gen.gen(2,2,2,2)); + //System.out.println(gen.gen()); + //System.out.println(gen.gen(6)); + //System.out.println(gen.gen(2,2,2,0)); } } diff --git a/maxkey-core/src/test/java/org/maxkey/crypto/password/PasswordPolicyValidatorTest.java b/maxkey-core/src/test/java/org/maxkey/crypto/password/PasswordPolicyValidatorTest.java new file mode 100644 index 000000000..630e8ed3c --- /dev/null +++ b/maxkey-core/src/test/java/org/maxkey/crypto/password/PasswordPolicyValidatorTest.java @@ -0,0 +1,31 @@ +package org.maxkey.crypto.password; + +import org.maxkey.domain.PasswordPolicy; +import org.maxkey.domain.UserInfo; +import org.maxkey.persistence.db.PasswordPolicyValidator; + +public class PasswordPolicyValidatorTest { + + public static void main(String[] args) { + // TODO Auto-generated method stub + PasswordPolicy passwordPolicy =new PasswordPolicy(); + passwordPolicy.setDigits(3); + passwordPolicy.setMaxLength(16); + passwordPolicy.setMinLength(6); + passwordPolicy.setLowerCase(2); + passwordPolicy.setUpperCase(2); + passwordPolicy.setSpecialChar(1); + passwordPolicy.setUsername(1); + passwordPolicy.setSimplePasswords("admin,1qaz,2wsx,123456,12345678,1234567890"); + PasswordPolicyValidator passwordPolicyValidator =new PasswordPolicyValidator(); + + passwordPolicyValidator.setPasswordPolicy(passwordPolicy); + + UserInfo u=new UserInfo(); + u.setUsername("admin"); + u.setPassword("admin无"); + passwordPolicyValidator.validator(u); + + } + +} diff --git a/maxkey-core/src/test/resources/log4j2.xml b/maxkey-core/src/test/resources/log4j2.xml new file mode 100644 index 000000000..dfb480698 --- /dev/null +++ b/maxkey-core/src/test/resources/log4j2.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/maxkey-persistence/src/main/java/org/maxkey/persistence/service/UserInfoService.java b/maxkey-persistence/src/main/java/org/maxkey/persistence/service/UserInfoService.java index 0b7739ba3..eff1e918f 100644 --- a/maxkey-persistence/src/main/java/org/maxkey/persistence/service/UserInfoService.java +++ b/maxkey-persistence/src/main/java/org/maxkey/persistence/service/UserInfoService.java @@ -27,6 +27,7 @@ import org.maxkey.domain.UserInfo; import org.maxkey.identity.kafka.KafkaIdentityAction; import org.maxkey.identity.kafka.KafkaIdentityTopic; import org.maxkey.identity.kafka.KafkaProvisioningService; +import org.maxkey.persistence.db.PasswordPolicyValidator; import org.maxkey.persistence.mapper.UserInfoMapper; import org.maxkey.util.DateUtils; import org.maxkey.util.StringUtils; @@ -49,6 +50,9 @@ public class UserInfoService extends JpaBaseService { @Autowired private PasswordEncoder passwordEncoder; + @Autowired + PasswordPolicyValidator passwordPolicyValidator; + @Autowired KafkaProvisioningService kafkaProvisioningService; @@ -153,8 +157,13 @@ public class UserInfoService extends JpaBaseService { } return userInfo; } + + public boolean changePassword(UserInfo userInfo) { try { + + passwordPolicyValidator.validator(userInfo); + if(WebContext.getUserInfo() != null) { userInfo.setModifiedBy(WebContext.getUserInfo().getId()); @@ -277,4 +286,8 @@ public class UserInfoService extends JpaBaseService { return getMapper().updateProfile(userInfo); } + public void setPasswordPolicyValidator(PasswordPolicyValidator passwordPolicyValidator) { + this.passwordPolicyValidator = passwordPolicyValidator; + } + } diff --git a/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/SafeController.java b/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/SafeController.java index 8f32475dd..c6fa9fca1 100644 --- a/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/SafeController.java +++ b/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/SafeController.java @@ -118,7 +118,7 @@ public class SafeController { _logger.debug("decipherable new : "+ReciprocalUtils.encode(PasswordReciprocal.getInstance().rawPassword(userInfo.getUsername(), newPassword))); if(newPassword.equals(confirmPassword)){ if(oldPassword==null || - passwordEncoder.matches(PasswordReciprocal.getInstance().rawPassword(userInfo.getUsername(),oldPassword), userInfo.getPassword())){ + passwordEncoder.matches(oldPassword, userInfo.getPassword())){ userInfo.setPassword(newPassword); userInfoService.changePassword(userInfo); //TODO syncProvisioningService.changePassword(userInfo);