增加登录失败锁定
This commit is contained in:
parent
b245868de2
commit
b3bc28771a
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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>
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -215,6 +215,8 @@ public interface IUserService extends IUserBaseService, IUserCheckService {
|
||||
*/
|
||||
void updateExpiredDate(String userId, UpdateExpiredDateVO updateExpiredDateVO);
|
||||
|
||||
void updateUserState(String userId, UserStateEnum userStateEnum);
|
||||
|
||||
/**
|
||||
* 更新用户状态通过
|
||||
*
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user