写在前面

现在很多互联网项目、app等都会有登录异常提醒、登录异常次数限制,基本都是5次异常后就会锁定一定时间的账户,让该账户无法进行登录操作。需要注册用户使用安全验证手段(如动态验证码等),解除锁定后才能进行新一轮登录操作。这么做无非就是两个目的:

1. 为了注册用户的账户安全;

2. 更多的是防止黑客攻击,维护网站等的安全。

项目需求

项目开发结束,进入测试阶段,很多项目或者开发就进入到了边测试边修改bug的阶段了。不过,我们公司的项目还需要进行安全测试,对不符合要求的项目需要进行安全隐患的整改。这不,我就“被迫”的研究起了如何避免被无差别攻击,防止通过无限攻击次数、穷举等破解密码的手段了。

整体思路

1. 验证及提示

获取并验证账号登录异常信息,提示登录异常。代码片段如下

        // 验证账户是否封锁String usercode = userLoginDto.getUsercode();ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();// 如果这个账号登录异常,则在登录页面提醒。String shiroLoginCount = opsForValue.get(SHIRO_LOGIN_COUNT + usercode);if (!StringUtils.isEmpty(shiroLoginCount) && Integer.parseInt(shiroLoginCount) >= 10) {if ("LOCK".equals(opsForValue.get(SHIRO_IS_LOCK + usercode))) {// 计数大于10次,设置用户被锁定5分钟String msg = "由于输入错误次数大于10次,帐号5分钟内已经禁止登录!";log.info(msg);return PlatformResult.failure(msg);}}

2. 计数

用户的每次异常登录(其实就是密码错误)时,将账户及错误次数记录到redis中。代码片段如下

            // 登录失败计数opsForValue.increment(SHIRO_LOGIN_COUNT + usercode, 1); // 每次增加1log.info(usercode + ":账号登录异常次数:" + opsForValue.get(SHIRO_LOGIN_COUNT + usercode));

3. 检查及限制

检查累积阈值错误次数(本系统设置为10次)时,锁定该用户,并限制一定时间内(本系统设置为5分钟)无法进行登录操作。代码片段如下

            // 登录异常次数超限实现锁定if (Integer.parseInt(opsForValue.get(SHIRO_LOGIN_COUNT + usercode)) >= 10) {opsForValue.set(SHIRO_IS_LOCK + usercode, "LOCK"); // 锁住这个账号,值是LOCK。stringRedisTemplate.expire(SHIRO_IS_LOCK + usercode, 5, TimeUnit.MINUTES); // expire 变量存活期限}

4. 解锁及清空

在错误10次(可根据业务需要进行调整)前登录成功,则解锁该用户异常状态,清空登录错误次数和解锁账户。代码片段如下

    /*** 清空登录计数* * @author: caip* @date: 2021-04-07 15:45:57* @param userName*/private void clearLoginCount(String userName) {log.info("UserLoginServiceImpl clearLoginCount start");ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();// 清空登录计数opsForValue.set(SHIRO_LOGIN_COUNT + userName, "0");// 清空锁opsForValue.set(SHIRO_IS_LOCK + userName, "");log.info("UserLoginServiceImpl clearLoginCount end");}

整体代码

1. 参数对象

用于前后交互传参

package cn.xxx.rdc.fi.dto;import lombok.Getter;
import lombok.Setter;/*** @author xxx* @date: 2021-02-25 10:54:02* @Copyright: Copyright (c) 2006 - 2021* @Company: 公司* @Version: V1.0*/
@Getter
@Setter
public class UserLoginDto {/** 账号. */private String usercode;/** 密码. */private String password;/** 记住密码. */private String remarkid;/** 用户类型. */private Integer userType;/** 微信userid. */private String wxuserid;/** 是否加密:1.是;0.否. */private String isEncryption;
}

2. 接口

定义交互标准

    @Autowiredprivate IUserLoginService userLoginService;@ApiOperation(value = "用户登录", notes = "用户登录")@PostMapping("/userLogin")public PlatformResult<JSONObject> userLogin(@RequestBody UserLoginDto userLoginDto, ServletResponse response) {return userLoginService.userLogin(userLoginDto, response);}

3. 服务定义

定义服务标准

    /*** 用户登录* * @author: xxx* @date: 2021-02-24 17:17:23* @param userLoginDto 用户登录对象* @return*/PlatformResult<JSONObject> userLogin(UserLoginDto userLoginDto, ServletResponse response);

4. 服务实现

定义服务具体实现

package cn.xxx.rdc.knowledge.service.impl;import java.util.concurrent.TimeUnit;import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSONObject;import cn.xxx.BootComm.Contants;
import cn.xxx.BootComm.utils.PlatformResult;
import cn.xxx.rdc.knowledge.dto.UserLoginDto;
import cn.xxx.rdc.knowledge.service.IUserService;
import cn.xxx.rdc.knowledge.utils.HttpClientUtil;
import cn.xxx.rdc.knowledge.utils.HttpRequestUtil;
import cn.xxx.rdc.knowledge.utils.RSAUtil;
import lombok.extern.slf4j.Slf4j;/*** 用户登录服务类* * @author xxx* @date: 2021-02-24 17:17:53* @Copyright: Copyright (c) 2006 - 2021* @Company: xxx* @Version: V1.0*/
@Slf4j
@Service
public class UserServiceImpl implements IUserService {@Value("${user.login.url}")private String userLoginUrl;@Value("${sso.verifyUrl}")private String ssoVerifyUrl;@Value("${sso.defaultPrikey}")private String ssoDefaultPrikey;@Value("${user.logout.url}")private String userLogoutUrl;@Autowiredprivate StringRedisTemplate stringRedisTemplate;// 用户登录次数计数 redisKey 前缀private final String SHIRO_LOGIN_COUNT = "kb_shiro_login_count_";// 用户登录是否被锁定 redisKey 前缀private final String SHIRO_IS_LOCK = "kb_shiro_is_lock_";@Overridepublic PlatformResult<JSONObject> userLogin(UserLoginDto userLoginDto, ServletResponse response) {log.info("UserLoginServiceImpl userLogin start");// 获取HttpServletRequest、HttpServletResponseHttpServletResponse res = (HttpServletResponse)response;// 验证账户是否封锁String usercode = userLoginDto.getUsercode();ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();// 如果这个账号登录异常,则在登录页面提醒。String shiroLoginCount = opsForValue.get(SHIRO_LOGIN_COUNT + usercode);if (!StringUtils.isEmpty(shiroLoginCount) && Integer.parseInt(shiroLoginCount) >= 10) {if ("LOCK".equals(opsForValue.get(SHIRO_IS_LOCK + usercode))) {// 计数大于10次,设置用户被锁定5分钟String msg = "由于输入错误次数大于10次,帐号5分钟内已经禁止登录!";log.info(msg);return PlatformResult.failure(msg);}}// 未封锁,则进行登录请求userLoginDto.setIsEncryption("1");String params = JSONObject.toJSONString(userLoginDto);log.info("userLogin urlStr=[" + userLoginUrl + "]");log.info("userLogin params=[" + params.toString() + "]");// 调用登录http请求String result = HttpRequestUtil.httpCrossDomain(userLoginUrl, params);// 获取登录返回参数JSONObject resultJson = JSONObject.parseObject(result);int statusCode = resultJson.getIntValue("statusCode");boolean success = resultJson.getBooleanValue("success");// 判断是否登录成功String loginMsg = resultJson.getString("message");if (statusCode != 200 || !success) {// 登录失败计数opsForValue.increment(SHIRO_LOGIN_COUNT + usercode, 1); // 每次增加1log.info(usercode + ":账号登录异常次数:" + opsForValue.get(SHIRO_LOGIN_COUNT + usercode));// 登录异常次数超限实现锁定if (Integer.parseInt(opsForValue.get(SHIRO_LOGIN_COUNT + usercode)) >= 10) {opsForValue.set(SHIRO_IS_LOCK + usercode, "LOCK"); // 锁住这个账号,值是LOCK。stringRedisTemplate.expire(SHIRO_IS_LOCK + usercode, 5, TimeUnit.MINUTES); // expire 变量存活期限}return PlatformResult.failure(loginMsg);}// 登录成功解锁this.clearLoginCount(usercode);// 查询本系统用户信息JSONObject userObject = resultJson.getJSONObject("object");// 判断是否免密登录String tokenEncryption = userObject.getString("token");log.info("tokenEncryption=" + tokenEncryption);// 判断是否调用免密登录if (!StringUtils.isEmpty(tokenEncryption) && 36 < tokenEncryption.length()) {// 通过加密token,解密后调用sso接口获取用户信息String token = RSAUtil.decrypt(ssoDefaultPrikey, tokenEncryption);log.info("token=" + token);// 调用登录http请求String userResult = HttpClientUtil.getInstance().sendHttpGet(ssoVerifyUrl + "?token=" + token);log.info("userResult=" + userResult);// 获取登录返回参数resultJson = JSONObject.parseObject(userResult);String code = resultJson.getString("code");// 判断是否登录成功loginMsg = resultJson.getString("msg");if (!"0".equals(code)) {return PlatformResult.failure(loginMsg);}userObject = resultJson.getJSONObject("uid");}// 设置cookie中的THPMSCookie为登录用户的tokenres.addCookie(new Cookie(Contants.COOKIE_NAME, userObject.getString("token")));log.info("UserLoginServiceImpl userLogin result=[" + userObject.toString() + "]");log.info("UserLoginServiceImpl userLogin end");return PlatformResult.success(userObject);}/*** 清空登录计数* * @author: caip* @date: 2021-04-07 15:45:57* @param userName*/private void clearLoginCount(String userName) {log.info("UserLoginServiceImpl clearLoginCount start");ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();// 清空登录计数opsForValue.set(SHIRO_LOGIN_COUNT + userName, "0");// 清空锁opsForValue.set(SHIRO_IS_LOCK + userName, "");log.info("UserLoginServiceImpl clearLoginCount end");}}

补充说明

以上代码有部分业务代码,整合了自用的SSO(单点登录系统)用于验证账户密码正确性,使用时只需要关心锁定和解锁功能,其他代码直接删除或替换为业务代码即可。

java实现用户登录异常统计、锁定及解锁功能相关推荐

  1. java ee用户登录_EE Servlet 3:使用会话和过滤器开发用户登录

    java ee用户登录 我在上一篇文章中介绍了Application类,您可以在其中设置后端服务. 我添加的一个示例服务是UserService . 该服务将加载包含用户名和密码集的Java用户属性文 ...

  2. Java 实现用户登录项目

    Java 实现用户登录项目 需求: 在页面中要求输入用户名和密码,并显示验证码.在三项都通过验证后显示登录成功否则登录失败 分析; 在验证用户名密码之前应该先判断验证码是否通过验证,防止多次连接数据库 ...

  3. Liunx创建新用户登录异常:/usr/bin/xauth: error/timeout in locking authority file /home/liuqidong/.Xauthority

    Liunx创建新用户登录异常:/usr/bin/xauth: error/timeout in locking authority file /home/liuqidong/.Xauthority 问 ...

  4. 国产化DM达梦数据库 - 用户状态查询、锁定与解锁,“登录失败次数超过限制”问题解决

    达梦数据库密码输入错误达到限制后会被锁定一段时间. An error occurred while establishing the connection:Long Message: 登录失败次数超过 ...

  5. java实现用户登录注册功能(用集合框架来实现)

    需求:实现用户登录注册功能(用集合框架来实现) 分析: A:需求的类和接口 1.用户类 UserBean 2.用户操作方法接口和实现类 UserDao UserDaoImpl 3.测试类 UserTe ...

  6. qq浏览器网页版_QQ邮箱回应部分用户登录异常:系后台服务波动,问题已解决...

    5月6日消息,针对用户反映QQ邮箱登录异常情况,腾讯QQ邮箱官方回应称,因后台服务波动,部分用户出现登录异常情况,目前问题已解决. 5月6日上午,有网友反映QQ邮箱崩溃,换浏览器依然无法登录,PC端和 ...

  7. PostgreSQL用户登录失败自动锁定的解决办法

    墨墨导读:PostgreSQL使用session_exec插件实现用户密码验证失败几次后自动锁定,本文介绍一种处理方案. 一.插件session_exec安装配置篇 下载插件并编译安装. https: ...

  8. Java游戏用户登录注册_Java实现多用户注册登录的幸运抽奖

    本文实例为大家分享了Java实现简单幸运抽奖的具体代码,供大家参考,具体内容如下 代码模块: User类: package test1; public class User { private Str ...

  9. linux用户登录失败,锁定用户

    2019独角兽企业重金招聘Python工程师标准>>> #vim /etc/pam.d/login auth required pam_tally2.so deny=3 unlock ...

最新文章

  1. 菜鸟脱壳之脱壳的基础知识(六)——手动查找IAT和修复Dump的程序
  2. matlab模拟gpd,如何用ARMA模型预测中国GDP
  3. 一文带你看懂分布式软总线在家庭场景的应用
  4. 第三次学JAVA再学不好就吃翔(part6)--基础语法之char数据类型
  5. 基于套接字SOCKET的及时聊天
  6. 回文三位数(信息学奥赛一本通-T1155)
  7. Linux之python3编译安装
  8. Delphi 程序开发范例宝典(第2版)高清PDF下载 附光盘
  9. 长沙android工程师,长沙安卓工程师辅导
  10. 商务口语:议价时可能用到的句子
  11. IntelliJ IDEA使用技巧(三)——Debug 篇
  12. Javascript的一种代码结构方式——插件式
  13. 咪咕:笔试题(20190916)
  14. 如何测算信息化项目软件运维费?
  15. linux找回cp之前的文件,Linux中找回误删除的文件
  16. 普渡大学计算机图形,美国:普渡大学(UX方向)
  17. inputBox 与 Application.inputBox 的用法与区别。
  18. Anaconda入门:安装及包与环境的管理(conda命令)
  19. 重视六大职场面试礼仪
  20. fatal error: zlib.h: No such file or directory

热门文章

  1. 58同城将在美国纽交所挂牌上市
  2. Dreamweaver(Dw)2021下载及安装教程
  3. U盘小助手 使用和功能说明
  4. 计算机专业必读的几本经典书!计算机大腕推荐
  5. 基于FPGA的VGA显示图片
  6. 在microsoft vc++ 2008版中运行李先静先生一书及数据结构高一凡先生一书实例的方法...
  7. ORECAL分析函数
  8. CMW500LTE信令测试基础操作步骤
  9. X线DR医学图像 --- DR医用滤线栅及摩尔纹详解 (一) 滤线栅的原理
  10. LTE 测试文档(翻译)