mirror of
http://git.pig4cloud.com/pig/pigx.git
synced 2026-11-08 12:39:05 +08:00
✨ Introducing new features. close #3794 【新需求】登录错误次数限制,连续登录失败锁定账号
This commit is contained in:
1 parent
99fe51e6aa
commit
f669d1b968
11 files changed
+214
-12
No files matched your search
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.auth.handler;
|
||||
|
||||
import com.pig4cloud.pigx.admin.api.feign.RemoteUserService;
|
||||
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.KeyStrResolver;
|
||||
import com.pig4cloud.pigx.common.data.resolver.ParamResolver;
|
||||
import com.pig4cloud.pigx.common.security.handler.AuthenticationFailureHandler;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2022-04-11
|
||||
*
|
||||
* 登录操作次数锁定功能
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class PigxAuthenticationFailureLockEventHandler implements AuthenticationFailureHandler {
|
||||
|
||||
private final RedisTemplate<String, String> redisTemplate;
|
||||
|
||||
private final KeyStrResolver tenantKeyStrResolver;
|
||||
|
||||
private final RemoteUserService userService;
|
||||
|
||||
/**
|
||||
* 密码错误超过 5 此自动锁定
|
||||
* <p>
|
||||
* @param authenticationException 登录的authentication 对象
|
||||
* @param authentication 登录的authenticationException 对象
|
||||
* @param request 请求
|
||||
* @param response 响应
|
||||
*/
|
||||
@Async
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void handle(AuthenticationException authenticationException, Authentication authentication,
|
||||
HttpServletRequest request, HttpServletResponse response) {
|
||||
// 只处理账号密码错误异常
|
||||
if (!(authenticationException instanceof BadCredentialsException)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String username = authentication.getName();
|
||||
String key = String.format("%s:%s:%s", CacheConstants.LOGIN_ERROR_TIMES, tenantKeyStrResolver.key(), username);
|
||||
Long deltaTimes = ParamResolver.getLong("LOGIN_ERROR_TIMES", 5L);
|
||||
Long times = redisTemplate.opsForValue().increment(key);
|
||||
|
||||
// 自动过期时间
|
||||
Long deltaTime = ParamResolver.getLong("DELTA_TIME", 1L);
|
||||
redisTemplate.expire(key, deltaTime, TimeUnit.HOURS);
|
||||
|
||||
if (deltaTimes <= times) {
|
||||
userService.lockUser(username, SecurityConstants.FROM_IN);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -44,7 +44,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
@Slf4j
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class PigxAuthenticationFailureEventHandler implements AuthenticationFailureHandler {
|
||||
public class PigxAuthenticationFailureLogEventHandler implements AuthenticationFailureHandler {
|
||||
|
||||
private final RemoteLogService logService;
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.auth.handler;
|
||||
|
||||
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.KeyStrResolver;
|
||||
import com.pig4cloud.pigx.common.security.handler.AuthenticationSuccessHandler;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2022-04-11
|
||||
*
|
||||
* 登录操作次数锁定清楚功能
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class PigxAuthenticationSuccessLockEventHandler implements AuthenticationSuccessHandler {
|
||||
|
||||
private final RedisTemplate<String, String> redisTemplate;
|
||||
|
||||
private final KeyStrResolver tenantKeyStrResolver;
|
||||
|
||||
/**
|
||||
* 处理登录成功方法
|
||||
* <p>
|
||||
* 获取到登录的authentication 对象
|
||||
* @param authentication 登录对象
|
||||
* @param request 请求
|
||||
* @param response 返回
|
||||
*/
|
||||
@Async
|
||||
@Override
|
||||
public void handle(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {
|
||||
String username = authentication.getName();
|
||||
String key = String.format("%s:%s:%s", CacheConstants.LOGIN_ERROR_TIMES, tenantKeyStrResolver.key(), username);
|
||||
redisTemplate.delete(key);
|
||||
}
|
||||
|
||||
}
|
||||
+4
-2
@@ -36,12 +36,14 @@ import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2018/10/8
|
||||
* @date 2022-04-11
|
||||
*
|
||||
* 登录成功日志记录
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class PigxAuthenticationSuccessEventHandler implements AuthenticationSuccessHandler {
|
||||
public class PigxAuthenticationSuccessLogEventHandler implements AuthenticationSuccessHandler {
|
||||
|
||||
private final RemoteLogService logService;
|
||||
|
||||
+5
@@ -95,4 +95,9 @@ public interface CacheConstants {
|
||||
*/
|
||||
String CLIENT_FLAG = "client_config_flag";
|
||||
|
||||
/**
|
||||
* 登录错误次数
|
||||
*/
|
||||
String LOGIN_ERROR_TIMES = "login_error_times";
|
||||
|
||||
}
|
||||
+5
-5
@@ -29,6 +29,7 @@ import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
@@ -37,7 +38,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
public class AuthenticationFailureEventListener implements ApplicationListener<AbstractAuthenticationFailureEvent> {
|
||||
|
||||
@Autowired(required = false)
|
||||
private AuthenticationFailureHandler failureHandler;
|
||||
private List<AuthenticationFailureHandler> failureHandlerList;
|
||||
|
||||
/**
|
||||
* Handle an application event.
|
||||
@@ -58,10 +59,9 @@ public class AuthenticationFailureEventListener implements ApplicationListener<A
|
||||
AuthenticationException authenticationException = event.getException();
|
||||
Authentication authentication = (Authentication) event.getSource();
|
||||
|
||||
// 调用自定义业务实现
|
||||
if (failureHandler != null) {
|
||||
failureHandler.handle(authenticationException, authentication, request, response);
|
||||
}
|
||||
// 调用自定义业务链实现
|
||||
failureHandlerList.forEach(
|
||||
failureHandler -> failureHandler.handle(authenticationException, authentication, request, response));
|
||||
}
|
||||
|
||||
}
|
||||
+4
-4
@@ -29,6 +29,7 @@ import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
@@ -37,7 +38,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
public class AuthenticationSuccessEventListener implements ApplicationListener<AuthenticationSuccessEvent> {
|
||||
|
||||
@Autowired(required = false)
|
||||
private AuthenticationSuccessHandler successHandler;
|
||||
private List<AuthenticationSuccessHandler> successHandlerList;
|
||||
|
||||
/**
|
||||
* Handle an application event.
|
||||
@@ -46,13 +47,12 @@ public class AuthenticationSuccessEventListener implements ApplicationListener<A
|
||||
@Override
|
||||
public void onApplicationEvent(AuthenticationSuccessEvent event) {
|
||||
Authentication authentication = (Authentication) event.getSource();
|
||||
if (successHandler != null && isUserAuthentication(authentication)) {
|
||||
if (isUserAuthentication(authentication)) {
|
||||
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
|
||||
.getRequestAttributes();
|
||||
HttpServletRequest request = requestAttributes.getRequest();
|
||||
HttpServletResponse response = requestAttributes.getResponse();
|
||||
|
||||
successHandler.handle(authentication, request, response);
|
||||
successHandlerList.forEach(successHandler -> successHandler.handle(authentication, request, response));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
@@ -27,6 +27,7 @@ import com.pig4cloud.pigx.common.core.util.R;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
|
||||
import java.util.List;
|
||||
@@ -64,4 +65,13 @@ public interface RemoteUserService {
|
||||
@GetMapping("/user/ancestor/{username}")
|
||||
R<List<SysUser>> ancestorUsers(@PathVariable("username") String username);
|
||||
|
||||
/**
|
||||
* 锁定用户
|
||||
* @param username 用户名
|
||||
* @param from 调用标识
|
||||
* @return
|
||||
*/
|
||||
@PutMapping("/user/lock/{username}")
|
||||
R<Boolean> lockUser(@PathVariable("username") String username, @RequestHeader(SecurityConstants.FROM) String from);
|
||||
|
||||
}
|
||||
+11
@@ -201,4 +201,15 @@ public class SysUserController {
|
||||
return userService.importUser(excelVOList, bindingResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 锁定指定用户
|
||||
* @param username 用户名
|
||||
* @return R
|
||||
*/
|
||||
@Inner
|
||||
@PutMapping("/lock/{username}")
|
||||
public R lockUser(@PathVariable String username) {
|
||||
return userService.lockUser(username);
|
||||
}
|
||||
|
||||
}
|
||||
+7
@@ -117,4 +117,11 @@ public interface SysUserService extends IService<SysUser> {
|
||||
*/
|
||||
R<Boolean> registerUser(UserDTO userDto);
|
||||
|
||||
/**
|
||||
* 锁定用户
|
||||
* @param username
|
||||
* @return
|
||||
*/
|
||||
R<Boolean> lockUser(String username);
|
||||
|
||||
}
|
||||
+14
@@ -370,4 +370,18 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
return R.ok(saveUser(userDto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 锁定用户
|
||||
* @param username 用户名
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
@CacheEvict(value = CacheConstants.USER_DETAILS, key = "#username")
|
||||
public R<Boolean> lockUser(String username) {
|
||||
SysUser sysUser = baseMapper.selectOne(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getUsername, username));
|
||||
sysUser.setLockFlag(CommonConstants.STATUS_LOCK);
|
||||
baseMapper.updateById(sysUser);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user