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

spring boot集成mybatis和springsecurity实现权限控制功能

上一篇已经实现了登录认证功能,这一篇继续实现权限控制功能,文中代码只贴出来和上一篇不一样的修改的地方,完整代码可结合上一篇一起整理spring boot集成mybatis和springsecurity实现登录认证功能-CSDN博客

数据库建表

权限控制的意思就是根据用户角色的不同,赋予不同的访问权限,比如管理员可以对数据进行增删改查操作,而普通用户只能查询数据。根据网上大部分文章的说法,最好的授权方式叫RABC,意思是基于资源的访问控制,详见SpringSecurity授权-CSDN博客

这种方式会创建5个数据库表,分别为用户表,角色表,权限表,用户角色关系表,角色权限关系表,他们之间存在一些对应关系,每次根据用户名可以查询到这个用户的角色和权限信息,我也不懂为啥要把授权相关的表拆成5个,按照我自己的理解,就是把所有用户、角色、权限信息放在一张表里也不是不行,但是既然大家都建议这么做,肯定是有道理的,所以我就按照主流方式来实现权限控制的数据库表创建

数据库建表和数据SQL如下:

-- test.permission definition权限信息表 
CREATE TABLE `permission` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `permission` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='权限信息表'; 
INSERT INTO test.permission (name,permission) VALUES ('selectinfo','url1'), ('insertinfo','url2'), ('updateinfo','url3'), ('deleteinfo','url4'); 
-- test.`role` definition角色信息表 
CREATE TABLE `role` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `description` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1; 
INSERT INTO test.`role` (name,description) VALUES ('admin','admin'), ('user','user');
-- test.`user` definition用户信息表 
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) DEFAULT NULL, `password` varchar(255) DEFAULT NULL, `permission` varchar(255) DEFAULT NULL, `role` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1; 
INSERT INTO test.`user` (username,password,permission,`role`) VALUES ('aaa','$2a$10$m3tlFCB8IiZzm6dj7Q58KuD9FnkZI8M/1scJ2dZBZwiToFpgRMZCe',NULL,NULL), ('bbb','$2a$10$Wwfn31iTecY0n66LHXNSeeiPTcr.ZJsDHVuvnHR150rMqgJXugio2',NULL,NULL), ('ccc','1234',NULL,NULL); 
-- test.role_permission definition角色权限关系表 
CREATE TABLE `role_permission` ( `id` int(11) NOT NULL AUTO_INCREMENT, `role_id` varchar(255) DEFAULT NULL, `permission_id` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; 
INSERT INTO test.role_permission (role_id,permission_id) VALUES ('1','1'), ('1','2'), ('1','3'), ('1','4'), ('2','1'), ('2','3'); 
-- test.user_role definition用户角色关系表 
CREATE TABLE `user_role` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` varchar(255) DEFAULT NULL, `role_id` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; 
INSERT INTO test.user_role (user_id,role_id) VALUES ('1','1'), ('1','2'), ('2','2');

所有和5个数据库表相关的dao、dto、service、mapper文件可参考上一篇user表编写,这里不在贴出源码

代码修改
1,springsecurity配置类

WebSecurityConfig添加支持权限控制的注解如下:

其中第一个配置项perPostEnable表示开启@PreAuthroize注解,是方法或类级别的注解,只需要在方法上添加@PreAuthroize注解即可,可以在资源被访问之前判断用户权限,是比较常用的方法,比如

图中注解就表示只有具备url1权限的用户才能执行方法queryName()。

第二个配置项securedEnable是专门用于判断是否具有角色的,可以在方法或者类上使用,参数需要以ROLE_开头,本文未使用所以忽略

2,UserDetailsServiceImpl

该方法实现了框架的UserDetailsService接口,并复写了loadUserByUsername方法,用于查询数据库的用户信息,添加权限控制功能后,需要同时查询用户的权限信息,代码如下:

package com.sgp.ss.security;import com.sgp.ss.dao.IUserMapper;
import com.sgp.ss.domain.entity.PermissionEntity;
import com.sgp.ss.domain.entity.UserEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.List;/*** @author shanguangpu* @date 2023/11/30 15:50*/
@Component
public class UserDetailsServiceImpl implements UserDetailsService {@AutowiredIUserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {try {UserEntity userEntityByName = userMapper.getUserEntityByName(username);if (userEntityByName == null){throw new UsernameNotFoundException("用户"+username+"不存在");}List<PermissionEntity> permissionList = userMapper.findPermissionByUsername(username);//查询用户权限信息List<GrantedAuthority> grantedAuthorities=new ArrayList<>();for (PermissionEntity p : permissionList){grantedAuthorities.add(new SimpleGrantedAuthority(p.getPermission()));}return new LoginUser(userEntityByName,grantedAuthorities);//测试用
//            List<String> list = new ArrayList<>(Arrays.asList("test"));
//            List<GrantedAuthority> grantedAuthorities=new ArrayList<>();
//            for(String s:list){
//                grantedAuthorities.add(new SimpleGrantedAuthority(s));
//            }
//            return new LoginUser(userEntityByName,list);} catch (Exception e){e.printStackTrace();}return null;}
}

其中方法findPermissionByUsername()是我们新加的,需要在mapper文件中编写SQL,实现根据用户名查询用户权限的功能,这个SQL语句可以按照下述格式直接写,如果数据库表名称不同,只需要修改表名就行

<select id="findPermissionByUsername" resultType="com.sgp.ss.domain.entity.PermissionEntity" parameterType="java.lang.String"><if test="_parameter != null">SELECT DISTINCT permission.id,permission.name,permission.permission FROMuserLEFT JOIN user_role on user.id = user_role.user_idLEFT JOIN role on user_role.role_id = role.idLEFT JOIN role_permission on role.id = role_permission.role_idLEFT JOIN permission on role_permission.permission_id = permission.idwhere user.username = #{username} and 1 = 1<!--select <include refid="UserEntityColumns"/> from user--><!--where username = #{username} and 1 = 1--></if></select>
3,LoginUser 

从上述代码中可以看出,当我们从数据库查询完用户权限信息后,框架会将它封装到用户对象UserDetails中,代码如下:

package com.sgp.ss.security;import com.sgp.ss.domain.entity.UserEntity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.*;
import java.util.stream.Collectors;/*** @author shanguangpu* @date 2023/11/30 15:58*/
public class LoginUser extends UserEntity implements UserDetails {Collection<? extends GrantedAuthority> authorities;private List<String> permissions = new ArrayList<>();public LoginUser(UserEntity userEntity){if (null != userEntity){this.setUsername(userEntity.getUsername());this.setPassword(userEntity.getPassword());}}public LoginUser(UserEntity userEntity, Collection<? extends GrantedAuthority> authorities){this.setId(userEntity.getId());this.setUsername(userEntity.getUsername());this.setPassword(userEntity.getPassword());
//        permissions.add(userEntity.getPermission());this.authorities = authorities;}public LoginUser(UserEntity userEntity, List<String> permissions){this.setId(userEntity.getId());this.setUsername(userEntity.getUsername());this.setPassword(userEntity.getPassword());this.permissions = permissions;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {if (authorities != null){return authorities;}authorities = permissions.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());return authorities;}/*** 账户是否过期* @return*/@Overridepublic boolean isAccountNonExpired() {return true;}/*** 账户是否被锁定* @return*/@Overridepublic boolean isAccountNonLocked() {return true;}/*** 证书是否过期* @return*/@Overridepublic boolean isCredentialsNonExpired() {return true;}/*** 账户是否有效* @return*/@Overridepublic boolean isEnabled() {return true;}
}
4,JwtAuthenticationTokenFilter

下次访问资源只需要从token中解析出权限信息即可,所以需要在JWT过滤器中添加token解析步骤,并交给框架进行权限比对,存在上下文中

package com.sgp.ss.security;import com.sgp.ss.domain.entity.UserEntity;
import com.sgp.ss.service.UserEntityService;
import com.sgp.ss.util.JwtTokenUtil;
import io.jsonwebtoken.ExpiredJwtException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;/*** @author shanguangpu* @date 2023/12/8 15:29*/
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@Autowiredprivate UserEntityService userEntityService;@Autowiredprivate JwtTokenUtil jwtTokenUtil;//    @Autowired
//    private JwtProperties jwtProperties;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {//获取token信息final String authorization = request.getHeader("token");String name = null;String authToken = null;if (!StringUtils.isEmpty(authorization)) {authToken = authorization.replace("Authorization", "");try {name = jwtTokenUtil.getUsernameFromToken(authToken);} catch (ExpiredJwtException e){e.printStackTrace();}}if (name != null && SecurityContextHolder.getContext().getAuthentication() == null){
//            if (jwtTokenUtil.isTokenValid(name, authToken)){UserEntity userEntityByName = userEntityService.getUserEntityByName(name);List<GrantedAuthority> authorityFromToken = jwtTokenUtil.getAuthorityFromToken(authToken);UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userEntityByName, null, authorityFromToken);SecurityContextHolder.getContext().setAuthentication(authentication);
//            }}filterChain.doFilter(request, response);}public JwtAuthenticationTokenFilter(){super();}
}
5,JWT工具类

然后在JWT工具类中实现权限获取方法,完整代码如下:

package com.sgp.ss.util;import com.sgp.ss.security.LoginUser;
import io.jsonwebtoken.*;
import io.jsonwebtoken.impl.DefaultClock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;import java.io.Serializable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;/*** @author shanguangpu* @date 2023/2/22 10:42*/
@Component
public class JwtTokenUtil implements Serializable {private static final Logger LOG = LoggerFactory.getLogger(JwtTokenUtil.class);private static final long serialVersionUID = -1264536648286114018L;private Clock clock = DefaultClock.INSTANCE;private Map<String, String> tokenMap = new ConcurrentHashMap<>(32);public String generateToken(LoginUser loginUser){Map<String, Object> claims = new HashMap<>();claims.put("username", loginUser.getUsername());claims.put("authority", loginUser.getAuthorities());String token = generateToken(claims, loginUser.getId() + "");if (! StringUtils.isEmpty(token)){}return token;}/*** 生成token* @param claims* @param subject* @return*/private String generateToken(Map<String, Object> claims, String subject) {final Date createdDate = clock.now();final Date expirationDate = generateExpirationDate(createdDate);return Jwts.builder()// 自定义属性.setClaims(claims).setSubject(subject)// 创建时间.setIssuedAt(createdDate)// 过期时间.setExpiration(expirationDate)// 签名算法及秘钥.signWith(SignatureAlgorithm.HS512, "sgpsgp").compact();}/*** 设置过期时间,以当前时间加上毫秒数计算,该方法中设置的过期时间是6小时* @param createdDate* @return*/private Date generateExpirationDate(Date createdDate) {return new Date(createdDate.getTime() + 21600 * 1000);}// 从得到的令牌里面获取用户IDpublic Integer getUserIdFromToken(String token) {Integer id = null;try {final Claims claims = getClaimsFromToken(token);id = Integer.parseInt(claims.getSubject());return id;} catch (Exception e) {}return id;}// 从得到的令牌里面获取用户名public String getUsernameFromToken(String token) {String username;try {final Claims claims = getClaimsFromToken(token);username = (String) claims.get("username");} catch (Exception e) {username = null;}return username;}// 从得到的令牌里面获取权限public List<GrantedAuthority> getAuthorityFromToken(String token) {List<GrantedAuthority> list = new ArrayList<>();try {final Claims claims = getClaimsFromToken(token);StringBuffer sb = new StringBuffer();List<LinkedHashMap> authority2 = (List<LinkedHashMap>) claims.get("authority");for (LinkedHashMap s : authority2){String authority = (String) s.get("authority");sb.append(authority+",");list.add(new SimpleGrantedAuthority(authority));}LOG.info("当前用户名称是:{},权限url为:{}",claims.get("username"),sb.subSequence(0,sb.length()-1));return list;} catch (Exception e) {e.printStackTrace();}return list;}// 从得到的令牌里面获取创建时间public Date getCreatedDateFromToken(String token) {Date created;try {final Claims claims = getClaimsFromToken(token);created = claims.getIssuedAt();} catch (Exception e) {created = null;}return created;}// 从得到的令牌里面获取过期时间public Date getExpirationDateFromToken(String token) {Date expiration;try {final Claims claims = getClaimsFromToken(token);expiration = claims.getExpiration();} catch (Exception e) {expiration = null;}return expiration;}private Claims getClaimsFromToken(String token) {Claims claims;try {claims = Jwts.parser().setSigningKey("sgpsgp").parseClaimsJws(token).getBody();} catch (Exception e) {claims = null;}return claims;}/*** 检查token是否过期* @param token* @return*/public Boolean isTokenExpired(String token) {final Date expiration = getExpirationDateFromToken(token);boolean flag = expiration.before(new Date());if(flag) {String username = getUsernameFromToken(token);if(username != null) {tokenMap.remove(username);}}return flag;}public Boolean isTokenValid(String username, String token) {if(tokenMap.get(username) != null && tokenMap.get(username).equals(token.trim())) {return !isTokenExpired(token);}return false;}public Boolean isTokenValid(int id, String token) {if (tokenMap.get(id) != null && tokenMap.get(id).equals(token.trim())) {return !isTokenExpired(token);}return false;}public void deleteToken(String token) {String username = getUsernameFromToken(token);if(username != null) {tokenMap.remove(username);}}}}
6,controller

controller文件中可以多写几个方法,分别给与不同的权限,代码如下:

package com.sgp.ss.controller;import com.sgp.ss.dao.*;
import com.sgp.ss.domain.dto.data.*;
import com.sgp.ss.domain.entity.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;/*** @author shanguangpu* @date 2023/12/12 14:59*/
@RestController
public class index {@Autowiredprivate IUserMapper userMapper;@Autowiredprivate IRoleMapper roleMapper;@Autowiredprivate IPermissionMapper permissionMapper;@Autowiredprivate IUserRoleMapper userRoleMapper;@Autowiredprivate IRolePermissionMapper rolePermissionMapper;@GetMapping(value = "hello")public String request(){System.out.println("hello word");return "hello success";}@PostMapping(value = "select")@PreAuthorize("hasAuthority('url2')")public String selectInfo(){List<UserEntity> userEntities = userMapper.queryUserEntityList(new UserDto());return userEntities.toString();}@PostMapping(value = "queryName")@PreAuthorize("hasAuthority('url1')")public String queryName(){List<UserEntity> userEntities = userMapper.queryUserEntityList(new UserDto());int i = 1;for (UserEntity userEntity : userEntities){String username = userEntity.getUsername();System.out.println("name"+i+" is : "+username);i++;}return "query finish";}@PostMapping(value = "queryTable")public String queryRoleAndPermission(){List<RoleEntity> roleEntities = roleMapper.queryRoleEntityList(new RoleDto());for (RoleEntity roleEntity : roleEntities){System.out.println(roleEntity.getId()+","+roleEntity.getName()+","+roleEntity.getDescription());}List<PermissionEntity> permissionEntities = permissionMapper.queryPermissionEntityList(new PermissionDto());for (PermissionEntity permissionEntity : permissionEntities){System.out.println(permissionEntity.getId()+","+permissionEntity.getName()+","+permissionEntity.getPermission());}List<RolePermissionEntity> rolePermissionEntities = rolePermissionMapper.queryRolePermissionEntityList(new RolePermissionDto());for (RolePermissionEntity rolePermissionEntity : rolePermissionEntities){System.out.println(rolePermissionEntity.getId()+","+rolePermissionEntity.getPermission_id()+","+rolePermissionEntity.getRole_id());}List<UserRoleEntity> userRoleEntities = userRoleMapper.queryUserRoleEntityList(new UserRoleDto());for (UserRoleEntity userRoleEntity: userRoleEntities){System.out.println(userRoleEntity.getId()+","+userRoleEntity.getRole_id()+","+userRoleEntity.getUser_id());}List<PermissionEntity> bbb = userMapper.findPermissionByUsername("bbb");for (PermissionEntity p : bbb){System.out.println(p.getId()+","+p.getName()+","+p.getPermission());}return "success";}}
postman测试
1,获取token

首先我们通过用户名获取到token

2,具备权限测试

然后携带这个token,访问接口/queryTable,这个接口可以查询出数据库里所有相关的数据,以及用户bbb的权限列表打印在IDEA控制台

可以看到,用户bbb具备访问url1和url3的权限,那么我们继续测试接口/queryName,这个接口上方注解标注了需要具备url1权限才能访问,如下

3,不具备权限测试

然后测试接口/select,该接口上方注解表示只有具备url2的权限才能访问,用户bbb不具备这个权限,所以访问会失败,如下

至此,权限控制功能测试完毕

相关文章:

spring boot集成mybatis和springsecurity实现权限控制功能

上一篇已经实现了登录认证功能&#xff0c;这一篇继续实现权限控制功能&#xff0c;文中代码只贴出来和上一篇不一样的修改的地方&#xff0c;完整代码可结合上一篇一起整理spring boot集成mybatis和springsecurity实现登录认证功能-CSDN博客 数据库建表 权限控制的意思就是根…...

按键修饰符

在键盘监听事件时&#xff0c;我们经常需要判断详细的按键&#xff0c;此时&#xff0c;可以为键盘相关的事件添加按键修饰符&#xff0c;例如&#xff1a; 键盘修饰符案例&#xff1a;...

新版IDEA中Git的使用(一)

说明&#xff1a;本文介绍如何在新版IDEA中使用Git 创建项目 首先&#xff0c;在GitLab里面创建一个项目&#xff08;git_demo&#xff09;&#xff0c;克隆到桌面上。 然后在IDEA中创建一个项目&#xff0c;项目路径放在这个Git文件夹里面。 Git界面 当前分支&Commit …...

【性能测试】真实企业,性能测试流程总结分析(一)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 性能测试什么时候…...

20231224解决outcommit_id.xml1 parser error Document is empty的问题

20231224解决outcommit_id.xml1 parser error Document is empty的问题 2023/12/24 18:13 在开发RK3399的Android10的时候&#xff0c;出现&#xff1a;rootrootrootroot-X99-Turbo:~/3TB/Rockchip_Android10.0_SDK_Release$ make installclean PLATFORM_VERSION_CODENAMEREL…...

电子电器架构刷写方案——General Flash Bootloader

电子电器架构刷写方案——General Flash Bootloader 我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 注&#xff1a;文章1万字左右&#xff0c;深度思考者入&#xff01;&#xff01;&#xff01; 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免…...

【Linux】僵尸与孤儿 进程等待

目录 一&#xff0c;僵尸进程 1&#xff0c;僵尸进程 2&#xff0c;僵尸进程的危害 二&#xff0c;孤儿进程 1&#xff0c;孤儿进程 三&#xff0c;进程等待 1&#xff0c;进程等待的必要性 2&#xff0c;wait 方法 3&#xff0c;waitpid 方法 4&#xff0c;回收小结…...

Java小案例-Sentinel的实现原理

前言 Sentinel是阿里开源的一款面向分布式、多语言异构化服务架构的流量治理组件。 主要以流量为切入点&#xff0c;从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。 核心概念 要想理解一个新的技…...

【Leetcode Sheet】Weekly Practice 21

Leetcode Test 1901 寻找峰值Ⅱ(12.19) 一个 2D 网格中的 峰值 是指那些 严格大于 其相邻格子(上、下、左、右)的元素。 给你一个 从 0 开始编号 的 m x n 矩阵 mat &#xff0c;其中任意两个相邻格子的值都 不相同 。找出 任意一个 峰值 mat[i][j] 并 返回其位置 [i,j] 。 …...

C语言使用qsort和bsearch实现二分查找

引言 在计算机科学领域&#xff0c;查找是一项基本操作&#xff0c;而二分查找是一种高效的查找算法。本博客将详细解释一个简单的C语言程序&#xff0c;演示如何使用标准库函数qsort和bsearch来对一个整数数组进行排序和二分查找。 代码解析 包含头文件 #include <stdi…...

MySQL的替换函数及补全函数的使用

前提&#xff1a; mysql的版本是8.0以下的。不支持树形结构递归查询的。但是&#xff0c;又想实现树形结构的一种思路 提示&#xff1a;如果使用的是MySQL8.0及其以上的&#xff0c;想要实现树形结构&#xff0c;请参考&#xff1a;MySQL数据库中&#xff0c;如何实现递归查询…...

2022第十二届PostgreSQL中国技术大会-核心PPT资料下载

一、峰会简介 本次大会以“突破•进化•共赢 —— 安全可靠&#xff0c;共建与机遇”为主题&#xff0c;助力中国数据库基础软件可掌控、可研究、可发展、可生产&#xff0c;并推动数据库生态的繁荣与发展。大会为数据库从业者、数据库相关企业、数据库行业及整个IT产业带来崭…...

2024 年 10大 AI 趋势

2025 年&#xff0c;全球人工智能市场预计将达到惊人的 1906.1 亿美元&#xff0c;年复合增长率高达 36.62%。 人工智能软件正在迅速改变我们的世界&#xff0c;而且这种趋势在未来几年只会加速。 我们分析了未来有望彻底改变 2024 年的 10 个AI趋势。从生成式人工智能的兴起到…...

Uboot

什么是Bootloader? Linux系统要启动就必须需要一个 bootloader程序&#xff0c;也就说芯片上电以后先运行一段bootloader程序。 这段 **bootloader程序会先初始化时钟&#xff0c;看门狗&#xff0c;中断&#xff0c;SDRAM&#xff0c;等外设&#xff0c;然后将 Linux内核从f…...

ECMAScript 的未来:预测 JavaScript 创新的下一个浪潮

以下是简单概括关于JavaScript知识点以及一些目前比较流行的比如&#xff1a;es6 想要系统学习&#xff1a; 大家有关于JavaScript知识点不知道可以去 &#x1f389;博客主页&#xff1a;阿猫的故乡 &#x1f389;系列专栏&#xff1a;JavaScript专题栏 &#x1f389;ajax专栏&…...

代码随想录算法训练营第十三天 | 239. 滑动窗口最大值、347.前 K 个高频元素

239. 滑动窗口最大值 题目链接&#xff1a;239. 滑动窗口最大值 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 文章讲解…...

推荐五个免费的网络安全工具

导读&#xff1a; 在一个完美的世界里&#xff0c;信息安全从业人员有无限的安全预算去做排除故障和修复安全漏洞的工作。但是&#xff0c;正如你将要学到的那样&#xff0c;你不需要无限的预算取得到高质量的产品。这里有SearchSecurity.com网站专家Michael Cobb推荐的五个免费…...

Cross-Drone Transformer Network for Robust Single Object Tracking论文阅读笔记

Cross-Drone Transformer Network for Robust Single Object Tracking论文阅读笔记 Abstract 无人机在各种应用中得到了广泛使用&#xff0c;例如航拍和军事安全&#xff0c;这得益于它们与固定摄像机相比的高机动性和广阔视野。多无人机追踪系统可以通过从不同视角收集互补的…...

【LeetCode刷题笔记】动态规划(二)

647. 回文子串 解题思路: 1. 暴力穷举 , i 遍历 [0, N) , j 遍历 [i+1, N] ,判断每一个子串 s[i, j) 是否是回文串,判断是否是回文串可以采用 对撞指针 的方法。如果是回文串就计数 +1...

(十七)Flask之大型项目目录结构示例【二扣蓝图】

大型项目目录结构&#xff1a; 问题引入&#xff1a; 在上篇文章讲蓝图的时候我给了一个demo项目&#xff0c;其中templates和static都各自只有一个&#xff0c;这就意味着所有app的模板和静态文件都放在了一起&#xff0c;如果项目比较大的话&#xff0c;这就非常乱&#xf…...

蓝牙技术在物联网中的应用

随着蓝牙技术的不断演进和发展&#xff0c;蓝牙已经从单一的传统蓝牙技术发展成集传统蓝牙。高速蓝牙和低耗能蓝牙于一体的综合技术&#xff0c;不同的应用标准更是超过40个越来越广的技术领域和越来越多的应用场景&#xff0c;使得目前的蓝牙技术成为包含传感器技术、识别技术…...

宝塔面板Linux服务器CentOS 7数据库mysql5.6升级至5.7版本教程

近段时间很多会员问系统更新较慢&#xff0c;也打算上几个好的系统&#xff0c;但几个系统系统只支持MYSQL5.7版本&#xff0c;服务器一直使用较低的MYSQL5.6版本&#xff0c;为了测试几个最新的系统打算让5.6和5.7并存使用&#xff0c;参考了多个文档感觉这种并存问题会很多。…...

掌握常用Docker命令,轻松管理容器化应用

Docker是一个开源的应用容器引擎&#xff0c;它可以让开发者将应用程序及其依赖打包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的Linux机器或Windows机器上&#xff0c;也可以实现虚拟化。容器是完全使用沙箱机制&#xff0c;相互之间不会有任何接口。下面介…...

【数据结构1-2】P5076 普通二叉树(简化版)(c++,multiset做法)

文章目录 一、题目【深基16.例7】普通二叉树&#xff08;简化版&#xff09;题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1基本思路&#xff1a; 一、题目 【深基16.例7】普通二叉树&#xff08;简化版&#xff09; 题目描述 您需要写一种数据结构&#xff0c;来维…...

Linux系统安装及管理

目录 一、Linux应用程序基础 1.1应用程序与系统命令的关系 1.2典型应用程序的目录结构 1.3常见的软件包装类型 二、RPM软件包管理 1.RPM是什么&#xff1f; 2.RPM命令的格式 2,1查看已安装的软件包格式 2.2查看未安装的软件包 3.RPM安装包从哪里来&#xff1f; 4.挂…...

MySQL学生向笔记以及使用过程问题记录(内含8.0.34安装教程

MySQL 只会写代码 基本码农 要学好数据库&#xff0c;操作系统&#xff0c;数据结构与算法 不错的程序员 离散数学、数字电路、体系结构、编译原理。实战经验&#xff0c; 高级程序员 去IOE&#xff1a;去掉IBM的小型机、Oracle数据库、EMC存储设备&#xff0c;代之以自己在开源…...

obs video-io.c

video_frame_init 讲解 /* messy code alarm video_frame_init 函数用于初始化视频帧。它接受一个指向 struct video_frame 结构体的指针 frame&#xff0c; 视频格式 format&#xff0c;以及宽度 width 和高度 height。该函数根据视频格式的不同&#xff0c;计算出每个视频帧…...

简述 tcp 和 udp的区别?

简述 tcp 和 udp的区别&#xff1f; TCP&#xff08;Transmission Control Protocol&#xff09;和UDP&#xff08;User Datagram Protocol&#xff09;是两种不同的传输层协议&#xff0c;用于在计算机网络中进行数据传输。以下是它们的主要区别&#xff1a; 区别&#xff1…...

信息收集 - 谷歌hack

搜索引擎 FOFA网络空间测绘:https://fofa.info/ FOFA(FOcus on Assets)是一个网络空间搜索引擎,可以帮助用户快速定位和收集特定目标的信息。 ZoomEye:https://www.zoomeye.org ZoomEye 是一个网络空间搜索引擎,可以用于发现和收集特定目标的网络设备、Web应用程序、开放…...

英飞凌TC3xx之一起认识DSADC系列(七)应用实战项目二(实现旋变软解码)

英飞凌TC3xx之一起认识DSADC系列(七) 1 项目要求2 项目实现2.1 内部时钟配置2.2 输入信号配置2.3 调制器配置2.4 滤波器链路配置2.5 整流器配置3 总结本文写一篇关于DSADC的resover的载波信号生成的应用,刚刚接触DSADC的开发者很容易被手册中简短的文字描述弄的迷惑,它到底…...

【浏览器】同源策略和跨域

1. 什么是跨域 在说跨域之前,先说说同源策略,什么是同源策略呢?同源策略是浏览器的一种安全机制,减少跨站点脚本攻击(XSS,Cross Site Scripting)、跨站点请求伪造(CSRF,Cross Site Request Forgery)攻击等,因为非同源的请求会被浏览器拦截掉。 同源就是协议、域名(…...

云计算与大数据之间的羁绊(期末不挂科版):云计算 | 大数据 | Hadoop | HDFS | MapReduce | Hive | Spark

文章目录 前言&#xff1a;一、云计算1.1 云计算的基本思想1.2 云计算概述——什么是云计算&#xff1f;1.3 云计算的基本特征1.4 云计算的部署模式1.5 云服务1.6 云计算的关键技术——虚拟化技术1.6.1 虚拟化的好处1.6.2 虚拟化技术的应用——12306使用阿里云避免了高峰期的崩…...

基于jdk11和基于apache-httpclient的http请求工具类

1.基于apache-httpclient 需要引入依赖 <dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.3.5</version></dependency> 工具类如下&#xff1a; package com.bw.e…...

Node.js(二)-模块化

1. 模块化的基本概念 1.1 什么是模块化 模块化是指解决一个复杂问题时&#xff0c;自顶向下逐层将系统拆分成若干模块的过程。对于整个系统来说&#xff0c;模块是可组合、分解和更换的单元。 1.2 编程领域中的模块化 编程领域中的模块化&#xff0c;就是遵守固定的规则&…...

ARM AArch64的TrustZone架构详解(上)

目录 一、概述 1.1 在开始之前 二、什么是TrustZone? 2.1 Armv8-M的TrustZone 2.2 Armv9-A Realm Management Extension(RME)...

从源PC上一次性p2v(qcow2)的构想

磁盘分区表&#xff0c;虚拟硬盘文件&#xff0c;操作系统引导 1. 基本概念和术语 源硬盘&#xff1a;一般就是客户的PC机的硬盘&#xff0c;硬盘里面包含了Windows分区。 源Windows&#xff1a;以源硬盘启动的Windows环境。 虚拟磁盘文件&#xff1a;文件格式有qcow2、vhd…...

数据结构:KMP算法

1.何为KMP算法 KMP算法是由Knuth、Morris和Pratt三位学者发明的&#xff0c;所以取了三位学者名字的首字母&#xff0c;叫作KMP算法。 2.KMP的用处 KMP主要用于字符串匹配的问题&#xff0c;主要思想是当出现字符串不匹配时&#xff0c;我们可以知道一部分之前已经匹配过的的文…...

小程序真机如何清除订阅数据

在做小程序订阅消息开发的过程中发现&#xff0c;真机上如果是选择了‘总是保持以上选择’&#xff0c;一旦用户授权后&#xff0c;后面就不会再弹出申请改订阅消息的授权弹窗&#xff0c;这对于开发过程中是很不方便的。 曾试过清除缓存&#xff0c;重进小程序也不能清除掉 解…...

基于ssm出租车管理系统的设计与实现论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本出租车管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息&…...

音视频转码

音视频转码是指&#xff1a; 容器中音视频数据编码方式转换&#xff0c;如由H.264编码转成mpeg-4编码&#xff0c;mp3转成AAC&#xff1b;音视频码率的转换&#xff0c;如4Mb视频码率降为2Mb&#xff0c;视频分辨率的转换&#xff0c;如1080P转换为720P&#xff0c;音频重采样…...

编解码异常分析

前言 最近在做的项目&#xff0c;有H264解码的需求。部分H264文件解码播放后&#xff0c;显示为绿屏或者花屏。 分析 如何确认是否是高通硬解码的问题 adb 指令 adb root adb remount adb shell setenforce 0 adb shell setprop vendor.gralloc.disable_ubwc 1 adb shell c…...

APISpace 热门好用的API推荐,含免费次数

短信验证码&#xff1a;可用于登录、注册、找回密码、支付认证等等应用场景。支持三大运营商&#xff0c;3秒可达&#xff0c;99.99&#xff05;到达率&#xff0c;支持大容量高并发。通知短信&#xff1a;短信通知支持三大运营商以及虚拟运营商&#xff0c;我们提供电信级运维…...

Qt/QML编程学习之心得:一个.qml文件调用另一个.qml文件(十七)

在c++中,一个文件调用另外一个文件最直接最快捷的方式就是#incldue<头文件>的使用,那么在元数据描述性语言QML中,如何从一个界面描述调用另外一个界面描述,一个.qml文件调用另外一个.qml呢?QML虽然有个import,但是用法可以说完全不同于#include。 引用方法1:直接…...

C++_单列模式介绍

介绍 (1)…什么是单例 1.只能有一个实例化的对象的类(2).单例有什么用 1.多线程的线程池的设计 2.系统中只需要一个窗口时才使用单例(无法重复创建) 3.一个操作系统只能有一个文件系统(3).单例怎么用 1.隐藏所有构造函数 2.静态成员内部调用构造函数实例化 3.提供一个静态函数来…...

油烟净化器如何做到高效净化?科技力量,清新餐饮生活

我最近分析了餐饮市场的油烟净化器等产品报告&#xff0c;解决了餐饮业厨房油腻的难题&#xff0c;更加方便了在餐饮业和商业场所有需求的小伙伴们。 油烟净化器的出现&#xff0c;为我们的餐饮生活注入了一抹清新的色彩。然而&#xff0c;它究竟是如何工作的&#xff1f;为何能…...

【HTML5】HTML5 语音合成

一、前言 前一段时间在项目中需要用到播报文字语音。找到了 HTML 5 有这样的功能。 现在有时间进行总结下。 二、SpeechSynthesis SpeechSynthesis 接口是语音服务的控制接口。它可以用于获取设备上关于可用的合成声音的信息&#xff0c; 开始、暂停语音&#xff0c;或者别…...

顺序表的实现

目录 一. 数据结构相关概念​ 二、线性表 三、顺序表概念及结构 3.1顺序表一般可以分为&#xff1a; 3.2 接口实现&#xff1a; 四、基本操作实现 4.1顺序表初始化 4.2检查空间&#xff0c;如果满了&#xff0c;进行增容​编辑 4.3顺序表打印 4.4顺序表销毁 4.5顺…...

深度学习中的池化

1 深度学习池化概述 1.1 什么是池化 池化层是卷积神经网络中常用的一个组件&#xff0c;池化层经常用在卷积层后边&#xff0c;通过池化来降低卷积层输出的特征向量&#xff0c;避免出现过拟合的情况。池化的基本思想就是对不同位置的特征进行聚合统计。池化层主要是模仿人的…...

Java面试整理-Java设计模式

Java中的设计模式通常是从更广泛的面向对象设计模式中借鉴而来的,这些模式旨在解决特定的设计问题和改善代码的可维护性、灵活性和可扩展性。设计模式大致可以分为三类:创建型、结构型和行为型。以下是这三类中一些常见的设计模式: 创建型模式 单例模式(Singleton):确保一…...

用CHAT了解更多知识点

问CHAT&#xff1a;什么是硅基生命和碳基生命&#xff1f; CHAT回复&#xff1a;硅基生命和碳基生命是两种理论性的生物体类型&#xff0c;这些生物体主要是由硅或碳元素以及其他元素构成的。 碳基生命是我们当前所熟知的生命形式。碳元素能够形成稳定且复杂的分子&#xff0c;…...