当前位置: 首页 > news >正文

【SpringBoot应用篇】【AOP+注解】SpringBoot+SpEL表达式基于注解实现权限控制

【SpringBoot应用篇】【AOP+注解】SpringBoot+SpEL表达式基于注解实现权限控制

  • Spring SpEL
    • 基本表达式
    • 类相关表达式
    • 表达式模板
  • SpEL表达式实现权限控制
    • PreAuth
    • AuthFun
    • PreAuthAspect
    • UserController
    • SpelParserUtils

Spring SpEL

Spring 表达式语言 SpEL 是一种非常强大的表达式语言,它支持在运行时查询和操作对象图。 它提供了许多高级功能,例如方法调用和基本的字符串模板功能。表达式语言给静态Java语言增加了动态功能。

Spring 表达式语言最初是为 Spring 社区创建的,它拥有一种受良好支持的表达式语言,可用于 Spring 产品组合中的所有产品。 虽然 SpEL 是 Spring 产品组合中表达式评估的基础,但它不直接与 Spring 绑定,可以独立使用

Spring Security框架中启用prePost注解的支持就是使用SpEL表达式实现的权限控制

  • @PreAuthorize(“hasAuthority(‘save’)”)
  • @PreAuthorize(“isAnonymous()”)
  • @PreAuthorize(“isAuthenticated()”)

SpEL支持如下表达式:

  • 基本表达式: 字面量表达式、关系,逻辑与算数运算表达式、字符串连接及截取表达式、三目运算及Elivis表达式、正则表达式、括号优先级表达式
  • 类相关表达式: 类类型表达式、类实例化、instanceof表达式、变量定义及引用、赋值表达式、自定义函数、对象属性存取及安全导航表达式、对象方法调用、Bean引用
  • 集合相关表达式:内联List、内联数组、集合,字典访问、列表,字典,数组修改、集合投影、集合选择;不支持多维内联数组初始化;不支持内联字典定义
  • 其他表达式:模板表达式。

注:SpEL表达式中的关键字是不区分大小写的。

基本表达式

/**
* 基本表达式 : 字面量表达式
*/
@Test
public void test01() {// 1)创建解析器:SpEL使用ExpressionParser接口表示解析器,提供SpelExpressionParser默认实现;ExpressionParser parser = new SpelExpressionParser();// 2)解析表达式:使用ExpressionParser的parseExpression来解析相应的表达式为Expression对象。Expression expression = parser.parseExpression("1+2");// 3)求值:通过Expression接口的getValue方法根据上下文获得表达式值。System.out.println(expression.getValue());String str1 = parser.parseExpression("'Hello World!'").getValue(String.class);int int1 = parser.parseExpression("1").getValue(Integer.class);long long1 = parser.parseExpression("-1L").getValue(long.class);float float1 = parser.parseExpression("1.1").getValue(Float.class);double double1 = parser.parseExpression("1.1E+2").getValue(double.class);int hex1 = parser.parseExpression("0xa").getValue(Integer.class);long hex2 = parser.parseExpression("0xaL").getValue(long.class);boolean true1 = parser.parseExpression("true").getValue(boolean.class);boolean false1 = parser.parseExpression("false").getValue(boolean.class);Object null1 = parser.parseExpression("null").getValue(Object.class);System.out.println("str1=" + str1);System.out.println("int1=" + int1);System.out.println("long1=" + long1);System.out.println("float1=" + float1);System.out.println("double1=" + double1);System.out.println("hex1=" + hex1);System.out.println("hex2=" + hex2);System.out.println("true1=" + true1);System.out.println("false1=" + false1);System.out.println("null1=" + null1);
}
/**
* 基本表达式 : 字面量表达式
*/
@Test
public void test02() {// 1)创建解析器:SpEL使用ExpressionParser接口表示解析器,提供SpelExpressionParser默认实现;ExpressionParser parser = new SpelExpressionParser();// 2)解析表达式:使用ExpressionParser的parseExpression来解析相应的表达式为Expression对象。Expression expression = parser.parseExpression("('Hello' + ' World').concat(#end)");// 3)构造上下文:准备比如变量定义等等表达式需要的上下文数据。EvaluationContext context = new StandardEvaluationContext();context.setVariable("end", "!");// 4)求值:通过Expression接口的getValue方法根据上下文获得表达式值。System.out.println(expression.getValue(context));
}

类相关表达式

@Data
@Component("ss")
public class User {private String username;private String address;private Integer age;public String sayHello(String username) {return "hello " + username;}public String sayHello(Integer age) {return "hello " + username + ";age=" + age;}public String sayHello() {return "hello " + username;}
}

/**
* 类相关表达式: 变量定义及引用
*/
@Test
public void test03() {String expression = "#user.username";ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression(expression);StandardEvaluationContext ctx = new StandardEvaluationContext();User user = new User();user.setAddress("长沙");user.setUsername("zysheep");user.setAge(24);;ctx.setVariable("user", user);String value = exp.getValue(ctx, String.class);System.out.println("value = " + value);
}
@Test
public void test04() {String expression = "username";ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression(expression);StandardEvaluationContext ctx = new StandardEvaluationContext();User user = new User();user.setAddress("长沙");user.setUsername("zysheep");user.setAge(24);// user 对象设置为 rootObject,那么表达式中就不需要 #user.ctx.setRootObject(user);String value = exp.getValue(ctx, String.class);System.out.println("value = " + value);
}
/**
* 类相关表达式: 对象方法调用
*/
@Test
public void test05() {String expression = "sayHello(99)";ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression(expression);StandardEvaluationContext ctx = new StandardEvaluationContext();User user = new User();user.setAddress("长沙");user.setUsername("zysheep");user.setAge(24);ctx.setRootObject(user);String value = exp.getValue(ctx, String.class);System.out.println("value = " + value);
}
/**
* 类相关表达式: 对象方法调用
*
* 调用无参的 sayHello
*/
@Test
public void test06() {String expression = "sayHello()";ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression(expression);StandardEvaluationContext ctx = new StandardEvaluationContext();User user = new User();user.setAddress("长沙");user.setUsername("zysheep");user.setAge(24);ctx.setRootObject(user);String value = exp.getValue(ctx, String.class);System.out.println("value = " + value);
}
/**
* 类相关表达式: Bean引用
*/
@Test
public void test07() {// 通过 SpEL 表达式来调用这个名为 ss 的 bean 中的 sayHello 方法String expression = "@ss.sayHello('spel bean引用')";ExpressionParser parser = new SpelExpressionParser();Expression exp = parser.parseExpression(expression);StandardEvaluationContext ctx = new StandardEvaluationContext();// 给配置的上下文环境设置一个 bean 解析器,这个 bean 解析器会自动跟进名字从 Spring 容器中找打响应的 bean 并执行对应的方法ctx.setBeanResolver(new BeanFactoryResolver(beanFactory));String value = exp.getValue(ctx, String.class);System.out.println("value = " + value);
}

表达式模板

/**
* 表达式模板:
* 模板表达式就是由字面量与一个或多个表达式块组成。每个表达式块由“前缀+表达式+后缀”形式组成,如“${1+2}”即表达式块
*
*/
@Test
public void test08() {//创建解析器SpelExpressionParser parser = new SpelExpressionParser();//创建解析器上下文ParserContext context = new TemplateParserContext("%{", "}");Expression expression = parser.parseExpression("你好:%{#name},我们正在学习:%{#lesson}", context);//创建表达式计算上下文EvaluationContext evaluationContext = new StandardEvaluationContext();evaluationContext.setVariable("name", "zysheep");evaluationContext.setVariable("lesson", "SpEL表达式!");//获取值String value = expression.getValue(evaluationContext, String.class);System.out.println(value);
}

SpEL表达式实现权限控制

PreAuth

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PreAuth {/***** permissionAll()-----只要配置了角色就可以访问* hasPermission("MENU.QUERY")-----有MENU.QUERY操作权限的角色可以访问* permitAll()-----放行所有请求* denyAll()-----只有超级管理员角色才可访问* hasAuth()-----只有登录后才可访问* hasTimeAuth(1,,10)-----只有在1-10点间访问* hasRole(‘管理员’)-----具有管理员角色的人才能访问* hasAllRole(‘管理员’,'总工程师')-----同时具有管理员、总工程师角色的人才能访问** Spring el* 文档地址:https://docs.spring.io/spring/docs/5.1.6.RELEASE/spring-framework-reference/core.html#expressions*/String value();}

AuthFun

/*** <p>* 类相关表达式: Bean引用,参数要用单引号包括** @PreAuth("@af.hasPermission('ADMIN, USER')")* </p>** @author : lyw* @since : 2023/11/23 16:01*/
@Component("af")
public class AuthFun {/*** 判断角色是否具有接口权限** @return {boolean}*/public boolean permissionAll() {//TODO  读取数据库权限数据return true;}/*** 判断角色是否具有接口权限** @param permission 权限编号,对应菜单的MENU_CODE* @return {boolean}*/public boolean hasPermission(String permission) {return hasRole(permission);}/*** 放行所有请求** @return {boolean}*/public boolean permitAll() {return true;}/*** 只有超管角色才可访问** @return {boolean}*/public boolean denyAll() {return hasRole("ADMIN");}/*** 是否已授权** @return {boolean}*/public boolean hasAuth() {return true;}/*** 是否有时间授权** @param start 开始时间* @param end   结束时间* @return {boolean}*/public boolean hasTimeAuth(Integer start, Integer end) {Integer hour = DateUtil.hour(new Date(), true);return hour >= start && hour <= end;}/*** 判断是否有该角色权限** @param role 单角色* @return {boolean}*/public boolean hasRole(String role) {return hasAnyRole(role);}/*** 判断是否具有所有角色权限** @param role 角色集合* @return {boolean}*/public boolean hasAllRole(String... role) {for (String r : role) {if (!hasRole(r)) {return false;}}return true;}/*** 判断是否有该角色权限** @param role 角色集合* @return {boolean}*/public boolean hasAnyRole(String... role) {return Arrays.stream(role).anyMatch(item -> StringUtils.equals("ADMIN", item));}
}

PreAuthAspect

@Aspect
@Component
public class PreAuthAspect {@Autowiredprivate BeanFactory applicationContext;private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();@Pointcut("@annotation(cn.zysheep.annotation.PreAuth) || @within(cn.zysheep.annotation.PreAuth)")public void pointcut() {}@Around("pointcut()")public Object preAuth(ProceedingJoinPoint point) throws Throwable {if (handleAuth(point)) {return point.proceed();}throw new BizException("用户无权限,非法用户!");}private boolean handleAuth(ProceedingJoinPoint point) {MethodSignature ms = point.getSignature() instanceof MethodSignature? (MethodSignature) point.getSignature():null;Method method = ms.getMethod();// 读取权限注解,优先方法上,没有则读取类PreAuth preAuth = method.getAnnotation( PreAuth.class);// 判断表达式String condition = preAuth.value();if (StringUtils.isNotBlank(condition)) {// @PreAuth("@af.hasPermission('ADMIN, USER')")Expression expression = EXPRESSION_PARSER.parseExpression(condition);// 方法参数值Object[] args = point.getArgs();StandardEvaluationContext context = getEvaluationContext(method, args);// 获取解析计算的结果return expression.getValue(context, Boolean.class);}return false;}/*** 获取方法上的参数** @param method 方法* @param args   变量* @return {SimpleEvaluationContext}*/private StandardEvaluationContext getEvaluationContext(Method method, Object[] args) {// 初始化Spel表达式上下文,并设置 AuthFunStandardEvaluationContext context = new StandardEvaluationContext();// 设置表达式支持spring beancontext.setBeanResolver(new BeanFactoryResolver(applicationContext));// 可以从session中获取登录用户的权限
//        for (int i = 0; i < args.length; i++) {
//            // 读取方法参数
//            MethodParameter methodParam = ClassUtil.getMethodParameter(method, i);
//            // 设置方法 参数名和值 为spel变量
//            context.setVariable(methodParam.getParameterName(), args[i]);
//        }return context;}
}

UserController

@RestController
@RequestMapping("/api")
@Slf4j
public class UserController {@GetMapping("/save")@PreAuth("@af.hasPermission('ADMIN')")public R save() {log.info("====执行保存业务逻辑=====");return R.success();}@GetMapping("/get")@PreAuth("@af.hasPermission('USER')")public R get() {log.info("====执行保存业务逻辑=====");return R.success();}}

SpelParserUtils

public final class SpelParserUtils {private static final String EXPRESSION_PREFIX = "#{";private static final String EXPRESSION_SUFFIX = "}";/*** 表达式解析器*/private static ExpressionParser expressionParser = new SpelExpressionParser();/***  参数名解析器,用于获取参数名*/private static DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();private SpelParserUtils(){}/*** 解析spel表达式** @param method 方法* @param args 参数值* @param spelExpression  表达式* @param clz  返回结果的类型* @param defaultResult 默认结果* @return 执行spel表达式后的结果*/public static <T> T parse(Method method, Object[] args, String spelExpression, Class<T> clz, T defaultResult) {String[] params = parameterNameDiscoverer.getParameterNames(method);EvaluationContext context = new StandardEvaluationContext();//设置上下文变量for (int i = 0; i < params.length; i++) {context.setVariable(params[i], args[i]);}T result = getResult(context,spelExpression,clz);if(Objects.isNull(result)){return defaultResult;}return result;}/*** 解析spel表达式** @param method  方法* @param args 参数值* @param spelExpression  表达式* @param clz  返回结果的类型* @return 执行spel表达式后的结果*/public static <T> T parse(Method method, Object[] args, String spelExpression, Class<T> clz) {String[] params = parameterNameDiscoverer.getParameterNames(method);EvaluationContext context = new StandardEvaluationContext();//设置上下文变量for (int i = 0; i < params.length; i++) {context.setVariable(params[i], args[i]);}return getResult(context,spelExpression,clz);}/*** 解析spel表达式** @param param  参数名* @param paramValue 参数值* @param spelExpression  表达式* @param clz  返回结果的类型* @return 执行spel表达式后的结果*/public static <T> T parse(String param, Object paramValue, String spelExpression, Class<T> clz) {EvaluationContext context = new StandardEvaluationContext();//设置上下文变量context.setVariable(param, paramValue);return getResult(context,spelExpression,clz);}/*** 解析spel表达式** @param param 参数名* @param paramValue 参数值* @param spelExpression  表达式* @param clz  返回结果的类型* @param defaultResult 默认结果* @return 执行spel表达式后的结果*/public static <T> T parse(String param, Object paramValue,String spelExpression, Class<T> clz, T defaultResult) {EvaluationContext context = new StandardEvaluationContext();//设置上下文变量context.setVariable(param, paramValue);T result = getResult(context,spelExpression,clz);if(Objects.isNull(result)){return defaultResult;}return result;}/*** 获取spel表达式后的结果** @param context 解析器上下文接口* @param spelExpression  表达式* @param clz  返回结果的类型* @return 执行spel表达式后的结果*/private static <T> T getResult(EvaluationContext context,String spelExpression, Class<T> clz){try {//解析表达式Expression expression = parseExpression(spelExpression);//获取表达式的值return expression.getValue(context, clz);} catch (Exception e) {log.error(e.getMessage(),e);}return null;}/*** 解析表达式* @param spelExpression spel表达式* @return*/private static Expression parseExpression(String spelExpression){// 如果表达式是一个#{}表达式,需要为解析传入模板解析器上下文if(spelExpression.startsWith(EXPRESSION_PREFIX) && spelExpression.endsWith(EXPRESSION_SUFFIX)){return expressionParser.parseExpression(spelExpression,new TemplateParserContext());}return expressionParser.parseExpression(spelExpression);}}

相关文章:

【SpringBoot应用篇】【AOP+注解】SpringBoot+SpEL表达式基于注解实现权限控制

【SpringBoot应用篇】【AOP注解】SpringBootSpEL表达式基于注解实现权限控制 Spring SpEL基本表达式类相关表达式表达式模板 SpEL表达式实现权限控制PreAuthAuthFunPreAuthAspectUserControllerSpelParserUtils Spring SpEL Spring 表达式语言 SpEL 是一种非常强大的表达式语言…...

Java研学-HTTP 协议

一 概述 1 概念和作用 概念&#xff1a;HTTP 是 HyperText Transfer Protocol (超文本传输协议)的简写&#xff0c;它是 TCP/IP 协议之上的一个应用层协议。简单理解就是 HTTP 协议底层是对 TCP/IP 协议的封装。   作用&#xff1a;用于规定浏览器和服务器之间数据传输的格式…...

差生文具多之(二): perf

栈回溯和符号解析是使用 perf 的两大阻力&#xff0c;本文以应用程序 fio 的观测为例子&#xff0c;提供一些处理它们的经验法则&#xff0c;希望帮助大家无痛使用 perf。 前言 系统级性能优化通常包括两个阶段&#xff1a;性能剖析和代码优化&#xff1a; 性能剖析的目标是寻…...

【SPI和API有什么区别】

✅什么是SPI&#xff0c;和API有什么区别 ✅典型解析&#x1f7e2;拓展知识仓&#x1f7e2;如何定义一个SPI&#x1f7e2;SPI的实现原理 ✅SPI的应用场景SpringDubbo ✅典型解析 Java 中区分 API和 SPI&#xff0c;通俗的进: API和 SPI 都是相对的概念&#xff0c;他们的差别只…...

Day67力扣打卡

打卡记录 美丽塔 II&#xff08;前缀和 单调栈&#xff09; 链接 class Solution:def maximumSumOfHeights(self, maxHeights: List[int]) -> int:n len(maxHeights)stack collections.deque()pre, suf [0] * n, [0] * nfor i in range(n):while stack and maxHeights…...

什么是网站监控?

网站监控是跟踪网站的可用性和性能&#xff0c;以最小化宕机时间&#xff0c;优化性能并确保顺畅的用户体验。维护网站正常运行对于任何企业来说都是至关重要的&#xff0c;因而对大多数业务来说&#xff0c;网站应用监控都是一个严峻的挑战。Applications Manager网站应用监控…...

游戏软件提示d3dcompiler_43.dll的五个解决方法,亲测靠谱

在使用电脑进行工作&#xff0c;玩游戏的时候&#xff0c;我们常常会遇到一些错误提示&#xff0c;其中之一就是“D3DCompiler_43.dll丢失”的提示。D3DCompiler_43.dll是一个非常重要的动态链接库文件。它是由DirectX SDK提供的&#xff0c;用于编译和优化DirectX着色器代码的…...

python使用opencv提取视频中的每一帧、最后一帧,并存储成图片

提取视频每一帧存储图片 最近在搞视频检测问题&#xff0c;在用到将视频分帧保存为图片时&#xff0c;图片可以保存&#xff0c;但是会出现(-215:Assertion failed) !_img.empty() in function cv::imwrite问题而不能正常运行&#xff0c;在检查代码、检查路径等措施均无果后&…...

说说对React refs 的理解?应用场景?

先了解&#xff0c;是什么&#xff1f; React 中的 Refs提供了一种方式&#xff0c;允许我们访问 DOM节点或在 render方法中创建的 React元素。 本质为ReactDOM.render()返回的组件实例&#xff0c;如果是渲染组件则返回的是组件实例&#xff0c;如果渲染dom则返回的是具体的do…...

Pytorch 读取t7文件

Pytorch 1.0以上可以使用&#xff1a; import torchfileth_path r"./path/xx.t7" data torchfile.load(th_path)print(data.shape)若data的尺寸为0&#xff0c;则将torch版本降为0.4.1&#xff0c;并使用以下函数&#xff1a; from torch.utils.serialization im…...

【YOLOV8预测篇】使用Ultralytics YOLO进行检测、分割、姿态估计和分类实践

目录 一 安装Ultralytics 二 使用预训练的YOLOv8n检测模型 三 使用预训练的YOLOv8n-seg分割模型 四 使用预训练的YOLOv8n-pose姿态模型 五 使用预训练的YOLOv8n-cls分类模型 <...

[Linux] MySQL数据库之索引

一、索引的相关知识 1.1 索引的简介 索引是一个排序列表&#xff0c;包含索引值和包含该值的数据行的物理地址&#xff08;类似于 c 语言链表&#xff0c;通过指针指向数据记录的内存地址&#xff09;。 使用索引后可以不用扫描全表来定位某行的数据&#xff0c;而是先通过索…...

【期末考试】计算机网络、网络及其计算 考试重点

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 计算机网络及其计算 期末考点 &#x1f680;数…...

力扣labuladong——一刷day79

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、力扣785. 判断二分图二、力扣886. 可能的二分法 前言 给你一幅「图」&#xff0c;请你用两种颜色将图中的所有顶点着色&#xff0c;且使得任意一条边的两个…...

【数据结构入门精讲 | 第十篇】考研408排序算法专项练习(二)

在上文中我们进行了排序算法的判断题、选择题的专项练习&#xff0c;在这一篇中我们将进行排序算法中编程题的练习。 目录 编程题R7-1 字符串的冒泡排序R7-1 抢红包R7-1 PAT排名汇总R7-2 统计工龄R7-1 插入排序还是堆排序R7-2 龙龙送外卖R7-3 家谱处理 编程题 R7-1 字符串的冒…...

【ES实战】Elasticsearch6开始的CCR

【ES实战】学习使用Elasticsearch6开始的CCR 本文涉及官网文章地址 OverviewRequirements for leader indicesAutomatically following indicesGetting started with cross-cluster replicationUpgrading clusters CCR > Cross-cluster replication 文章目录 【ES实战】学…...

Deployment Pay

axure watermark...

MySQL创建member表失败

最近在做一个项目&#xff0c;在台式机上可以跑通&#xff0c;也测试了各个已完成的接口&#xff0c;提交到了GitHub后想着用宿舍的电脑跑一下&#xff0c;在测试member表相关接口时就出错了。报了SQL语法错误&#xff0c;但SQL语句很简单&#xff0c;就根据手机号查询不至于出…...

使用minio实现大文件断点续传

部署 minio 拉取镜像 docker pull minio/minio docker images新建映射目录 新建下面图片里的俩个目录 data(存放对象-实际的数据) config 存放配置开放对应端口 我使用的是腾讯服务器所以 在腾讯的安全页面开启 9000&#xff0c;9090 两个端口就可以了&#xff08;根据大家实际…...

插入排序之C++实现

描述 插入排序是一种简单直观的排序算法。它的基本思想是将一个待排序的数据序列分为已排序和未排序两部分&#xff0c;每次从未排序序列中取出一个元素&#xff0c;然后将它插入到已排序序列的适当位置&#xff0c;直到所有元素都插入完毕&#xff0c;即完成排序。 实现思路…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡&#xff08;如 HAProxy、AWS NLB、阿里 SLB&#xff09;发起上游连接时&#xff0c;将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后&#xff0c;ngx_stream_realip_module 从中提取原始信息…...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

css3笔记 (1) 自用

outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size&#xff1a;0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格&#xff…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…...

GO协程(Goroutine)问题总结

在使用Go语言来编写代码时&#xff0c;遇到的一些问题总结一下 [参考文档]&#xff1a;https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现&#xff1a; 今天在看到这个教程的时候&#xff0c;在自己的电…...

在 Spring Boot 中使用 JSP

jsp&#xff1f; 好多年没用了。重新整一下 还费了点时间&#xff0c;记录一下。 项目结构&#xff1a; pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...

uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)

UniApp 集成腾讯云 IM 富媒体消息全攻略&#xff08;地理位置/文件&#xff09; 一、功能实现原理 腾讯云 IM 通过 消息扩展机制 支持富媒体类型&#xff0c;核心实现方式&#xff1a; 标准消息类型&#xff1a;直接使用 SDK 内置类型&#xff08;文件、图片等&#xff09;自…...