登陆接口的设计+springboot防暴力破解_登录接口设计-程序员宅基地

技术标签: java  vue  springboot  

安全——登陆接口的设计

没有绝对的安全,只的一步一步的防护

登陆是系统的门户接口,也是最容易被访问的接口。你写的是合格的吗,还是只是实现了个查询。

一、相关库表的设计

1、用户的基本信息和用户账号信息最好没有放在一个表;

2、有没有三方登陆的需求,有的话;第三方的信息最好在一个表;

3、密码加密是肯定的,那加盐了吗,账号信息表中放一个salt吧;

4、做上登陆记录Log表,也可以记录ip可以做异地提醒;

二、你的友好提示可能存在安全问题

1、账号不存在?表示你做了一个全表的账号查询。
2、密码错误?表示我的账号是对的了?那我试试这个密码?

* 账号或密码错误。这是可取的。

三、做一个防暴力破解吧

传统用户名,密码登陆
例子:登陆接口,一分钟内出错超过3次,账号封锁3分钟,并向用户手机发送验证和提醒。具体业务逻辑可以自定义;

实现:springboot的aop+redis

aop的两个方法:

@AfterReturning :后置——接口方法执行完
@AfterThrowing:存在异常——接口方法执行存在异常

基本逻辑:账号失败一次,就把账号做为key,vaue为 0,time=过期时间放到redis里;失败一次+1;成功:看看redis里有没有这个key,有就删除;

redis.setString(key:用户名或账号,value: 次数,time:(过期时间))

核心代码:

@AfterReturning(returning = "ret", pointcut = "countPoint()")// returning的值和doAfterReturning的参数名一致
public void doAfterReturning(Object ret) throws Throwable {
    
    ObjectMapper objectMapper = new ObjectMapper();
    // ResponseVO responseVO = objectMapper.convertValue(ret, ResponseVO.class);
    String s = redisUtils.get(redisKey);
    if (s != null) {
    
        redisUtils.del(redisKey);
    }
}
  @AfterThrowing(throwing = "ex", pointcut = "countPoint()")
public void afterThrowing(Exception ex) {
    
    String s = redisUtils.get(redisKey);
    if (null == s) {
    
        redisUtils.set(redisKey, "1", defaultTimeOut);
    } else {
    
        Integer i = Integer.parseInt(s) + 1;
        if (i < count) {
    //小于设定次数
            long ttl = redisUtils.ttl(redisKey);
            redisUtils.set(redisKey, i.toString(), ttl);
        }
        if (i == count) {
    //符合设定值
            redisUtils.set(redisKey, i.toString(), timeOut);
            throw new EchoException("请求锁定," + timeOut + "秒后再做尝试。");
        }
    }

}

完整代码

1、自定义注解

/**
 * @Description: 多次接口尝试进行账户封锁
 * @Author: HuangJiangMin
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodResponseException {
    
    /**
     * 失敗次数
     */
    int exceptionCount() default 5;

    /**
     * 限制时间 (秒)
     */
    long redisExpired() default 60;

}

2、AOP

/**
 * @Description:多次接口尝试进行账户封锁
 * @Author:HuangJiangMin
 */

@Aspect
@Component
public class ResponseExceptionAspect {
    
    @Autowired
    private RedisUtils redisUtils;

    private String redisKey;
    private int count;
    private long timeOut;
    private long defaultTimeOut = 60;

    @Pointcut("@annotation(scirichon.echo.art.infrastructure.annotation.MethodResponseException)")
    private void countPoint() {
    

    }

    @Before("countPoint()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
    

        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String methodUrl = request.getRequestURL().toString();
        /**
         * 获取注解自定义参数
         */
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        MethodResponseException methodResponseException = method.getAnnotation(MethodResponseException.class);

        count = methodResponseException.exceptionCount();
        timeOut = methodResponseException.redisExpired();


        redisKey = RedisEnum.RESPONSE_EXCEPTION_COUNT_KEY.tacetion(methodKey);
        String s = redisUtils.get(redisKey);

        if (null != s && count == Integer.parseInt(s)) {
    
            throw new EchoException("请求锁定中,请稍后再做尝试。");
        }

    }

    @AfterReturning(returning = "ret", pointcut = "countPoint()")// returning的值和doAfterReturning的参数名一致
    public void doAfterReturning(Object ret) throws Throwable {
    
        ObjectMapper objectMapper = new ObjectMapper();
        // ResponseVO responseVO = objectMapper.convertValue(ret, ResponseVO.class);
        String s = redisUtils.get(redisKey);
        if (s != null) {
    
            redisUtils.del(redisKey);
        }
    }

    @AfterThrowing(throwing = "ex", pointcut = "countPoint()")
    public void afterThrowing(Exception ex) {
    
        String s = redisUtils.get(redisKey);
        if (null == s) {
    
            redisUtils.set(redisKey, "1", defaultTimeOut);
        } else {
    
            Integer i = Integer.parseInt(s) + 1;
            if (i < count) {
    //小于设定次数
                long ttl = redisUtils.ttl(redisKey);
                redisUtils.set(redisKey, i.toString(), ttl);
            }
            if (i == count) {
    //符合设定值
                redisUtils.set(redisKey, i.toString(), timeOut);
                throw new EchoException("请求锁定," + timeOut + "秒后再做尝试。");
            }
        }

    }

    @Around("countPoint()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
    
        long startTime = System.currentTimeMillis();
        Object ob = pjp.proceed();// ob 为方法的返回值
        //logger.info("性能监控(耗时) : " + (System.currentTimeMillis() - startTime) + "毫秒");
        return ob;
    }
}

 

以上就用springboot实现了防暴力破解

安全只是相对的。

有人说了,我可以写个程序一直调用你的接口;让所有的用户都登陆不上。
——用CSRF可以;
又有问题了 referer可以人为变更。你要怎么处理呢。
——一个IP一分钟内访问超过多少次,就封禁可以吗(这个不一定可行)

只有层层防护。没有绝对安全

我的个人博客:
http://www.dhzi.com.cn/

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/huangjiangmin/article/details/111482588

智能推荐

【 FPGA 】Xilinx设计约束(XDC)中时钟约束的表示方法_xdc period-程序员宅基地

文章浏览阅读1.2w次,点赞16次,收藏84次。目录 时钟描述基本时钟虚拟时钟生成时钟时钟描述(1)clk0的时钟属性:周期为10ns,占空比为50%,相移为0ns;(相移也可以用°来表示,例如相移位0°,相移为90°等)(2)clk1的时钟属性:周期为8ns,占空比为75%,相移为2ns;(相移为2ns,也就是相移为90°)描述时钟,默认第一个值为上升沿,占空比是高电平占周期的比。则上图中时钟..._xdc period

树莓派keras加载modle失败_【开源项目】特斯拉+树莓派实现车牌识别检测系统-程序员宅基地

文章浏览阅读119次。怎样在不换车的前提下打造一个智能车系统呢?一段时间以来,本文作者 Robert Lucian Chiriac 一直在思考让车拥有探测和识别物体的能力。本文来源:机器视觉怎样在不换车的前提下打造一个智能车系统呢?一段时间以来,本文作者 Robert Lucian Chiriac 一直在思考让车拥有探测和识别物体的能力。这个想法非常有意思,因为我们已经见识过特斯拉的能力,虽然没法马上买一辆特..._特斯拉model3车牌不能识别

C++ 洛谷练习题 · 蛇形方阵_c++蛇形方阵-程序员宅基地

文章浏览阅读641次。洛谷P5731题解(超详细)_c++蛇形方阵

ArcGIS.Server.9.3和ArcGIS API for JavaScript实现基本的地图功能-程序员宅基地

文章浏览阅读494次。转贴链接:(博文有顺序)http://www.cnblogs.com/hll2008/archive/2008/11/14/1333828.html(显示地图,鼠标坐标,地图范围)http://www.cnblogs.com/hll2008/archive/2008/11/17/1335313.html(Toc功能)http://www.cnblogs.com/hll2008/ar

数据科学与大数据分析项目练习-7在石油产量数据集上应用时间序列分析_月产油量数据集-程序员宅基地

文章浏览阅读1.1k次,点赞3次,收藏11次。在石油产量数据集上应用时间序列分析这部分在学习笔记部分介绍过,在这里结合代码来学习实践。首先加载库,设置默认路径,并读取文件library(forecast)setwd("c:/Users/T7/Desktop/123")gas_prod_input <- as.data.frame( read.csv("gas_prod.csv") )读取的数据集如下:创建一个时间序列对象并输出图gas_prod <- ts(gas_prod_input[,2])plot(gas_p_月产油量数据集

4-16译码器-程序员宅基地

文章浏览阅读5.2k次,点赞2次,收藏2次。这么复杂的软件竟然整明白了,很兴奋很激动!从安装软件到新建工程,从画电路图到仿真波形,太真不容易了!不过很是高兴啊,下面的就是本次实验结果,两片3-8译码器拼接成4-16译码器还不会的小伙伴看过来,省的费劲了,哈哈哈 此刻怀揣着十分激动的心情去做下一个作业对了,别忘了我叫_4-16译码器

随便推点

数据结构与算法分析(二) —— ArrayList泛型类的实现-程序员宅基地

文章浏览阅读1.8k次,点赞9次,收藏3次。在学习数据结构与算法分析过程中,便于使用的ArrayList类的实现是个很好的练手项目,本博文将提供详细的代码,给出一个便于使用的ArrayList泛型类的实现。为了避免与类库中的类相混淆,我们将其命名为MyArrayList。由于程序较长,也相对简单,不太想逐段分析,我们先将几个注意点指出,后面放出整段程序。(1)首先,注意Collection和Collections是完全不同的两个概念。java

如何将一张图片或者是一个文件读取到matlab中_matlab怎么导入图片-程序员宅基地

文章浏览阅读6.6k次,点赞8次,收藏42次。一、编辑器我用的2015版本,版本如果不一样,也不影响这些基础的操作,首先选择自己要把文件创建在哪个位置,可以点击紫色源泉里的三角形,一级一级的选择要保存的地址。二、选择好地方之后,比如说我要在作业这个目录下创建一个脚本,选择新建->脚本三、这个时候你来到了一个新的界面,这时你按下键盘的ctrl+s进行保存,会弹出以下界面,你可以改一下你的脚本的名字,然后点击保存即可。四、将你要读到matlab中的图片放在你这个脚本所在的位置,你的脚本的位置可以通过界面进行查看,也就是我红色方.._matlab怎么导入图片

java.lang.NoClassDefFoundError问题-程序员宅基地

文章浏览阅读1.3w次。Java程序员经常被运行时的java.lang.NoClassDefFoundError搞得焦头烂额,产生这个问题的原因显然是Java的类加载器没有找到相关类的定义这里就先举一个实际问题的例子[root@cat Work]# java -classpath /mnt/data/Work/TestRabbitMQ/lib/rabbitmq-client.jar -jar TestRa_java.lang.noclassdeffounderror

element-ui 中使用Dialog 对话框 点击非对话框区域设置对话框不关闭_element puls 对话框 点击非内容区域-程序员宅基地

文章浏览阅读409次。element-ui Dialog 对话框 默认的点击非弹窗区会关闭,有时候感觉太美(蹩脚),所以要设置为不可关闭,本身自带该属性即可实现 <el-dialog title="锁定岗位" :close-on-click-modal="false" > </el-dialog>:close-on-click-modal=“false” 设置为false即可实现..._element puls 对话框 点击非内容区域

C/C++ 代码检测工具_c++代码查重工具-程序员宅基地

文章浏览阅读8k次。valgrind:https://www.cnblogs.com/AndyStudy/p/6409287.htmlC/C++静态代码检查工具对比分析:http://qa.blog.163.com/blog/static/190147002201611147530522/C++代码质量扫描主流工具深度比较:https://blog.csdn.net/wetest_tencent/article..._c++代码查重工具

android 验证URL是否合法_android url是否合规-程序员宅基地

文章浏览阅读3.4k次。Patterns.WEB_URL.matcher(URL).matches(),中间填入需要验证的URl地址,合法返回true,不合法返回false;这种方法只能验证URL是否合法,而不能验证URl是否可以进入或者使用~_android url是否合规