增加登录失败锁定

This commit is contained in:
TS-QD1 2024-01-05 12:01:11 +08:00
parent b245868de2
commit b3bc28771a
11 changed files with 319 additions and 0 deletions

View File

@ -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;
}
}

View File

@ -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<String, Object> params);
void delete(String username);
Integer count(Map<String, Object> params);
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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<DefaultSecurit
private ILoginHandlerService loginHandler;
private HttpSession httpSession;
private IMongoLoginUserService mongoLoginUserService;
private LoginFailureLogService loginFailureLogService;
@Override
public void configure(HttpSecurity http) throws Exception {
@ -52,6 +54,7 @@ public class UserSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurit
userAuthenticationProvider.setHttpSession(httpSession);
userAuthenticationProvider.setLoginHandler(loginHandler);
userAuthenticationProvider.setMongoLoginUserService(mongoLoginUserService);
userAuthenticationProvider.setLoginFailureLogService(loginFailureLogService);
// 加入SpringSecurity的authentication管理的provider集合当中并且添加到UsernamePasswordAuthenticationFilter之前
http.authenticationProvider(userAuthenticationProvider).addFilterBefore(userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
@ -88,4 +91,8 @@ public class UserSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurit
public void setMongoLoginUserService(IMongoLoginUserService mongoLoginUserService) {
this.mongoLoginUserService = mongoLoginUserService;
}
public void setLoginFailureLogService(LoginFailureLogService loginFailureLogService) {
this.loginFailureLogService = loginFailureLogService;
}
}

View File

@ -0,0 +1,40 @@
package ink.wgink.login.base.service.log;
import ink.wgink.common.base.DefaultBaseService;
import ink.wgink.login.base.dao.log.ILoginFailureLogDao;
import ink.wgink.util.date.DateUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class LoginFailureLogService extends DefaultBaseService {
@Autowired
private ILoginFailureLogDao loginFailureLogDao;
public Integer saveAndReturnCount(String username) {
save(username);
return countByUsername(username);
}
public void delete(String username) {
loginFailureLogDao.delete(username);
}
private void save(String username) {
Map<String, Object> params = getHashMap(2);
params.put("username", username);
params.put("gmtCreate", DateUtil.getTime());
loginFailureLogDao.save(params);
}
private Integer countByUsername(String username) {
Map<String, Object> params = getHashMap(1);
params.put("username", username);
Integer count = loginFailureLogDao.count(params);
return count == null ? 0 : count;
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="ink.wgink.login.base.dao.log.ILoginFailureLogDao">
<resultMap id="loginFailureLogDTO" type="ink.wgink.login.base.pojo.dtos.log.LoginFailureLogDTO">
<result column="id" property="id"/>
<result column="username" property="username"/>
<result column="gmt_create" property="gmtCreate"/>
</resultMap>
<insert id="save" parameterType="map">
INSERT INTO log_login_failure_log (
username,
gmt_create
) VALUES(
#{username},
#{gmtCreate}
)
</insert>
<delete id="delete" parameterType="java.lang.String">
DELETE FROM log_login_failure_log WHERE username = #{_parameter}
</delete>
<select id="count" parameterType="map" resultType="java.lang.Integer">
SELECT
count(*)
FROM
log_login_failure_log
<where>
<if test="username!= null and username!= ''">
AND username = #{username}
</if>
</where>
</select>
</mapper>

View File

@ -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 += '<div class="item' + (isActive ? ' active' : '') + '" data-user-id="' + user.userId + '" data-user-username="' + user.userUsername + '" data-user-name="' + user.userName + '">';
itemHtml += '<div class="avatar">';
if (user.avatar) {
itemHtml += '<img src="route/file/download/true/' + user.avatar + '"/>';
} else {
itemHtml += '<img src="assets/images/profile-photo.jpg"/>';
}
itemHtml += '</div>';
itemHtml += '<div class="info">';
itemHtml += '<div class="text">' + user.userUsername + '</div>';
itemHtml += '<div class="text">' + user.userName + '</div>';
itemHtml += '</div>';
itemHtml += '<div class="checkbox">';
itemHtml += '<i class="fa fa-check-square-o"></i>';
itemHtml += '</div>';
itemHtml += '</div>';
}
var layIndex = layer.open({
type: 1,
area: ['300px', '400px'],
closeBtn: 0,
title: '选择候选人',
shadeClose: false,
scrollbar: false,
content: '<div class="select-user">' +
'<div class="list">'
+ itemHtml +
'</div>' +
'<div class="foot">' +
'<div class="layui-btn-group">' +
'<button type="button" class="layui-btn layui-btn-sm confirm-btn">确定</button>' +
'<button type="button" class="layui-btn layui-btn-sm layui-btn-primary close-btn">关闭</button>' +
'</div>' +
'</div>' +
'</div>',
end: function () {
onConfirm(selectedUsers);
}
});
addClick();
}
}

View File

@ -215,6 +215,8 @@ public interface IUserService extends IUserBaseService, IUserCheckService {
*/
void updateExpiredDate(String userId, UpdateExpiredDateVO updateExpiredDateVO);
void updateUserState(String userId, UserStateEnum userStateEnum);
/**
* 更新用户状态通过
*

View File

@ -378,6 +378,16 @@ public class UserServiceImpl extends DefaultBaseService implements IUserService
updateMongoLoginUser(userId);
}
@Override
public void updateUserState(String userId, UserStateEnum userStateEnum) {
Map<String, Object> params = getHashMap(4);
params.put("userId", userId);
params.put("userState", userStateEnum.getValue());
userDao.updateUserState(params);
updateMongoLoginUser(userId);
}
@Override
public void updateUserStatePass(String userId) {
Map<String, Object> params = getHashMap(4);