后端常用安全措施
一、限流
1.简介
限流就是限制流量,但这里的流量是一个比较笼统的概念。如果考虑各种不同的场景,限流是非常复杂的,而且和具体的业务规则密切相关
通过限流,可以控制服务请求的速率,从而提高系统应对突发大流量的能力,让系统更具弹性
可以考虑如下几种常见的场景:
- 限制某个接口一分钟内最多请求 100 次
-
限制某个用户的下载速度最多 100KB/S
-
限制某个用户同时只能对某个接口发起 5 路请求
-
限制某个 IP 来源禁止访问任何请求
专业术语
名称 | 说明 |
---|---|
Request rate limiting | 请求频率限流 |
Concurrent requests limiting | 并发量限流 |
内部通过集成过滤器和Bucket4j实现请求拦截
2.Bucket4j限流库
Bucket4j 是一个基于令牌桶算法实现的强大的限流库,它不仅支持单机限流,还支持通过诸如 Hazelcast、Ignite、Coherence、Infinispan 或其他兼容 JCache API (JSR 107) 规范的分布式缓存实现分布式限流。
核心概念
概念 | 说明 |
---|---|
Bucket | 接口代表了令牌桶的具体实现也是我们操作的入口。它提供了诸如 |
Bandwidth | 带宽,可以理解为限流的规则。Bucket4j 提供了两种方法来创建 Bandwidth:simple 和 classic 。simple 方式桶大小和填充速度是一样的,classic 方式更灵活一点,可以自定义填充速度 |
Refill | 用于填充令牌桶,可以通过它定义填充速度,Bucket4j 有两种填充令牌的策略:间隔策略(intervally) 和 贪婪策略(greedy) 间隔策略指的是每隔一段时间,一次性的填充所有令牌 贪婪策略会尽可能贪婪的填充令牌 |
Bucket4j 唯一不足的地方是它只支持请求频率限流,不支持并发量限流
3.代码实现
重要配置参数
rest:limits:tenant: #租户拦截enabled: "${TB_SERVER_REST_LIMITS_TENANT_ENABLED:false}" configuration: "${TB_SERVER_REST_LIMITS_TENANT_CONFIGURATION:100:1,2000:60}" #1秒最高100次,1分钟最高2000次customer: #客户拦截enabled: "${TB_SERVER_REST_LIMITS_CUSTOMER_ENABLED:false}"configuration: "${TB_SERVER_REST_LIMITS_CUSTOMER_CONFIGURATION:50:1,1000:60}" #1秒最高50次,1分钟最高1000次 |
代码实现
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {SecurityUser user = getCurrentUser();//获取当前请求的用户if (user != null && !user.isSystemAdmin()) {//判断用户是不是系统管理员if (perTenantLimitsEnabled) {TbRateLimits rateLimits = perTenantLimits.computeIfAbsent(user.getTenantId(), id -> new TbRateLimits(perTenantLimitsConfiguration)); //构建用户对应的速率控制类if (!rateLimits.tryConsume()) {//判断当前请求是否达到限制errorResponseHandler.handle(new TbRateLimitsException(EntityType.TENANT), (HttpServletResponse) response);return;}}if (perCustomerLimitsEnabled && user.isCustomerUser()) {TbRateLimits rateLimits = perCustomerLimits.computeIfAbsent(user.getCustomerId(), id -> new TbRateLimits(perCustomerLimitsConfiguration));if (!rateLimits.tryConsume()) {errorResponseHandler.handle(new TbRateLimitsException(EntityType.CUSTOMER), (HttpServletResponse) response);return;}}}chain.doFilter(request, response);}//构建速率控制对象public TbRateLimits(String limitsConfiguration) {LocalBucketBuilder builder = Bucket4j.builder();boolean initialized = false;for (String limitSrc : limitsConfiguration.split(",")) {long capacity = Long.parseLong(limitSrc.split(":")[0]);long duration = Long.parseLong(limitSrc.split(":")[1]);builder.addLimit(Bandwidth.simple(capacity, Duration.ofSeconds(duration)));initialized = true;}if (initialized) {bucket = builder.build();} else {throw new IllegalArgumentException("Failed to parse rate limits configuration: " + limitsConfiguration);}} |
二、加密
1.简介
我们开发时进行密码加密,可用的加密手段有很多,比如对称加密、非对称加密、信息摘要等。在一般的项目里,常用的就是信息摘要算法,也可以被称为散列加密函数,或者称为散列算法、哈希函数。
常用的散列函数有 MD5 消息摘要算法
2.散列加密原理
散列函数通过把消息或数据压缩成摘要信息,使得数据量变小,将数据的格式固定下来,然后将数据打乱混合,再重新创建成一个散列值,从而达到加密的目的。
散列值通常用一个短的随机字母和数字组成的字符串来代表,一个好的散列函数在输入域中很少出现散列冲突。在散列表和数据处理时,如果我们不抑制冲突来区别数据,会使得数据库中的记录很难找到。
但是仅仅使用散列函数还不够,如果我们只是单纯的使用散列函数而不做特殊处理,其实是有风险的!比如在两个用户密码明文相同时,生成的密文也会相同,这样就增加了密码泄漏的风险。
所以为了增加密码的安全性,一般在密码加密过程中还需要"加盐",而所谓的"盐"可以是一个随机数,也可以是用户名。”加盐“之后,即使密码的明文相同,用户生成的密码密文也不相同,这就可以极大的提高密码的安全性。
传统的加盐方式需要在数据库中利用专门的字段来记录盐值,这个字段可以是用户名字段(因为用户名唯一),也可以是一个专门记录盐值的字段,但这样的配置比较繁琐。
3.Spring Security中的密码处理方案
Spring Security对密码的处理方案,有如下3种方式:
-
对密码进行明文处理,即不采用任何加密方式;
-
采用MD5加密方式;
-
采用哈希算法加密方式。
项目中使用BCryptPasswordEncoder 加密
4. BCryptPasswordEncoder简介
Spring Security提供了多种密码加密算法,但官方推荐使用的是BCryptPasswordEncoder方案
我们开发时,用户表中的密码通常是使用MD5等不可逆算法加密后存储,但为了防止彩虹表破解,可以先使用一个特定的字符串(如域名)进行加密,然后再使用一个随机的salt(盐值)加密。
其中特定的字符串是程序代码中固定的,salt是每个密码单独随机的,我们一般会给用户表加一个字段单独存储,但这样比较麻烦。
而BCrypt算法却可以随机生成salt并混入最终加密后的密码,验证时也无需单独提供之前的salt,从而无需单独处理salt。不同于 Shiro 中需要自己处理密码加盐,在 Spring Security 中,BCryptPasswordEncoder 本身就自带了盐,所以处理起来非常方便。
另外BCryptPasswordEncoder使用BCrypt强哈希函数,我们在使用时可以选择提供strength和SecureRandom参数。strength值(取值在4~31之间,默认为10)越大,则密钥的迭代次数就越多,密钥迭代次数为2^strength
5. 配置密码加密算法
@Beanprotected BCryptPasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();} |
三、token验证
1.简介
Token 的中文意思是"令牌"。主要用来身份验证。比传统的身份验证方法,Token 有扩展性强,安全性高的特点,非常适合用在 Web 应用或者移动应用上
它是服务端生成的字符串,作为客户端进行请求的一个标识
JSON WEB TOKEN
JWT是token的一种实现方式,它将用户信息加密到token
里,服务器不保存任何用户信息。服务器通过使用保存的密钥验证token
的正确性,只要正确即通过验证
JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,
也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
2.基于传统session认证所显露的问题
功能 | 问题描述 |
---|---|
Session | 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大 |
扩展性 | 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力 |
CSRF | 因为是基于cookie来进行用户识别的,cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击 |
3.基于token的鉴权机制
基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息
这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利
流程说明:
- 用户使用用户名密码来请求服务器
- 服务器进行验证用户的信息
- 服务器通过验证发送给用户一个token
- 客户端存储token,并在每次请求时附送上这个token值
- 服务端验证token值,并返回数据
这个token必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持CORS(跨来源资源共享)
策略,一般我们在服务端这么做就可以了Access-Control-Allow-Origin: *
4.代码应用
//创建token public AccessJwtToken createAccessJwtToken(SecurityUser securityUser) {if (StringUtils.isBlank(securityUser.getEmail()))throw new IllegalArgumentException("Cannot create JWT Token without username/email");if (securityUser.getAuthority() == null)throw new IllegalArgumentException("User doesn't have any privileges");UserPrincipal principal = securityUser.getUserPrincipal();String subject = principal.getValue();Claims claims = Jwts.claims().setSubject(subject);claims.put(SCOPES, securityUser.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()));claims.put(USER_ID, securityUser.getId().getId().toString());claims.put(FIRST_NAME, securityUser.getFirstName());claims.put(LAST_NAME, securityUser.getLastName());claims.put(ENABLED, securityUser.isEnabled());claims.put(IS_PUBLIC, principal.getType() == UserPrincipal.Type.PUBLIC_ID);if (securityUser.getTenantId() != null) {claims.put(TENANT_ID, securityUser.getTenantId().getId().toString());}if (securityUser.getCustomerId() != null) {claims.put(CUSTOMER_ID, securityUser.getCustomerId().getId().toString());}ZonedDateTime currentTime = ZonedDateTime.now();String token = Jwts.builder().setClaims(claims).setIssuer(settings.getTokenIssuer()).setIssuedAt(Date.from(currentTime.toInstant())).setExpiration(Date.from(currentTime.plusSeconds(settings.getTokenExpirationTime()).toInstant())).signWith(SignatureAlgorithm.HS512, settings.getTokenSigningKey()).compact();return new AccessJwtToken(token, claims);}//解析tokenpublic Jws<Claims> parseTokenClaims(JwtToken token) {try {return Jwts.parser().setSigningKey(settings.getTokenSigningKey()).parseClaimsJws(token.getToken());} catch (UnsupportedJwtException | MalformedJwtException | IllegalArgumentException | SignatureException ex) {log.debug("Invalid JWT Token", ex);throw new BadCredentialsException("Invalid JWT token: ", ex);} catch (ExpiredJwtException expiredEx) {log.debug("JWT Token is expired", expiredEx);throw new JwtExpiredTokenException(token, "JWT Token expired", expiredEx);}} |
相关文章:

后端常用安全措施
一、限流 1.简介 限流就是限制流量,但这里的流量是一个比较笼统的概念。如果考虑各种不同的场景,限流是非常复杂的,而且和具体的业务规则密切相关 通过限流,可以控制服务请求的速率,从而提高系统应对突发大流量的能…...

虚拟机数据恢复—通过拼接数据库页碎片的方式恢复数据库的数据恢复案例
虚拟机数据恢复环境: 某品牌服务器通过同品牌某型号的RAID卡,将4块STAT硬盘为一组RAID10阵列。上层部署XenServer虚拟化平台,虚拟机安装Windows Server系统,每台虚拟机有两个虚拟机磁盘(系统盘 数据盘)&am…...

【vue】自封组件,基于vue2封装一个弹框组件
源码:https://download.csdn.net/download/galaxyJING/89913551...
ES6基础知识
一、定义变量的关键字let和const 1. let 定义变量的语法: let 变量名 值; 2. 和var定义变量的区别 1. 是否支持同一个作用域变量同名 var支持,let不支持 2. 是否支持预解析 var支持,let不支持 3. 是否会挂载在window对象…...

基于Multisim的模拟拔河游戏比赛设计与仿真
1.设计一个模拟拔河游戏比赛的逻辑电路 2.使用15个发光二极管表示绳子,开机后只有最中间的发光二极管亮。 3.比赛双方各持一个按钮,快速不断地按动按钮,产生脉冲,谁按的快,发光的二极管就向谁的方向移动,每…...
MyBatis 配置详解
在项目中经常会用到 mybatis 相关的一些配置,而在启动类项目工程中,一般会把 mybatis 配置文件单独写到 mybatis,yml 中,如下简单介绍下常用的 mybatis 配置 mybatis:configuration:call-setters-on-nulls: truemap-underscore-to-camel-case…...

研发运营一体化(DevOps)能力成熟度模型
目录 应用设计 安全风险管理 技术运 持续交付 敏捷开发管理 基于微服务的端到端持续交付流水线案例 应用设计 安全风险管理 技术运 持续交付...
躺平成长-利用kimi编辑助手帮助自己编程第二天
天有道,无常道,兵无常势。 {今天开始听歌(歌曲:青丝!)进行编程!} 尝试用ai帮助自己进行小程序的开发,同时最为关键,是无法能够完成相关的代码的记忆,所以我开…...
OpenSuse-搭建NFS-Server
在OpenSUSE上搭建NFS服务可以通过以下步骤完成: ### 1. 安装NFS服务器软件 首先,确保你已经安装了NFS服务器软件包。你可以使用zypper命令来安装: sudo zypper install nfs-kernel-server### 2. 配置NFS导出目录 编辑/etc/exports文件&#x…...

【数据结构与算法】之二分查找
二分查找(Binary Search)是一种在有序数组中查找特定元素的搜索算法。它通过比较数组中间元素与目标值来工作,从而将搜索范围缩小到一半,也称折半查找,是一种非常高效的工作于有序数组的查找算法。本文主要介绍二分查找…...
vue修饰符
表单修饰符 1、lazy <input type "text" v-model.lazy "value"> <p>{{value}}</p>lazy跟懒加载类似,只有再说鼠标离开光标的时候才会触发,也就是说在input事件的oninput书法的时候不会赋值,当onch…...
Oracle里面,with ... as 用法介绍
在Oracle数据库中,WITH AS 子句(也称为公用表表达式,CTE, Common Table Expression)是一种在查询中定义临时结果集的方法。这个临时结果集可以在后续的查询中被引用,就像是一个临时的表或视图一样。使用 WITH AS 子句可…...

一个简单的Qt Console Application计算练习程序
初步体验Qt Creator 用途:练习20以内2位数乘法速算的程序 功能1:支持用户设定题目数量 std::cout << "请输入本次练习题目数量:";int numProblems 0;std::string num;std::cin >> num;try {numProblems std::stoi(…...

windows文件拷贝给wsl2的Ubuntu
参考: windows文件如何直接拖拽到wsl中_win 移到文件到wsl-CSDN博客 cp -r /mnt/盘名/目标文件 要复制到wsl中的位置e.g.cp -r /mnt/d/byt5 /home Linux文件复制、移动、删除等操作命令_linux移动命令-CSDN博客 Linux 文件、文件夹的复制、移动、删除 - Be-myse…...
vivado 采用 SSI 器件进行设计
SSI 管脚的考虑因素 在为特定 SLR 中的组件规划管脚时,请将引脚放置在同一个 SLR 中。例如,将器件的 DNA 信息作为外部接口的一部分 时,请将该接口的引脚放置在 DNA_PORT 所在的主 SLR 中。其它考虑因素包括如下: • 把…...

Lua环境安装
软考鸭微信小程序 学软考,来软考鸭! 提供软考免费软考讲解视频、题库、软考试题、软考模考、软考查分、软考咨询等服务 Lua是一种轻量级、小巧且易于嵌入应用程序的脚本语言,广泛用于游戏开发、Web开发、自动化脚本等领域。本文将详细介绍如何在不同操作系统上安装L…...

浏览器控制的无线开关
esp32-c3 作为HTTP server 控制led 灯。服务器注册两个uri 。一个"/open" 控制开,一个"/close"控制关。下一步再用一片c3作为客户端,运行http client 发送/open. /Close 模拟浏览器,控制led. 其实只要用手机或pc或平…...

Docker部署SSM项目及避坑指南
#又踩坑了,这里记录一下,以免日后忘记 前言:本来以为用docker部署个项目很轻松,嗯结果,又踩坑了,这里记录一个完整版。话不多说,开整。 第一步: 用docker拉取MySQL和Tomcat&#…...
多线程代码案例:单例模式/阻塞队列/线程池/定时器
案例一.单例模式 单例模式是一种设计模式;类似于棋谱,有固定套路,针对一些特定场景可以给出一些比较好的解决方案; 只要按照设计模式来写代码,就可以保证代码不会太差,保证了代码的下限; --------------------------------------------------------------------------------…...
Ruby CGI Cookie
Ruby CGI Cookie 在Web开发中,Cookie是一种常用的技术,用于在用户浏览器和服务器之间存储和传递信息。Ruby作为一种流行的编程语言,提供了CGI(Common Gateway Interface)库来处理Cookie。本文将详细介绍如何在Ruby中使用CGI库来创建、读取、修改和删除Cookie。 Cookie的…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...

如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...

基于SpringBoot在线拍卖系统的设计和实现
摘 要 随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统,主要的模块包括管理员;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...
怎么让Comfyui导出的图像不包含工作流信息,
为了数据安全,让Comfyui导出的图像不包含工作流信息,导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo(推荐) 在 save_images 方法中,删除或注释掉所有与 metadata …...