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

Spring Boot整合Spring Security

Spring Boot 专栏:Spring Boot 从零单排
Spring Cloud 专栏:Spring Cloud 从零单排
GitHub:SpringBootDemo
Gitee:SpringBootDemo

Spring Security是针对Spring项目的安全框架,也是Spring Boot底层安全模块的默认技术选型,仅需引入spring-boot-starter-security模块,进行少量配置,即可实现强大的Web安全控制。

Spring Security的两个主要目标是认证授权(访问控制)

官方文档:https://docs.spring.io/spring-security/site/docs/5.2.0.RELEASE/reference/htmlsingle/

0 开发环境

  • JDK:1.8
  • Spring Boot:2.7.18

Spring Boot 版本升级为2.7.18,专栏中其他Spring Boot相关环境同步升级

1 引入依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>

2 测试

2.1 新建Controller

@RestController
@RequestMapping("user")
public class UserController {@GetMapping(value = "query")public String query() {return "用户查询成功";}
}

2.2 测试

启动服务,浏览器访问 127.0.0.1:8090/user/query,页面自动跳转到授权登录页

在这里插入图片描述

默认用户名为user,控制台上会打印默认密码,默认密码每次启动服务都会刷新

在这里插入图片描述

登录成功后,就可以正常访问了

在这里插入图片描述

3 自定义密码

该部分会使用到Spring Security的几个关键类,如下:

  • WebSecurityConfigurerAdapter 自定义Security策略
  • AuthenticationManagerBuilder 自定义认证策略
  • @EnableWebSecurity 开启WebSecurity模式

3.1 通过application.yml 配置

spring:security:user:name: adminpassword: 123456

3.2 自定义配置类配置

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {//密码加密BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();//配置用户名、密码,该配置方式下,用户名和密码保存在内存中auth.inMemoryAuthentication()//密码加密方式.passwordEncoder(new BCryptPasswordEncoder()).withUser("admin").password(passwordEncoder.encode("123456")).roles("admin");}
}

3.3 自定义实现类配置

3.3.1 编写UserDetailsService实现类

这里我们就直接固定写死用户名和密码,实际生产中可以从数据库中获取

@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {//设置角色,角色的概念后续介绍List<GrantedAuthority> roles = AuthorityUtils.commaSeparatedStringToAuthorityList("admin");return new User("admin", new BCryptPasswordEncoder().encode("123456"), roles);}
}

3.3.2 编写配置类

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsServiceImpl userDetailsService;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {//使用UserDetailsServiceImpl 查询用户名、密码auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());}
}

分别测试,都通过

4 用户认证和授权/基于角色和权限的访问控制

实际生产中,需要根据用户角色的权限来控制可访问的页面、可执行的操作等

4.1 新建4个页面

level-1.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1 style="color: red">这是用户等级1可访问的页面</h1>
</body>
</html>

level-2.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1 style="color: green">这是用户等级2可访问的页面</h1>
</body>
</html>

level-3.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1 style="color: blue">这是用户等级3可访问的页面</h1>
</body>
</html>

index.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<a href="level-1.html">等级1</a><br><br>
<a href="level-2.html">等级2</a><br><br>
<a href="level-3.html">等级3</a>
</body>
</html>

4.2 修改配置类

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {//请求授权的规则//开启认证http.authorizeRequests()//首页所有人可访问.antMatchers("/").permitAll()//功能页对应角色或权限才能访问//hasRole 为角色授权,表示用户拥有指定角色//hasAuthority 为权限授权,表示用户拥有指定权限.antMatchers("/level-1.html").hasRole("level1").antMatchers("/level-2.html").hasRole("level2").antMatchers("/level-3.html").hasAuthority("level3");//开启登录,无权限时进入登录页面http.formLogin();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {//密码加密BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();//配置用户名、密码,该配置方式下,用户名和密码保存在内存中auth.inMemoryAuthentication()//密码加密方式.passwordEncoder(new BCryptPasswordEncoder()).withUser("admin").password(passwordEncoder.encode("123456")).roles("admin").and().withUser("admin1").password(passwordEncoder.encode("123456")).roles("level1").and().withUser("admin2").password(passwordEncoder.encode("123456")).roles("level2").and().withUser("admin3").password(passwordEncoder.encode("123456")).authorities("level3").and().withUser("admin0").password(passwordEncoder.encode("123456")).authorities("ROLE_level1", "ROLE_level2", "level3");}
}

**hasRole()hasAuthority()**用法是类似的,只不过hasRole()方法会给自定义的角色名前加上 ROLE_ 前缀

在这里插入图片描述

因此在自定义用户时,如果使用**authorities()给用户设置角色时,需要自行添加上ROLE_**前缀。

roles()authorities()设置的角色或权限,最终都存放在authorities参数中,且这两个方法会互相覆盖彼此的值。

在这里插入图片描述

4.3 测试

浏览器访问

在这里插入图片描述

依次点击等级1、等级2、等级3,均自动跳转到授权登录页面,登录对应权限的用户后,可成功访问。

其中,登录admin用户,无法访问任何页面,登录admin0,可访问所有页面

在这里插入图片描述

登录权限不匹配的用户,拒绝访问

在这里插入图片描述

4.4 使用UserDetailsService类实现

UserDetailsServiceImpl

@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {List<GrantedAuthority> roles;if ("admin1".equals(s)) {roles = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_level1");} else if ("admin2".equals(s)) {roles = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_level2");} else if ("admin3".equals(s)) {roles = AuthorityUtils.commaSeparatedStringToAuthorityList("level3");} else if ("admin0".equals(s)) {roles = AuthorityUtils.createAuthorityList("ROLE_level1", "ROLE_level2", "level3");} else {roles = AuthorityUtils.createAuthorityList("admin");}return new User(s, new BCryptPasswordEncoder().encode("123456"), roles);}
}

SecurityConfig

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsServiceImpl userDetailsService;@Overrideprotected void configure(HttpSecurity http) throws Exception {//请求授权的规则//开启认证http.authorizeRequests()//首页所有人可访问.antMatchers("/").permitAll()//功能页对应角色或权限才能访问//hasRole 为角色授权,表示用户拥有指定角色//hasAuthority 为权限授权,表示用户拥有指定权限.antMatchers("/level-1.html").hasRole("level1").antMatchers("/level-2.html").hasRole("level2").antMatchers("/level-3.html").hasAuthority("level3");//开启登录,无权限时进入登录页面http.formLogin();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {//使用UserDetailsServiceImpl 查询用户名、密码auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());}
}

浏览器访问,测试,通过

5 常用注解

可控制用户认证访问接口

5.1 @Secured()

校验用户具有某个角色,才可访问接口

需在启动类开启注解

@EnableGlobalMethodSecurity(securedEnabled = true)

然后在接口方法上配置注解

@RestController
@RequestMapping("user")
public class UserController {@GetMapping(value = "query")@Secured("ROLE_level1")public String query() {return "用户查询成功";}@GetMapping(value = "update")@Secured({"ROLE_level1", "ROLE_level2"})public String update() {return "用户更新成功";}
}

5.2 @PreAuthorize()

在进入方法前校验用户具有某个权限或角色

需在启动类开启注解

@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)

然后在接口方法上配置注解

@RestController
@RequestMapping("user")
public class UserController {@GetMapping(value = "delete")@PreAuthorize("hasAnyAuthority('ROLE_level1','level3')")public String delete() {return "用户删除成功";}
}

5.3 @PostAuthorize()

在进入方法后校验用户具有某个权限或角色

需在启动类开启注解

@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)

然后在接口方法上配置注解

@RestController
@RequestMapping("user")
public class UserController {@GetMapping(value = "delete")@PostAuthorize("hasAnyAuthority('ROLE_level1','level3')")public String delete() {return "用户删除成功";}
}

5.4 @PostFilter()

校验权限后对数据进行过滤,只返回满足条件的数据

新建实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserModel {private String username;private String password;
}

然后在方法上加上注解

@RestController
@RequestMapping("user")
public class UserController {@GetMapping(value = "queryList")@PreAuthorize("hasAnyAuthority('ROLE_level1','level3')")@PostFilter("filterObject.username == 'test'")public List<UserModel> queryList() {List<UserModel> userList = new ArrayList<>();userList.add(new UserModel("test", "qwerty"));userList.add(new UserModel("test2", "asdfgh"));userList.add(new UserModel("test3", "zxcvbn"));return userList;}
}

测试,权限验证通过后

在这里插入图片描述

5.5 @PreFilter()

校验权限后对数据进行过滤,只有满足条件的数据才能传入接口方法中

@RestController
@RequestMapping("user")
public class UserController {@PostMapping(value = "queryUser")@PreAuthorize("hasAnyAuthority('ROLE_level1','level3')")@PreFilter("filterObject.username == 'test2'")public List<UserModel> queryUser(@RequestBody List<UserModel> userModels) {return userModels;}
}

测试,权限验证通过后

在这里插入图片描述

6 记住我

配置类中开启记住我

    @Overrideprotected void configure(HttpSecurity http) throws Exception {//请求授权的规则//开启认证http.authorizeRequests()//首页所有人可访问.antMatchers("/").permitAll()//功能页对应角色或权限才能访问//hasRole 为角色授权,表示用户拥有指定角色//hasAuthority 为权限授权,表示用户拥有指定权限.antMatchers("/level-1.html").hasRole("level1").antMatchers("/level-2.html").hasRole("level2").antMatchers("/level-3.html").hasAuthority("level3");//开启登录,无权限时进入登录页面http.formLogin();//记住我http.rememberMe();}

启动服务,访问页面,登录页面增加了记住我选择框

在这里插入图片描述

登录成功后,cookie中已保存用户信息,默认时间为2周

在这里插入图片描述

7 注销

7.1 配置类中开启注销

    @Overrideprotected void configure(HttpSecurity http) throws Exception {//请求授权的规则//开启认证http.authorizeRequests()//首页所有人可访问.antMatchers("/").permitAll()//功能页对应角色或权限才能访问//hasRole 为角色授权,表示用户拥有指定角色//hasAuthority 为权限授权,表示用户拥有指定权限.antMatchers("/level-1.html").hasRole("level1").antMatchers("/level-2.html").hasRole("level2").antMatchers("/level-3.html").hasAuthority("level3");//开启登录,无权限时进入登录页面http.formLogin();//记住我http.rememberMe();//开启注销,注销成功后回首页http.logout().logoutSuccessUrl("/");}

7.2 level-* 页面增加注销按钮

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<h1 style="color: red">这是用户等级1可访问的页面</h1>
<br><br>
<a href="/logout">注销</a>
</body>
</html>

其他两个页面做相同修改

启动服务,登录成功后点击注销按钮,注销成功,返回首页,访问页面需再次登录

8 自定义登录页

8.1 新建登录页login.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><meta charset="UTF-8"><title>Login Page</title><style>body {font-family: Arial, sans-serif;background-color: #f5f5f5;}.container {width: 300px;margin: auto;padding: 40px;border: 1px solid #ccc;background-color: white;box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);}h2 {text-align: center;}label {display: block;margin-bottom: 10px;}input[type="text"],input[type="password"] {width: 100%;padding: 6px;border: 1px solid #ccc;outline: none;}button {width: 100%;padding: 10px;color: white;background-color: #4CAF50;cursor: pointer;border: none;outline: none;}button:hover {opacity: 0.9;}</style>
</head>
<body>
<div class="container"><h2>登录</h2><form action="/login" method="post"><label for="username">用户名</label><input type="text" id="username" name="username"><br><br><label for="password">密码</label><input type="password" id="password" name="password"><br><br><input type="checkbox" name="remember-me" title="记住我">记住我<br><br><button type="submit">登 录</button></form>
</div>
</body>
</html>

8.2 配置类中开启自定义登录页

    @Overrideprotected void configure(HttpSecurity http) throws Exception {//请求授权的规则//开启认证http.authorizeRequests()//首页所有人可访问.antMatchers("/").permitAll()//功能页对应角色或权限才能访问//hasRole 为角色授权,表示用户拥有指定角色//hasAuthority 为权限授权,表示用户拥有指定权限.antMatchers("/level-1.html").hasRole("level1").antMatchers("/level-2.html").hasRole("level2").antMatchers("/level-3.html").hasAuthority("level3");//开启登录,无权限时进入登录页面//自定义登录页http.formLogin().loginPage("/login.html").loginProcessingUrl("/login");//关闭csrf防护http.csrf().disable();//记住我http.rememberMe();//开启注销,注销成功后回首页http.logout().logoutSuccessUrl("/");}

这里,自定义登录,默认用户参数是username,默认密码参数是password,默认记住我参数是remember-me,如果需要自定义登录表单的参数,做如下修改

        //开启登录,无权限时进入登录页面http.formLogin().loginPage("/login.html").loginProcessingUrl("/login").usernameParameter("username").passwordParameter("password");//关闭csrf防护http.csrf().disable();//记住我http.rememberMe().rememberMeParameter("remember-me");

启动服务,访问地址,跳转到自定义登录页

在这里插入图片描述

9 自定义403页面

9.1 新建403.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
权限不足,无法访问
</body>
</html>

9.2 配置类中开启自定义403页面

    @Overrideprotected void configure(HttpSecurity http) throws Exception {//...其他代码...//自定义403页面http.exceptionHandling().accessDeniedPage("/403.html");}

启动服务,浏览器访问,登录无权限用户后提示

在这里插入图片描述

至此,Spring Boot整合Spring Security实现用户认证和授权基本用法已讲解完毕,且测试通过。

相关文章:

Spring Boot整合Spring Security

Spring Boot 专栏&#xff1a;Spring Boot 从零单排 Spring Cloud 专栏&#xff1a;Spring Cloud 从零单排 GitHub&#xff1a;SpringBootDemo Gitee&#xff1a;SpringBootDemo Spring Security是针对Spring项目的安全框架&#xff0c;也是Spring Boot底层安全模块的默认技术…...

Rust字符串深入理解

一、概述 Rust是一种系统级语言&#xff0c;进行操作系统等底层应用开发&#xff0c;同时又具合理的抽象处理能力。在进行Rust编程时&#xff0c;字符串处理是程序员经常碰到的工作。本文深入解析Rust语言中字符串的使用&#xff0c;包括 static string&#xff0c;String与&a…...

TSINGSEE青犀AI智能分析网关V4酿酒厂安全挂网AI检测算法

在酿酒行业中&#xff0c;安全生产一直是企业经营中至关重要的一环。为了确保酒厂生产过程中的安全&#xff0c;TSINGSEE青犀AI智能分析网关V4的安全挂网AI检测算法发挥了重要作用。 TSINGSEE青犀AI智能分析网关V4的安全挂网检测算法是针对酒厂里酒窖挂网行为进行智能检测与识…...

LeetCode第126场双周赛个人题解

目录 100262. 求出加密整数的和 原题链接 思路分析 AC代码 3080. 执行操作标记数组中的元素 原题链接 思路分析 AC代码 100249. 替换字符串中的问号使分数最小 原题链接 思路分析 AC代码 100241. 求出所有子序列的能量和 原题链接 思路分析 AC代码 100262. 求出…...

牛客NC403 编辑距离为一【中等 模拟法 Java,Go,PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/0b4b22ae020247ba8ac086674f1bd2bc 思路 注意&#xff1a;必须要新增一个&#xff0c;或者删除一个&#xff0c;或者替换一个&#xff0c;所以不能相等1.如果s和t相等&#xff0c;返回false,如果s和t长度差大于1…...

C# SetWindowPos函数

在C#中&#xff0c;SetWindowPos函数用于设置窗口的位置和大小。 原型&#xff1a; [DllImport("user32.dll", SetLastError true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int …...

zookeeper快速入门五:用zookeeper实现服务注册与发现中心

系列&#xff1a; zookeeper快速入门一&#xff1a;zookeeper安装与启动-CSDN博客 zookeeper快速入门二&#xff1a;zookeeper基本概念-CSDN博客 zookeeper快速入门三&#xff1a;zookeeper的基本操作 zookeeper快速入门四&#xff1a;在java客户端中操作zookeeper-CSDN博客…...

Java 中 BitSet 类的用法

Java 中 BitSet 类的用法 API构造置位为 true清除为 false查找位反转长度运算流其他 原理底层数据结构如何工作 API 构造 无参构造 &#xff1a;默认为 64 个 bit 的容量 BitSet bitset new BitSet();有参构造 &#xff1a;设置为 n 个 bit 的容量 BitSet bitset new BitSe…...

Jenkins-pipeline流水线构建完钉钉通知

添加钉钉机器人 在钉钉群设置里添加机器人拿出Webhook地址&#xff0c;设置关键词 Jenkins安装钉钉插件 Dashboard > 系统管理 > 插件管理&#xff0c;搜索构建通知&#xff0c;直接搜索Ding Talk也行 安装DingTalk插件&#xff0c;重启Jenkins 来到Dashboard > 系…...

汽车制造业供应商管理会面临哪些问题?要如何解决?

汽车行业的供应链是及其复杂的&#xff0c;并且呈全球化分布&#xff0c;企业在知识产权方面的优势很可能是阶段性的。企业需要持续保持领先&#xff0c;将面临巨大的挑战&#xff0c;尽快地将产品推向市场是保持领先的唯一途径。然而&#xff0c;如果没有正确的方式去实现安全…...

day28|93. 复原 IP 地址|Leetcode 78. 子集|90.子集II

Leetcode 93. 复原 IP 地址 链接&#xff1a;93. 复原 IP 地址 class Solution { public:vector<string> res;string path;int pointNum 0;vector<string> restoreIpAddresses(string s) {backtracking(0, s);return res;}void backtracking(int start, string …...

怎样提升小程序日活?签到抽奖可行吗?

一、 日活运营策略 小程序应该是即用即走的&#xff0c;每个小程序都在用户中有自己的独特定位&#xff0c;可能是生活日常必备&#xff08;美食、团购、商城&#xff09;&#xff0c;也可能是工作办公必备&#xff08;文档、打卡、工具&#xff09;。 如果你想要让自己的小程…...

hive语法树分析,判断 sql语句中有没有select *

pom依赖参考以下博文java 通过 IMetaStoreClient 取 hive 元数据信息-CSDN博客1 节点处理器类 import lombok.Getter; import org.apache.hadoop.hive.ql.lib.Dispatcher; import org.apache.hadoop.hive.ql.lib.Node; import org.apache.hadoop.hive.ql.parse.ASTNode; impor…...

【论文阅读】MSGNet:学习多变量时间序列预测中的多尺度间序列相关性

MSGNet&#xff1a;学习多变量时间序列预测中的多尺度间序列相关性 文献介绍摘要总体介绍背景及当前面临的问题现有解决方案及其局限性本文的解决方案及其贡献 背景知识的相关工作背景知识问题表述&#xff1a; Method论文主要工作1.输入嵌入和剩余连接 (Input Embedding and R…...

智慧城市与数字孪生:共创未来城市的智慧生活

目录 一、智慧城市与数字孪生的概念与特点 二、智慧城市与数字孪生共创智慧生活的路径 1、城市规划与建设的智能化 2、城市管理与服务的智慧化 3、城市安全与应急管理的智能化 三、智慧城市与数字孪生面临的挑战与对策 四、智慧城市与数字孪生的发展趋势与展望 1、技术…...

【Ubuntu】FTP站点搭建

配置顺序 前提条件&#xff1a;确保软件仓库可以正常使用&#xff0c;确保已正常配置IP地址 1.安装FTP服务 2.编辑FTP配置文件 3.设置开机自启 4.创建用户 5.配置用户限制名单 6.重启服务 7.查看运行状态 8.测试在同一局域网下的Windows查看文件 1.安装FTP服务 sudo apt insta…...

RK3228H is the same SoC as rk3328.

RK3228H is the same SoC as rk3328....

Golang 开发实战day04 - Standard Library

Golang 开发实战day04 - Standard Library 接下来开始我们第四天学习&#xff0c;Go语言标准库提供了丰富的功能&#xff0c;可以帮助开发者快速完成各种任务。 golang就像其他语言一样&#xff0c;附带了一些非常轻量级的函数和特性&#xff0c;都是开箱即用的&#xff0c;这里…...

程序员排查BUG指南

程序员排查BUG&#xff08;错误&#xff09;是软件开发过程中的重要一环, 以下是一份程序员排查BUG的指南&#xff0c;帮助你更有效地识别、定位和修复问题&#xff1a; 1、重现BUG&#xff1a;确保能够准确地重现BUG&#xff0c;这是解决问题的第一步。尽量记录重现BUG的步骤。…...

【Vue】elementUI-MessageBox组件相关

官方代码&#xff1a; <template><el-button type"text" click"open">点击打开 Message Box</el-button> </template><script>export default {methods: {open() {this.$confirm(此操作将永久删除该文件, 是否继续?, 提示…...

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表&#xff1f;1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具

作者&#xff1a;来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗&#xff1f;了解下一期 Elasticsearch Engineer 培训的时间吧&#xff01; Elasticsearch 拥有众多新功能&#xff0c;助你为自己…...

Docker 运行 Kafka 带 SASL 认证教程

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

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

JAVA后端开发——多租户

数据隔离是多租户系统中的核心概念&#xff0c;确保一个租户&#xff08;在这个系统中可能是一个公司或一个独立的客户&#xff09;的数据对其他租户是不可见的。在 RuoYi 框架&#xff08;您当前项目所使用的基础框架&#xff09;中&#xff0c;这通常是通过在数据表中增加一个…...

wpf在image控件上快速显示内存图像

wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像&#xff08;比如分辨率3000*3000的图像&#xff09;的办法&#xff0c;尤其是想把内存中的裸数据&#xff08;只有图像的数据&#xff0c;不包…...

Vue 模板语句的数据来源

&#x1f9e9; Vue 模板语句的数据来源&#xff1a;全方位解析 Vue 模板&#xff08;<template> 部分&#xff09;中的表达式、指令绑定&#xff08;如 v-bind, v-on&#xff09;和插值&#xff08;{{ }}&#xff09;都在一个特定的作用域内求值。这个作用域由当前 组件…...