Spring security 动态权限管理(基于数据库)

news2024/9/25 9:38:19

一、简介

如果对该篇文章不了解,请移步上一篇文章:spring security 中的授权使用-CSDN博客

     当我们配置的 URL 拦截规则请求 URL 所需要的权限都是通过代码来配置的,这样就比较死板,如果想要调整访问某一个 URL 所需要的权限,就需要修改代码。动态管理权限规则就是我们将UR 拦截规则和访问 URI 所需要的权限都保存在数据库中,这样,在不修改源代码的情况下,只需要修改数据库中的数据,就可以对权限进行调整。

二、库表设计

     里面涉及到 用户 ,角色 ,权限   ,用户角色关系表,角色菜单表共计五张表,用户是用来认证使用的;其中是一些建表语句,如果角色复杂可以将用户这会用内存实现;

2.1 权限表

-- 菜单表
CREATE TABLE `t_authority_menu`(
  id int(11) NOT NULL AUTO_INCREMENT primary key ,
  pattern_url varchar(128) DEFAULT ''
 )ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- 添加数据
BEGIN;
  insert into t_authority_menu values (1,'/admin/**');
  insert into t_authority_menu values (2,'/user/**');
  insert into t_authority_menu values (3,'/guest/**');

commit ;

2.2 角色表

-- 角色表
CREATE TABLE `t_authority_role`(
  id int(11) NOT NULL AUTO_INCREMENT primary key ,
  role_name varchar(128) DEFAULT '' comment '角色标识',
  role_desc varchar(128) DEFAULT '' comment '角色描述'
)ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- 添加数据
BEGIN;
insert into t_authority_role values (1,'ROLE_ADMIN','系统管理员');
insert into t_authority_role values (2,'ROLE_USER','普通用户');
insert into t_authority_role values (3,'ROLE_GUEST','游客');
commit ;

2.3 角色权限关联表

-- 角色菜单关系表 primary key (m_id,r_id)
CREATE TABLE `t_authority_role_menu`(
  id int(11) NOT NULL AUTO_INCREMENT primary key,
  m_id int(11) DEFAULT NULL comment '菜单id',
  r_id int(11) DEFAULT NULL comment '角色id'
)ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- 创建唯一索引
CREATE UNIQUE INDEX i_authority_role_menu1 ON t_authority_role_menu(m_id, r_id);

-- 添加数据
BEGIN;
insert into t_authority_role_menu(m_id, r_id) values (1,1);
insert into t_authority_role_menu(m_id, r_id) values (2,1);
insert into t_authority_role_menu(m_id, r_id) values (2,2);
insert into t_authority_role_menu(m_id, r_id) values (3,3);
insert into t_authority_role_menu(m_id, r_id) values (3,2);
commit ;

2.4 用户表

-- 用户表
CREATE TABLE t_authority_user (
  id int(11)  NOT NULL AUTO_INCREMENT primary key,
  user_name   varchar(32) DEFAULT '' comment '用户名',
  password    varchar(32) DEFAULT '' comment '密码',
  enabled     tinyint(1) DEFAULT 1 comment '是否启用  1启用',
  locked      tinyint(1) DEFAULT 1 comment '是否锁定  1 未锁定'
 ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

BEGIN;
insert into t_authority_user values (1,'admin','{noop}123456','1','1');
insert into t_authority_user values (2,'user','{noop}123456','1','1');
insert into t_authority_user values (3,'guest','{noop}123456','1','1');
commit ;

2.5 用户角色关联表

-- 角色用户关系表
CREATE TABLE `t_authority_role_user`(
  id int(11) NOT NULL AUTO_INCREMENT primary key ,
  u_id int(11) DEFAULT 0 comment '用户id',
  r_id int(11) DEFAULT 0 comment '角色id'
)ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- 创建唯一索引
CREATE UNIQUE INDEX i_authority_role_user1 ON t_authority_role_user(u_id, r_id);

-- 添加数据
BEGIN;
insert into t_authority_role_user(u_id, r_id) values (1,1);
insert into t_authority_role_user(u_id, r_id) values (1,2);
insert into t_authority_role_user(u_id, r_id) values (2,2);
insert into t_authority_role_user(u_id, r_id) values (3,3);
commit ;

三、用户角色权限相关

我们设计 用户 《=》角色  《=》 权限,一个用户拥有多个角色,一个角色对应多个权限

3.1 相关实体类

// 用户
@Data
public class AuthorityUser implements Serializable {
    private Integer id;

    /**
     * 用户名
     */
    private String userName;

    /**
     * 密码
     */
    private String password;

    /**
     * 是否启用  1启用
     */
    private Boolean enabled;

    /**
     * 是否锁定  1 未锁定
     */
    private Boolean locked;

    private static final long serialVersionUID = 1L;
}


// 角色
@Data
public class AuthorityRole implements Serializable {
    private Integer id;

    /**
     * 角色标识
     */
    private String roleName;

    /**
     * 角色描述
     */
    private String roleDesc;

    private static final long serialVersionUID = 1L;
}

// 菜单
@Data
public class AuthorityMenu implements Serializable {
    private Integer id;

    private String patternUrl;

    /**
     *  一个菜单对应多个角色
     */
    private List<AuthorityRole> roles;

    private static final long serialVersionUID = 1L;
}

3.2 相关dao 接口

// 用户dao
public interface AuthorityUserDao {


    /**
     *  根据用户名获取用户信息  认证使用
     * @param id
     * @return
     */
    AuthorityUser selectByusername(String id);


    int deleteByPrimaryKey(Integer id);

    int insert(AuthorityUser record);

    int insertSelective(AuthorityUser record);

    AuthorityUser selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(AuthorityUser record);

    int updateByPrimaryKey(AuthorityUser record);
}

// 角色
public interface AuthorityRoleDao {

    /**
     *  根据用户编码获取角色列表
     * @param userId
     * @return
     */
    List<AuthorityRole> selectRoleListByUserId(int userId);

    int deleteByPrimaryKey(Integer id);

    int insert(AuthorityRole record);

    int insertSelective(AuthorityRole record);

    AuthorityRole selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(AuthorityRole record);

    int updateByPrimaryKey(AuthorityRole record);
}
// 权限
public interface AuthorityMenuDao {

    AuthorityMenu selectByPrimaryKey(Integer id);

    /**
     *  获取所有的菜单权限
     * @return
     */
    List<AuthorityMenu> getAllMenu();

}

3.3 dao对应的xml   mybatis

// 用户
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fashion.mapper.mysql.AuthorityUserDao">
  <resultMap id="BaseResultMap" type="com.fashion.model.AuthorityUser">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="user_name" jdbcType="VARCHAR" property="userName" />
    <result column="password" jdbcType="VARCHAR" property="password" />
    <result column="enabled" jdbcType="BOOLEAN" property="enabled" />
    <result column="locked" jdbcType="BOOLEAN" property="locked" />
  </resultMap>
  <sql id="Base_Column_List">
    id, user_name, `password`, enabled, locked
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from t_authority_user
    where id = #{id,jdbcType=INTEGER}
  </select>

  <select id="selectByusername" parameterType="java.lang.String" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from t_authority_user
    where user_name = #{userName}
  </select>


  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from t_authority_user
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" keyColumn="id" keyProperty="id" parameterType="com.fashion.model.AuthorityUser" useGeneratedKeys="true">
    insert into t_authority_user (user_name, `password`, enabled, 
      locked)
    values (#{userName,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{enabled,jdbcType=BOOLEAN}, 
      #{locked,jdbcType=BOOLEAN})
  </insert>
  <insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="com.fashion.model.AuthorityUser" useGeneratedKeys="true">
    insert into t_authority_user
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="userName != null">
        user_name,
      </if>
      <if test="password != null">
        `password`,
      </if>
      <if test="enabled != null">
        enabled,
      </if>
      <if test="locked != null">
        locked,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="userName != null">
        #{userName,jdbcType=VARCHAR},
      </if>
      <if test="password != null">
        #{password,jdbcType=VARCHAR},
      </if>
      <if test="enabled != null">
        #{enabled,jdbcType=BOOLEAN},
      </if>
      <if test="locked != null">
        #{locked,jdbcType=BOOLEAN},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.fashion.model.AuthorityUser">
    update t_authority_user
    <set>
      <if test="userName != null">
        user_name = #{userName,jdbcType=VARCHAR},
      </if>
      <if test="password != null">
        `password` = #{password,jdbcType=VARCHAR},
      </if>
      <if test="enabled != null">
        enabled = #{enabled,jdbcType=BOOLEAN},
      </if>
      <if test="locked != null">
        locked = #{locked,jdbcType=BOOLEAN},
      </if>
    </set>
    where id = #{id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.fashion.model.AuthorityUser">
    update t_authority_user
    set user_name = #{userName,jdbcType=VARCHAR},
      `password` = #{password,jdbcType=VARCHAR},
      enabled = #{enabled,jdbcType=BOOLEAN},
      locked = #{locked,jdbcType=BOOLEAN}
    where id = #{id,jdbcType=INTEGER}
  </update>
</mapper>


// 角色
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fashion.mapper.mysql.AuthorityRoleDao">
  <resultMap id="BaseResultMap" type="com.fashion.model.AuthorityRole">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="role_name" jdbcType="VARCHAR" property="roleName" />
    <result column="role_desc" jdbcType="VARCHAR" property="roleDesc" />
  </resultMap>
  <sql id="Base_Column_List">
    id, role_name, role_desc
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from t_authority_role
    where id = #{id,jdbcType=INTEGER}
  </select>

  <select id="selectRoleListByUserId" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select a.role_name,a.role_desc from
     t_authority_role a
     left join t_authority_role_user b on a.id = b.r_id
      where b.u_id = #{userId}
  </select>

  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from t_authority_role
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" keyColumn="id" keyProperty="id" parameterType="com.fashion.model.AuthorityRole" useGeneratedKeys="true">
    insert into t_authority_role (role_name, role_desc)
    values (#{roleName,jdbcType=VARCHAR}, #{roleDesc,jdbcType=VARCHAR})
  </insert>
  <insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="com.fashion.model.AuthorityRole" useGeneratedKeys="true">
    insert into t_authority_role
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="roleName != null">
        role_name,
      </if>
      <if test="roleDesc != null">
        role_desc,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="roleName != null">
        #{roleName,jdbcType=VARCHAR},
      </if>
      <if test="roleDesc != null">
        #{roleDesc,jdbcType=VARCHAR},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.fashion.model.AuthorityRole">
    update t_authority_role
    <set>
      <if test="roleName != null">
        role_name = #{roleName,jdbcType=VARCHAR},
      </if>
      <if test="roleDesc != null">
        role_desc = #{roleDesc,jdbcType=VARCHAR},
      </if>
    </set>
    where id = #{id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.fashion.model.AuthorityRole">
    update t_authority_role
    set role_name = #{roleName,jdbcType=VARCHAR},
      role_desc = #{roleDesc,jdbcType=VARCHAR}
    where id = #{id,jdbcType=INTEGER}
  </update>
</mapper>

// 菜单
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fashion.mapper.mysql.AuthorityMenuDao">
  <resultMap id="BaseResultMap" type="com.fashion.model.AuthorityMenu">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="pattern_url" jdbcType="VARCHAR" property="patternUrl" />
  </resultMap>
  <sql id="Base_Column_List">
    id, pattern_url
  </sql>


  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from t_authority_menu
    where id = #{id,jdbcType=INTEGER}
  </select>


  <resultMap id="MenuResultMap" type="com.fashion.model.AuthorityMenu">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="pattern_url" jdbcType="VARCHAR" property="patternUrl" />
    <collection property="roles" ofType="com.fashion.model.AuthorityRole">
      <id column="rId" property="id"/>
      <result column="role_name" jdbcType="VARCHAR" property="roleName" />
      <result column="role_desc" jdbcType="VARCHAR" property="roleDesc" />
    </collection>
 </resultMap>


  <select id="getAllMenu"  resultMap="MenuResultMap">
   select a.*,c.id rId,c.role_name,c.role_desc
    from t_authority_menu a
    left join t_authority_role_menu b on a.id = b.m_id
    left join t_authority_role c on c.id = b.r_id
  </select>
</mapper>

四、自定义UserDetailsService

用来实现自定义登录逻辑,查询数据库用户,如果不懂请看前面系列教程

@Service
public class UserServiceDetailImpl implements UserDetailsService {


    @Autowired
    private AuthorityUserDao authorityUserDao;

    @Autowired
    private AuthorityRoleDao authorityRoleDao;



    /**
     *  认证
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 1、 根据用户名获取信息
        AuthorityUser authorityUser = authorityUserDao.selectByusername(username);

        if (null == authorityUser) {
            throw new UsernameNotFoundException("用户不存在!");
        }

        // 2、获取用户对应的角色
        List<AuthorityRole> roles = authorityRoleDao.selectRoleListByUserId(authorityUser.getId());

        UserDetailInf userDetailInf = new UserDetailInf(authorityUser, roles);

        return userDetailInf;
    }
}

4.1  UserDetails 自定义

用来保存用户登录成功后,SpringSecurityHolder中的认证信息,里面有用户权限信息

public class UserDetailInf implements UserDetails {

    private AuthorityUser user;

    private List<AuthorityRole> roles;

    public UserDetailInf(AuthorityUser user, List<AuthorityRole> roles) {
        this.user = user;
        this.roles = roles;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return roles.stream()
                .map(r -> new SimpleGrantedAuthority(r.getRoleName()))
                .collect(ArrayList::new, List::add,List::addAll);
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getPassword();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

4.2 自定义数据源扫码mapper

@Configuration
@MapperScan(basePackages = MysqlDsConfiguration.PACKAGE, sqlSessionFactoryRef = "mysqlSqlSessionFactory" )
@Slf4j
public class MysqlDsConfiguration {


    static final String PACKAGE = "com.fashion.mapper.mysql";
    static final String MAPPER_LOCATION = "classpath:mybatis/mapper/mysql/*.xml";


    /**
     *  配置数据源
     * @return
     */
    @Primary
    @Bean
    public DataSource mysqlDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test");
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUsername("root");
        dataSource.setPassword("12345");
        return dataSource;
    }

    @Primary
    @Bean
    public SqlSessionFactory mysqlSqlSessionFactory(@Autowired DataSource mysqlDataSource){
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        try {
            sessionFactory.setDataSource(mysqlDataSource);
            sessionFactory.setConfigLocation(new ClassPathResource("/mybatis/mybatis-config.xml"));
            sessionFactory.setMapperLocations(
                    new PathMatchingResourcePatternResolver().getResources(MysqlDsConfiguration.MAPPER_LOCATION));
            return sessionFactory.getObject();
        } catch (Exception e) {
            log.error("mysql数据源初始化失败",e);
        }
        return null;
    }

}

五、定义 MetadataSource 权限信息

    实现 FilterInvocationSecurity ,用来将权限对应的角色信息加载进去,该类用于我们重写getAttributes ,将我们菜单对应的角色权限查询出来,实现动态授权

@Component
public class UrlAntPatchMetadataSource implements FilterInvocationSecurityMetadataSource {


    @Autowired
    private AuthorityMenuDao authorityMenuDao;

    private AntPathMatcher antPathMatcher = new AntPathMatcher();


    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        // 当前obj 实际上是一个url
        String requestURI = ((FilterInvocation) object).getRequest().getRequestURI();
        // 获取所有的url 对应的角色集合
        List<AuthorityMenu> allAuthorityMenus = authorityMenuDao.getAllMenu();

        String[] roles = allAuthorityMenus.stream()
                .filter(menu -> antPathMatcher.match(menu.getPatternUrl(),requestURI))
                .flatMap(authorityMenu -> authorityMenu.getRoles().stream())
                .map(AuthorityRole::getRoleName)
                .toArray(String[]::new);

        if (null != roles && roles.length > 0) {
            return SecurityConfig.createList(roles);
        }

//        for (AuthorityMenu menu : allAuthorityMenus) {
//            if (antPathMatcher.match(menu.getPatternUrl(),requestURI)) {
//                String[] roles = menu.getRoles().stream().map(r -> r.getRoleName()).toArray(String[]::new);
//                return SecurityConfig.createList(roles);
//            }
//        }
        return null;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}

六、SecurityConfiguration 配置

1、设置全局的自定义数据源
2、设置权限过滤规则,将自定义的FilterInvocationSecurityMetadataSource注入

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {


    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private UrlAntPatchMetadataSource urlAntPatchMetadataSource;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 1、获取工厂对象
        ApplicationContext applicationContext = http.getSharedObject(ApplicationContext.class);
        // 2、设置自定义url 匹配规则
        http.apply(new UrlAuthorizationConfigurer<>(applicationContext))
             .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                 @Override
                 public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                     object.setSecurityMetadataSource(urlAntPatchMetadataSource);
                     // 如果没有权限是否拒绝
                     object.setRejectPublicInvocations(true);

                     return object;
                 }
             });

        http.authorizeRequests()
                .anyRequest().authenticated()
                .and().formLogin()//开启表单登录
                .and().csrf().disable();
    }
}

七、测试controller

用户对应的角色分析

        admin 用户拥有 ROLE_ADMIN/ROLE_USER 的角色

        user 用户拥有  ROLE_USER 的角色

        guest 用户拥有  ROLE_GUEST 的角色

角色对应的权限分析

        ROLE_ADMIN角色拥有 /admin/** 以及 /user/** 以下权限

        ROLE_USER 角色拥有 /user/** 以及/guest/**  以下权限

        ROLE_GUEST用户拥有 /guest/** 路径以下权限

@RestController
public class HelloController {


    /**
     *
     * @return
     */
    @RequestMapping("/admin/hello")
    public String admin() {
        return "hello admin";
    }


    @RequestMapping("/user/hello")
    public String user() {
        return "hello user";
    }

    @RequestMapping("/guest/hello")
    public String guest() {
        return "hello guest";
    }

    @RequestMapping("/hello")
    public String hello() {
        return "hello";
    }

}

7.1  admin 登录测试效果

使用 admin 用户登录,我们访问接口测试权限,经分析,admin 拥有下面所有的权限,对应controller中的三个方法是都可以访问的;

7.2 user登录测试效果

使用 user用户登录,分析得到;user用户只能访问 /user/hello  或者 /guest/hello 接口

7.3 guest 用户登录测试效果

使用 guest用户登录,分析得到;guest用户只能访问 /guest/hello 接口

八、问题总结

一、AntPathMatcher中的mather 方法,里面千万不能写反了,第一个是我们通配库里面定义的,第二个参数为请求的url,两个的顺序不能对调;
antPathMatcher.match(menu.getPatternUrl(),requestURI)  

二、我们每一次授权都需要走一次数据库,性能问题;
   解决方案:

       1、设置一个hutool中的timecache 定时清除里面对应权限信息,设置10分钟,这样我们10分钟才跟数据源有一次交互;问题就是集群,可能每一台里面都需要放一次,优点是效率更高
        2、我们将权限信息存到redis中,这种方案更好,如果是集群也不影响;缺点就是需要一个redis的依赖;

源码跟文档我都上传了,有需求的小伙伴自行下载,下载链接:

https://download.csdn.net/download/qq_36260963/89733771

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2121499.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【专项刷题】— 队列

1、N 叉树的层序遍历 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 每次遍历一层节点的时候就把当前节点的值添加到列表中再将当前层的节点的子节点添加到队列中每次遍历完一层之后就添加到总表中代码&#xff1a; public List<List<Integer>> levelO…

如何远程实时监控员工的电脑屏幕?远程桌面监控的五个可实现方法分享

想象一下&#xff0c;你在办公室喝着咖啡&#xff0c;员工的电脑屏幕却在数百公里之外实时呈现在你的眼前。你可以看到他们在干什么&#xff0c;是埋头工作还是悄悄摸鱼&#xff1f;远程桌面监控让这一切变得触手可及&#xff0c;简直像给了管理者一双“千里眼”&#xff01; 如…

RedisTemplate操作String的API

文章目录 1 String 介绍2 命令3 对应 RedisTemplate API❄️❄️ 3.1 添加缓存❄️❄️ 3.2 设置过期时间(单独设置)❄️❄️ 3.3 获取缓存值❄️❄️ 3.4 删除key❄️❄️ 3.5 顺序递增❄️❄️ 3.6 顺序递减 ⛄4 以下是一些常用的API⛄5 应用场景 1 String 介绍 String 类型…

9.10-AutoAWQ代码解析

1、首先要去官网下载源码。https://github.com/casper-hansen/AutoAWQ.githttps://github.com/casper-hansen/AutoAWQ.git 2、git clone后&#xff0c;下载AutoAWQ所需环境。 pip install -e . 3、查看quantize.py代码&#xff0c;修改model_path部分&#xff0c;修改为想要量…

系统架构师考试学习笔记第四篇——架构设计实践知识(19)嵌入式系统架构设计理论与实践

本章考点&#xff1a; 第19课时主要学习嵌入式系统架构设计的理论和工作中的实践。根据新版考试大纲&#xff0c;本课时知识点会涉及案例分析题&#xff08;25分&#xff09;。在历年考试中&#xff0c;案例题对该部分内容都有固定考查&#xff0c;综合知识选择题目中有固定分值…

您与该网站的连接不是私密连接,存在安全隐患

您与该网站的连接不是私密连接&#xff0c;存在安全隐患。 攻击者可能会试图窃取您的信息&#xff08;例如&#xff1a;密码、通讯内容或信用卡信息&#xff09;。为避免您的信息失窃&#xff0c;建议您停止访问该页面。了解详情 解决办法如下&#xff1a; 1、查看电脑时间&…

使用FastJson2将对象转成JSON字符串时,小数位“0”开头时转换出错

maven坐标&#xff1a; <dependency> <groupId>com.alibaba.fastjson2</groupId> <artifactId>fastjson2</artifactId> <version>2.0.40</version> </dependency> 问题现象&#xff1a; 问题原因&#xff1a; I…

IP路由选择

文章目录 1. 基本概念2. RIP(路由选择信息协议)3. OSPF 1. 基本概念 路由选择协议 路由选择协议让路由器能够动态地发现互联网络&#xff0c;并确保所有路由器的路由选择表都相同。路由选择协议还用于找出最佳路径&#xff0c;让分组穿越互联网络前往目的地的效率最高。RIP、R…

领夹麦克风哪个品牌好?无线领夹麦克风品牌大全,麦克风推荐

在这个全民直播、Vlog盛行的时代&#xff0c;一款轻便高效的无线领夹麦克风成了不少内容创作者的必备神器。但市面上产品五花八门&#xff0c;有的打着“超远传输、无损音质”的旗号&#xff0c;实则性能平平&#xff0c;甚至存在信号干扰、噪音大等问题&#xff0c;让人直呼交…

SpringBoot集成MyBatis-PlusDruid

目录 MyBatis-Plus简介 实例演示 创建Springboot项目 初始化Springboot项目 添加关键依赖 application.properties添加相关配置 启动类 编写实体类 编写mapper接口 条件构造器 分页插件 自定义 SQL 映射 MyBatis-Plus简介 MyBatis-Plus简介‌MyBatis-Plus‌&…

铁威马秋季新品即将上线,你想要的NAS我都有!

各位铁粉们&#xff0c;注意啦&#xff01; 一场关于存储的饕餮盛宴即将拉开帷幕 铁威马&#xff0c;带着九款全新力作NAS 将于9月19日席卷全球市场 是的&#xff0c;你没听错 九款&#xff01; 从入门级到专业级 从桌面型到机架式 全系搭载TOS 6 总有一款能击中你的心…

PCI 9054应用总结

1 PCI配置空间 1.1 BAR大小的确定 Linux kernel读取PCI BARn表示的内存长度时&#xff0c;先直接读取BARn的值&#xff0c;这个就是地址&#xff0c;然后再向BARn写入0xffff,ffff&#xff0c;再读取BARn的值就是需要的内存长度&#xff08;忽略bit3到bit0的处理&#xff09;&a…

微波无源器件 3 一种用于Ka频带双极化波束形成网络的双模三路功分器

摘要&#xff1a; 本文给出了一种用于Ka频带的双极化工作的双模3路功分器的设计和性能。对有着三个输出端口的平衡地很好的功分的TE10和TE01模式和27.5-30GHz上优于-23dB的输入匹配可以获得相似的性能。与双模定向耦合器相连结&#xff0c;此三路功分器对于双极化波束形成网络具…

【Go】Go语言介绍与开发环境搭建

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

Qt篇——Qt获取Windows电脑上所有外接设备的名称、物理端口位置等信息

我之前有发过一篇文章《Qt篇——获取Windows系统上插入的串口设备的物理序号》&#xff0c;文章中主要获取的是插入的USB串口设备的物理序号&#xff1b;而本篇文章则进行拓展&#xff0c;可以获取所有外接设备的相关信息&#xff08;比如USB摄像头、USB蓝牙、USB网卡、其它一些…

膨胀腐蚀操作opencv dilate膨胀白膨胀,erode腐蚀是黑吃白。主要针对二值图

效果&#xff1a; 代码&#xff1a; import cv2 import numpy as np from matplotlib import pyplot as pltif __name__ "__main__":h 10w 10data np.random.normal(0, 1, [h, w]) # sigma, 2*sigma, 3*sigma之间的数的比例分别为0.68&#xff0c; 0.96&#…

甲骨文新财报云基建营收同比增长45%,与亚马逊AWS合作,盘后涨超9%

KlipC报道&#xff1a;当地时间9月9日&#xff0c;数据库软件与云服务龙头甲骨文发布截止8月份的2025财年第一财季财报&#xff0c;财报显示&#xff0c;营业收入133亿美元&#xff0c;同比增长7%&#xff1b;净利润 29.29 亿美元&#xff0c;同比增长21.03%。盘后大涨超9%。 …

C++ 第一阶段(基础语法)

c初识 创建项目 编写“HelloWorld”代码 #include <iostream> using namespace std; int main() {cout << "Hello World!\n"; }注释 单行注释 //表述信息 多行注释 /*表述信息*/ 变量 作用:给一段指定的内存空间起名&#xff0c;方便操作这段内存 语…

Tensorflow2如何读取自制数据集并训练模型?-- Tensorflow自学笔记13

一. 如何自制数据集&#xff1f; 1. 目录结构 以下是自制数据集-手写数字集, 保存在目录 mnist_image_label 下 2. 数据存储格式 2.1. 目录mnist_train_jpeg_60000 下存放的是 60000张用于测试的手写数字 如 : 0_5.jpg, 表示编号为0&#xff0c;标签为5的图片 6_1.jpg, 表示…

DGV结构变异数据库下载及bedToBigBed+bigBedToBed转换

DGV结构变异数据库通常用来过滤检测的CNV、Indels等结构变异信息&#xff0c;以达到去除假阳性的目的。 下载的bedToBigBed和bigBedToBed为二进制程序&#xff0c;已编译好&#xff0c;可以直接使用。 1. 基础软件和文件下载 # bedToBigBed下载 wget http://hgdownload.soe.…