From b3bc28771a8a2c4bb8c48a8740468e669c0d0e1f Mon Sep 17 00:00:00 2001 From: TS-QD1 Date: Fri, 5 Jan 2024 12:01:11 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=99=BB=E5=BD=95=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E9=94=81=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/UserAuthenticationProvider.java | 34 +++++ .../base/dao/log/ILoginFailureLogDao.java | 17 +++ .../pojo/dtos/log/LoginFailureLogDTO.java | 35 +++++ .../base/security/WebSecurityConfig.java | 4 + .../security/user/UserSecurityConfig.java | 7 + .../service/log/LoginFailureLogService.java | 40 ++++++ .../service/user/UserDetailServiceImpl.java | 4 + .../mapper/log/login-failure-log-mapper.xml | 37 +++++ .../resources/static/form/js/form-util.js | 129 ++++++++++++++++++ .../service/user/service/IUserService.java | 2 + .../user/service/impl/UserServiceImpl.java | 10 ++ 11 files changed, 319 insertions(+) create mode 100644 login-base/src/main/java/ink/wgink/login/base/dao/log/ILoginFailureLogDao.java create mode 100644 login-base/src/main/java/ink/wgink/login/base/pojo/dtos/log/LoginFailureLogDTO.java create mode 100644 login-base/src/main/java/ink/wgink/login/base/service/log/LoginFailureLogService.java create mode 100644 login-base/src/main/resources/mybatis/mapper/log/login-failure-log-mapper.xml diff --git a/login-base/src/main/java/ink/wgink/login/base/authentication/user/UserAuthenticationProvider.java b/login-base/src/main/java/ink/wgink/login/base/authentication/user/UserAuthenticationProvider.java index ece842a2..9032cf6c 100644 --- a/login-base/src/main/java/ink/wgink/login/base/authentication/user/UserAuthenticationProvider.java +++ b/login-base/src/main/java/ink/wgink/login/base/authentication/user/UserAuthenticationProvider.java @@ -1,14 +1,20 @@ package ink.wgink.login.base.authentication.user; +import ink.wgink.interfaces.consts.ISystemConstant; import ink.wgink.interfaces.expand.login.ILoginHandlerService; import ink.wgink.interfaces.user.mongo.IMongoLoginUserService; import ink.wgink.login.base.consts.IUserCenterConst; import ink.wgink.login.base.enums.LoginTypeEnum; import ink.wgink.login.base.exceptions.UserAuthenticationException; +import ink.wgink.login.base.service.log.LoginFailureLogService; +import ink.wgink.login.base.service.user.UserDetailServiceImpl; import ink.wgink.login.base.service.user.UserLoginService; import ink.wgink.pojo.bos.LoginUser; +import ink.wgink.service.user.enums.UserStateEnum; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; @@ -35,6 +41,7 @@ public class UserAuthenticationProvider implements AuthenticationProvider { private ILoginHandlerService loginHandler; private IMongoLoginUserService mongoLoginUserService; private HttpSession httpSession; + private LoginFailureLogService loginFailureLogService; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { @@ -53,8 +60,11 @@ public class UserAuthenticationProvider implements AuthenticationProvider { userLoginService.setLoginUserInfo(loginUser); } if (!passwordEncoder.matches(userAuthenticationToken.getCredentials().toString(), loginUser.getPassword())) { + loginFailure(username, loginUser.getUserId()); throw new UserAuthenticationException("用户名或密码错误"); } + // 密码正确,删除错误日志 + loginFailureLogService.delete(username); if (loginUser.getAuthorities().isEmpty()) { throw new UserAuthenticationException(loginUser.getUsername() + "用户无任何角色"); } @@ -70,6 +80,26 @@ public class UserAuthenticationProvider implements AuthenticationProvider { return userAuthenticationTokenResult; } + /** + * 登录失败 + * + * @param username + * @param userId + */ + private void loginFailure(String username, String userId) { + // admin登录失败冻结 + if (StringUtils.equals(ISystemConstant.ADMIN, username)) { + return; + } + Integer count = loginFailureLogService.saveAndReturnCount(username); + // 错误超过三次锁定 + if (count % 3 != 0) { + return; + } + UserDetailServiceImpl userDetailService = (UserDetailServiceImpl) userDetailsService; + userDetailService.updateUserState(userId, UserStateEnum.LOCK); + } + /** * 登录处理 @@ -115,4 +145,8 @@ public class UserAuthenticationProvider implements AuthenticationProvider { public void setMongoLoginUserService(IMongoLoginUserService mongoLoginUserService) { this.mongoLoginUserService = mongoLoginUserService; } + + public void setLoginFailureLogService(LoginFailureLogService loginFailureLogService) { + this.loginFailureLogService = loginFailureLogService; + } } diff --git a/login-base/src/main/java/ink/wgink/login/base/dao/log/ILoginFailureLogDao.java b/login-base/src/main/java/ink/wgink/login/base/dao/log/ILoginFailureLogDao.java new file mode 100644 index 00000000..1c845766 --- /dev/null +++ b/login-base/src/main/java/ink/wgink/login/base/dao/log/ILoginFailureLogDao.java @@ -0,0 +1,17 @@ +package ink.wgink.login.base.dao.log; + +import org.springframework.stereotype.Repository; + +import java.util.Map; + +@Repository +public interface ILoginFailureLogDao { + + void save(Map params); + + void delete(String username); + + Integer count(Map params); + + +} diff --git a/login-base/src/main/java/ink/wgink/login/base/pojo/dtos/log/LoginFailureLogDTO.java b/login-base/src/main/java/ink/wgink/login/base/pojo/dtos/log/LoginFailureLogDTO.java new file mode 100644 index 00000000..654adb14 --- /dev/null +++ b/login-base/src/main/java/ink/wgink/login/base/pojo/dtos/log/LoginFailureLogDTO.java @@ -0,0 +1,35 @@ +package ink.wgink.login.base.pojo.dtos.log; + +import java.io.Serializable; + +public class LoginFailureLogDTO implements Serializable { + + private Long id; + private String username; + private String gmtCreate; + + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getGmtCreate() { + return gmtCreate; + } + + public void setGmtCreate(String gmtCreate) { + this.gmtCreate = gmtCreate; + } +} diff --git a/login-base/src/main/java/ink/wgink/login/base/security/WebSecurityConfig.java b/login-base/src/main/java/ink/wgink/login/base/security/WebSecurityConfig.java index 2534675b..609fb6a7 100644 --- a/login-base/src/main/java/ink/wgink/login/base/security/WebSecurityConfig.java +++ b/login-base/src/main/java/ink/wgink/login/base/security/WebSecurityConfig.java @@ -7,6 +7,7 @@ import ink.wgink.interfaces.user.mongo.IMongoLoginUserService; import ink.wgink.login.base.handler.LoginFailureHandler; import ink.wgink.login.base.handler.LogoutHandler; import ink.wgink.login.base.security.user.UserSecurityConfig; +import ink.wgink.login.base.service.log.LoginFailureLogService; import ink.wgink.login.base.service.user.UserDetailServiceImpl; import ink.wgink.login.base.service.user.UserLoginService; import ink.wgink.properties.BaseProperties; @@ -51,6 +52,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private IMongoLoginUserService mongoLoginUserService; @Autowired private ApplicationContext applicationContext; + @Autowired + private LoginFailureLogService loginFailureLogService; @Override protected void configure(HttpSecurity http) throws Exception { @@ -111,6 +114,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { userSecurityConfig.setLoginHandler(loginHandler); userSecurityConfig.setHttpSession(httpSession); userSecurityConfig.setMongoLoginUserService(mongoLoginUserService); + userSecurityConfig.setLoginFailureLogService(loginFailureLogService); http.apply(userSecurityConfig); } diff --git a/login-base/src/main/java/ink/wgink/login/base/security/user/UserSecurityConfig.java b/login-base/src/main/java/ink/wgink/login/base/security/user/UserSecurityConfig.java index 13ada07c..47332e81 100644 --- a/login-base/src/main/java/ink/wgink/login/base/security/user/UserSecurityConfig.java +++ b/login-base/src/main/java/ink/wgink/login/base/security/user/UserSecurityConfig.java @@ -5,6 +5,7 @@ import ink.wgink.interfaces.user.mongo.IMongoLoginUserService; import ink.wgink.login.base.authentication.user.UserAuthenticationFilter; import ink.wgink.login.base.authentication.user.UserAuthenticationProvider; import ink.wgink.login.base.handler.LoginFailureHandler; +import ink.wgink.login.base.service.log.LoginFailureLogService; import ink.wgink.login.base.service.user.UserDetailServiceImpl; import ink.wgink.login.base.service.user.UserLoginService; import org.springframework.security.authentication.AuthenticationManager; @@ -36,6 +37,7 @@ public class UserSecurityConfig extends SecurityConfigurerAdapter params = getHashMap(2); + params.put("username", username); + params.put("gmtCreate", DateUtil.getTime()); + loginFailureLogDao.save(params); + } + + private Integer countByUsername(String username) { + Map params = getHashMap(1); + params.put("username", username); + Integer count = loginFailureLogDao.count(params); + return count == null ? 0 : count; + } + +} diff --git a/login-base/src/main/java/ink/wgink/login/base/service/user/UserDetailServiceImpl.java b/login-base/src/main/java/ink/wgink/login/base/service/user/UserDetailServiceImpl.java index 537172ae..4a29198d 100644 --- a/login-base/src/main/java/ink/wgink/login/base/service/user/UserDetailServiceImpl.java +++ b/login-base/src/main/java/ink/wgink/login/base/service/user/UserDetailServiceImpl.java @@ -61,4 +61,8 @@ public class UserDetailServiceImpl implements UserDetailsService, IUserDetailChe return UserUtil.createLoginUser(userPO); } + public void updateUserState(String userId, UserStateEnum userStateEnum) { + userService.updateUserState(userId, userStateEnum); + } + } diff --git a/login-base/src/main/resources/mybatis/mapper/log/login-failure-log-mapper.xml b/login-base/src/main/resources/mybatis/mapper/log/login-failure-log-mapper.xml new file mode 100644 index 00000000..69a766dd --- /dev/null +++ b/login-base/src/main/resources/mybatis/mapper/log/login-failure-log-mapper.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + INSERT INTO log_login_failure_log ( + username, + gmt_create + ) VALUES( + #{username}, + #{gmtCreate} + ) + + + + DELETE FROM log_login_failure_log WHERE username = #{_parameter} + + + + + \ No newline at end of file diff --git a/module-form/src/main/resources/static/form/js/form-util.js b/module-form/src/main/resources/static/form/js/form-util.js index 2e822e4c..1d54fb7f 100644 --- a/module-form/src/main/resources/static/form/js/form-util.js +++ b/module-form/src/main/resources/static/form/js/form-util.js @@ -830,4 +830,133 @@ function FormUtil(layui, viewer) { }); } + /** + * @callback onConfirm + * @param selectedUsers {Array} [{userId: '', userName: '', userUsername: ''}] + */ + /** + * @description 选择用户列表 + * @param opt {object} + * @param {String} opt.selectType 'radio|checkbox' 必传,2选1 + * @param {onConfirm} opt.onConfirm 确定按钮回调函数,必传 + * @param {Array} opt.users 参与选择的用户列表,必传,格式:[{userId: '', userUsername: '', userName: ''}] + * @param {Array} opt.selectedUserIds 默认选中的用户ID列表,非必传,如果selectType为 radio,默认选中[0]。格式:['userId1', 'userId2'] + */ + this.selectUsers = function (opt) { + var selectType = opt.selectType, + onConfirm = opt.onConfirm, + users = opt.users, + selectedUserIds = opt.selectedUserIds; + if (selectType != 'radio' && selectType != 'checkbox') { + throw 'selectType(arg1): [radio|checkbox]'; + return; + } + if (!users && !(users instanceof Array)) { + onConfirm([]); + } + + selectedUserIds = selectedUserIds && (selectedUserIds instanceof Array) ? selectedUserIds : []; + selectedUserIds = selectType == 'radio' && selectedUserIds.length > 1 ? selectedUserIds[0] : selectedUserIds; + + var selectedUserMap = {}; + var selectedUsers = []; + var itemHtml = ''; + + function addUserToMap(userId, userUsername, userName) { + selectedUserMap[userId] = { + userId: userId, + userUsername: userUsername, + userName: userName, + } + } + + function addClick() { + $('.select-user .list .item').click(function () { + var dataset = this.dataset; + var userId = dataset.userId; + if (selectType === 'radio') { + $('.select-user .list .item').removeClass('active'); + $(this).addClass('active'); + selectedUserMap = {}; + addUserToMap(userId, dataset.userUsername, dataset.userName); + } else { + if (selectedUserMap[userId]) { + delete selectedUserMap[userId]; + $(this).removeClass('active'); + } else { + addUserToMap(userId, dataset.userUsername, dataset.userName); + $(this).addClass('active'); + } + } + }); + $('.select-user .foot .confirm-btn').click(function () { + for (var key in selectedUserMap) { + selectedUsers.push(selectedUserMap[key]); + } + if (selectedUsers.length == 0) { + layer.msg('请选择人员'); + return; + } + layer.close(layIndex); + }); + $('.select-user .foot .close-btn').click(function () { + selectedUsers = null; + layer.close(layIndex); + }); + } + + // 添加用户到map + for (var i = 0, user; user = users[i++];) { + var isActive = false; + for (var j = 0, selectedUserId; selectedUserId = selectedUserIds[j++];) { + if (user.userId === selectedUserId) { + isActive = true; + addUserToMap(user.userId, user.userUsername, user.userName); + break; + } + } + itemHtml += '
'; + itemHtml += '
'; + if (user.avatar) { + itemHtml += ''; + } else { + itemHtml += ''; + } + itemHtml += '
'; + itemHtml += '
'; + itemHtml += '
' + user.userUsername + '
'; + itemHtml += '
' + user.userName + '
'; + itemHtml += '
'; + itemHtml += '
'; + itemHtml += ''; + itemHtml += '
'; + itemHtml += '
'; + } + + var layIndex = layer.open({ + type: 1, + area: ['300px', '400px'], + closeBtn: 0, + title: '选择候选人', + shadeClose: false, + scrollbar: false, + content: '
' + + '
' + + itemHtml + + '
' + + '
' + + '
' + + '' + + '' + + '
' + + '
' + + '
', + end: function () { + onConfirm(selectedUsers); + } + }); + + addClick(); + } + } \ No newline at end of file diff --git a/service-user/src/main/java/ink/wgink/service/user/service/IUserService.java b/service-user/src/main/java/ink/wgink/service/user/service/IUserService.java index 9ef9cf68..cea3383c 100644 --- a/service-user/src/main/java/ink/wgink/service/user/service/IUserService.java +++ b/service-user/src/main/java/ink/wgink/service/user/service/IUserService.java @@ -215,6 +215,8 @@ public interface IUserService extends IUserBaseService, IUserCheckService { */ void updateExpiredDate(String userId, UpdateExpiredDateVO updateExpiredDateVO); + void updateUserState(String userId, UserStateEnum userStateEnum); + /** * 更新用户状态通过 * diff --git a/service-user/src/main/java/ink/wgink/service/user/service/impl/UserServiceImpl.java b/service-user/src/main/java/ink/wgink/service/user/service/impl/UserServiceImpl.java index daffe5c0..6cd8b296 100644 --- a/service-user/src/main/java/ink/wgink/service/user/service/impl/UserServiceImpl.java +++ b/service-user/src/main/java/ink/wgink/service/user/service/impl/UserServiceImpl.java @@ -378,6 +378,16 @@ public class UserServiceImpl extends DefaultBaseService implements IUserService updateMongoLoginUser(userId); } + @Override + public void updateUserState(String userId, UserStateEnum userStateEnum) { + Map params = getHashMap(4); + params.put("userId", userId); + params.put("userState", userStateEnum.getValue()); + userDao.updateUserState(params); + + updateMongoLoginUser(userId); + } + @Override public void updateUserStatePass(String userId) { Map params = getHashMap(4);