spring security OAuth2 搭建资源服务器以及授权服务器/jdbc/jwt两种方案

news2024/9/20 14:47:35

一、认证服务器基于jdbc方式

如果不懂请移步上一篇文章:Spring security OAuth2 授权服务器搭建-CSDN博客

    在上一篇文章中,TokenStore的默认实现为 InHenoryTokenStore 即内存存储,对于 CLient 信息,userDetaitsServce 接负责从存储库中读取数据,在上面的案例中默认使用的也是 InHemoryCLientDetalsService 实现类。如果要想使用政据库存储,只要提供这些接口的实现类即可,而框架已经为我们写好 dbekenStore 和 dbcclientDetatsService

1.1 数据库所需表

我们使用spring security 自带的sql 语句生成对应的表结构,我们关注一下 oauth_client_details  这个表,这个表用来储存用户密钥信息,相当于前面的这个地方

clients.inMemory() //基于内存,后期可以入库查出来
        .withClient("client-lq")
        .secret(passwordEncoder.encode("secret-lq"))

-- 写入客户端信息
INSERT INTO oauth_client_details
VALUES ('client-id', NULL,'$2a$10$2McX6ml8CVK3RUNpLkX1zeQeNkrEvLCPOJ2hhpG18XMeIMbJWIJnK', 'read', 'authorization_code,refresh_token','http://www.baidu.com',NULL, NULL, NULL, NULL,NULL);

client_secret 需要加密存进去,否则会报不成功

--  structure for clientdetails

DROP TABLE IF EXISTS `clientdetails`;
CREATE TABLE clientdetails (
  appId                  varchar(256) NOT NULL,
  resourceIds            varchar(256)  DEFAULT NULL,
  appSecret              varchar(256)  DEFAULT NULL,
  scope                  varchar(256)  DEFAULT NULL,
  grantTypes             varchar(256)  DEFAULT NULL,
  redirectUrl            varchar(256)  DEFAULT NULL,
  authorities            varchar(256)  DEFAULT NULL,
  access_token_validity  int(11)       DEFAULT NULL,
  refresh_token_validity int(11)       DEFAULT NULL,
  additionalInformation  varchar(4096) DEFAULT NULL,
  autoApproveScopes      varchar(256)  DEFAULT NULL,
  
  PRIMARY KEY (appId)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;



-- Table structure for oauth_access_token
DROP TABLE IF EXISTS oauth_access_token;
CREATE TABLE oauth_access_token  (
  token_id varchar(256) DEFAULT NULL,
  token blob,
  authentication_id varchar(256) NOT NULL,
  user_name     varchar(256) DEFAULT NULL,
  client_id     varchar(256) DEFAULT NULL,
  authentication blob ,
  refresh_token  varchar(256) DEFAULT NULL,
  PRIMARY KEY (authentication_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


--  Table structure for oauth_approvals

DROP TABLE IF EXISTS  oauth_approvals;
CREATE TABLE oauth_approvals (
  userId varchar(256) DEFAULT NULL,
  clientId varchar(256) DEFAULT NULL,
  scope varchar(256) DEFAULT NULL,
  status varchar(10) DEFAULT NULL,
  expiresAt timestamp NOT NULL DEFAULT current_timestamp ON UPDATE current_timestamp,
  lastModifiedAt date null
)ENGINE=InnODB DEFAULT CHARSET=utf8mb4;



-- Table structure for oauth_client_details
DROP TABLE IF EXISTS oauth_client_details;
CREATE TABLE oauth_client_details (
  client_id               varchar(256) NOT NULL,
  resource_ids            varchar(256)  DEFAULT NULL,
  client_secret           varchar(256)  DEFAULT NULL,
  scope                   varchar(256)  DEFAULT NULL,
  authorized_grant_types  varchar(256)  DEFAULT NULL,
  web_server_redirect_uri varchar(256)  DEFAULT NULL,
  authorities             varchar(256)  DEFAULT NULL,
  access_token_validity   int(11)       DEFAULT NULL,
  refresh_token_validity  int(11)       DEFAULT NULL,
  additional_information  varchar(4096) DEFAULT NULL,
  autoapprove             varchar(256)  DEFAULT NULL,
  PRIMARY KEY (client_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;





--  Table structure for oauth_clienttoken
DROP TABLE IF EXISTS  oauth_client_token;
CREATE TABLE oauth_client_token (
  token_id          varchar(256) DEFAULT NULL,
  token             blob,
  authentication_id varchar(256) NOT NULL,
  user_nam          varchar(256) DEFAULT NULL,
  client_id         varchar(256) DEFAULT NULL,
  PRIMARY KEY (authentication_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


--  Table structure for oauth_code
DROP TABLE IF EXISTS oauth_code;
CREATE TABLE oauth_code (
  code           varchar(256) DEFAULT NULL,
  authentication blob
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


-- Table structure for oauth_refresh_token
DROP TABLE IF EXISTS  oauth_refresh_token;
CREATE TABLE oauth_refresh_token (
token_id varchar(256) DEFAULT NULL,
token blob,
authentication blob
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


select * from  oauth_refresh_token;

-- 写入客户端信息
INSERT INTO oauth_client_details
VALUES ('client-id', NULL,'$2a$10$2McX6ml8CVK3RUNpLkX1zeQeNkrEvLCPOJ2hhpG18XMeIMbJWIJnK', 'read', 'authorization_code,refresh_token','http://www.baidu.com',NULL, NULL, NULL, NULL,NULL);

1.2 认证服务器配置

@Configuration
@EnableAuthorizationServer  //指定当前应用为授权服务器
public class JdbcAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {


    private final PasswordEncoder passwordEncoder;
    private final UserDetailsService userDetailsService;
    private final AuthenticationManager authenticationManager;

    private final DataSource dataSource;

    @Autowired
    public JdbcAuthorizationServerConfig(PasswordEncoder passwordEncoder, UserDetailsService userDetailsService, AuthenticationManager authenticationManager, DataSource dataSource) {
        this.passwordEncoder = passwordEncoder;
        this.userDetailsService = userDetailsService;
        this.authenticationManager = authenticationManager;
        this.dataSource = dataSource;
    }


    @Bean
    public ClientDetailsService clientDetails() {
        JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
        // 使用哪一种加密方式
        clientDetailsService.setPasswordEncoder(passwordEncoder);
        return clientDetailsService;
    }

    // 用来配置授权服务器可以为哪些客户端授权   client_id,secret  redirect_url
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetails());// 使用jdbc存储
    }


    @Bean
    public TokenStore tokenStore() {
        JdbcTokenStore jdbcTokenStore = new JdbcTokenStore(dataSource);
        return jdbcTokenStore;
    }


    // 配置令牌存储

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
       endpoints.authenticationManager(authenticationManager);
       endpoints.tokenStore(tokenStore());// 配置令牌存储为数据库存储
        endpoints.userDetailsService(userDetailsService);

        // 配置 tokenServices 参数
        DefaultTokenServices tokenServices = new DefaultTokenServices();//修改默认令牌生成服务
        tokenServices.setTokenStore(endpoints.getTokenStore());// 基于数据库令牌生成
        tokenServices.setSupportRefreshToken(true);//是否支持刷新令牌
        tokenServices.setReuseRefreshToken(true); // 是否重复使用刷新令牌( 直到过期


        tokenServices.setClientDetailsService(endpoints.getClientDetailsService());//设置客户端信息
        tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());//用来控制令牌存储增强策略
        // 访问令牌的默认有效期( 以秒为单位)。过期的令牌为零或负数。
        tokenServices.setAccessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(30)); // 30天

        endpoints.tokenServices(tokenServices);// 使用配置令牌服务

    }

}

1.3 security 配置

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {


    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();

        inMemoryUserDetailsManager.createUser(User.withUsername("root").password(passwordEncoder().encode("123456")).roles("ADMIN").build());
        return inMemoryUserDetailsManager;
    }


    @Override
    @Bean
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()// 开启表单登录
                .and()
                .csrf().disable();
    }
}

1.4 数据库配置

@Configuration
@Slf4j
public class MysqlDsConfig {

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

}

1.5 启动测试

当我们刷新token的时候 oauth_refresh_token 会有值进去,使用 apifox 测试令牌


二、资源服务器基于 jdbc 方式

需要连接数据库信息,跟认证服务器数据源一样,因为资源服务器最终也要去读取表信息

所需的依赖,资料参考 哔哩博主,不良人编程 

<properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-cloud.version>Hoxton.SR9</spring-cloud.version>
        <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
    </properties>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-oauth2-resource-server</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>

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

        <!-- 数据源支持 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.8</version>
        </dependency>

    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>

    </dependencyManagement>

2.1  资源服务器配置

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {


    private final DataSource dataSource;

    @Autowired
    public ResourceServerConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }


    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.tokenStore(tokenStore());
    }


    public TokenStore tokenStore() {
        return new JdbcTokenStore(dataSource);
    }
}

2.2 测试controller

@RestController
public class HelloController {



    @RequestMapping("hello")
    public String hello() {
        System.out.println("hello resource server ");

        return "hello resource server";
    }
}

2.3 测试结果

当我们这个时候访问的时候就需要带上 Authorization:Bearer   参数,否则提示 Full authentication is required to access this resource 

curl -H "Authorization:Bearer token 对应的值" http://127.0.0.1:8083/hello


三、认证服务器基于 jwt 方式

jwt 分为三部分,用三个点隔开  ,第一部分标识 header 标识用那种加密方式;中间部分为subject 主体信息,可以是用户信息,也可以是一个json数据;最后一部分为sign 签名信息,这个信息需要设置足够复杂才能被破解;

3.1 认证服务器配置

可能在疑问,为什么还是要用数据库,这次我们只需要从库里面读取一次用户信息,只是使用一张表 oauth_client_details 其他信息都不会使用,jwt 有自动过期时间

@Configuration
@EnableAuthorizationServer  //指定当前应用为授权服务器
public class JwtAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {


    private final PasswordEncoder passwordEncoder;
    private final UserDetailsService userDetailsService;
    private final AuthenticationManager authenticationManager;

    private final DataSource dataSource;

    @Autowired
    public JwtAuthorizationServerConfig(PasswordEncoder passwordEncoder, UserDetailsService userDetailsService, AuthenticationManager authenticationManager, DataSource dataSource) {
        this.passwordEncoder = passwordEncoder;
        this.userDetailsService = userDetailsService;
        this.authenticationManager = authenticationManager;
        this.dataSource = dataSource;
    }


    @Bean
    public ClientDetailsService clientDetails() {
        JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
        // 使用哪一种加密方式
        clientDetailsService.setPasswordEncoder(passwordEncoder);
        return clientDetailsService;
    }

    // 用来配置授权服务器可以为哪些客户端授权   client_id,secret  redirect_url
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetails());// 使用jdbc存储
    }



    // 使用同一个秘钥编码
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey("12332111");
        return jwtAccessTokenConverter;
    }


    // jwt生成方式生成令牌
    @Bean
    public TokenStore tokenStore() {
        JwtTokenStore jdbcTokenStore = new JwtTokenStore(jwtAccessTokenConverter());
        return jdbcTokenStore;
    }


    // 配置令牌存储

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
       endpoints.authenticationManager(authenticationManager);
       endpoints.tokenStore(tokenStore())// 配置jwt令牌存储为数据库存储
         .accessTokenConverter(jwtAccessTokenConverter());
    }


    //http://127.0.0.1:8082/oauth/authorize?client_id=client&response_type=code&redirect_url=https://www.baidu.com

}

3.2 测试效果

1、页面授权效果有所不同,获取code

2、返回的令牌有所不同

api fox 效果

四、资源服务器基于 jwt 方式

    我们只需要跟认证服务器加密规则设置相同即可,因为jwt 自带加密算法,就类似于我们两个定义了一套rsa的秘钥,我们加密规则相同,约定一个签名戳,这个别人获取不到,你请求过来的时候我验证合法性,所以就不需要用到数据库了

4.1 资源服务器配置

@Configuration
@EnableResourceServer
public class JwtResourceServerConfig extends ResourceServerConfigurerAdapter {


    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.tokenStore(tokenStore());
    }

    // jwt生成方式生成令牌
    @Bean
    public TokenStore tokenStore() {
        JwtTokenStore jdbcTokenStore = new JwtTokenStore(jwtAccessTokenConverter());
        return jdbcTokenStore;
    }


    // 使用同一个秘钥编码
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey("12332111");
        return jwtAccessTokenConverter;
    }
}

4.2 测试效果

curl -H "Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjYzMTY1MTAsInVzZXJfbmFtZ
SI6InJvb3QiLCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6IjEyYTgyMmNhLWQ3MTgtNDE1Yy1hYWQ3LTA5ZjIxODNjNzY0YiIsImNsaWVudF9pZCI6ImNsaWVudC1scSIsInNjb3BlI
jpbInJlYWQ6dXNlciJdfQ.myzFE0VJOhlFLOsddBknOeB6Y499RwZ1X2zTM4PxC00" http://127.0.0.1:8083/hello
 

Z1X2zTM4PxC00" http://127.0.0.1:8083/hello
hello resource server
D:\java-tool\idea-project\spring-security-study>
 

使用apifox 测试

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

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

相关文章

vue项目如何在js文件中导入assets文件夹下图片

前言&#xff1a; 之前在vuewebpack项目中动态导入图片时&#xff0c;是使用的require()函数。但是在vite中不支持require()函数&#xff0c;换成了new URL()方式。 项目中使用&#xff1a;

使用 element UI 实现自定义日历

效果如下&#xff1a; HTML代码部分&#xff1a; <el-calendar v-model"value"><!-- 这里使用的是 2.5 slot 语法&#xff0c;对于新项目请使用 2.6 slot 语法--><template slot"dateCell" slot-scope"{date, data}"><!--…

简单示例,搞懂PowerBI的ALL(),ALLEXCEPT()和ALLSELECTED()的区别

假设我们有如下数据&#xff0c;我们来统计下各班级的人数 我们在报表页里加上 班级’二班‘ 的筛选条件&#xff0c;此时PowerBI已经自动为我们显示了各班级人数&#xff1a;一班有3人&#xff0c;二班有1人。 根据我们的筛选条件&#xff0c;我们的统计人数应该是按照筛选器&…

解决RabbitMQ设置TTL过期后不进入死信队列

解决RabbitMQ设置TTL过期后不进入死信队列 问题发现问题解决方法一&#xff1a;只监听死信队列&#xff0c;在死信队列里面处理业务逻辑方法二&#xff1a;改为自动确认模式 问题发现 最近再学习RabbitMQ过程中&#xff0c;看到关于死信队列内容&#xff1a; 来自队列的消息可…

【YashanDB知识库】archivelog磁盘满导致数据库abnormal

本文转自YashanDB官网&#xff0c;具体内容可见archivelog磁盘满导致数据库abnormal 【问题分类】功能使用 【关键字】磁盘空间满&#xff0c;archivelog日志&#xff0c;archivelog自动清理 【问题描述】数据库状态变更为abnormal&#xff0c;检查V$DIAG_INCIDENT视图&#…

足底筋膜炎5天自愈方法

足底筋膜炎并没有确切的5天自愈方法。足底筋膜炎是足底筋膜的一种无菌性炎症&#xff0c;主要症状是脚后跟部位的疼痛&#xff0c;这种疼痛通常是由于长时间站立、行走或跑步等引起的足底筋膜过度牵拉所致。由于这是一种慢性炎症&#xff0c;其恢复过程往往需要一定的时间&…

docker查看从当前最后100条起看日志

logs.sh 使用说明 logs.sh 是一个用于查看 Docker 容器日志的脚本。该脚本支持两种使用方式&#xff1a; 默认查看 video-console 容器的日志。通过指定容器 ID 来查看相应容器的日志。 1. 默认方式 不需要任何参数&#xff0c;直接运行脚本&#xff0c;将自动查找名为 vid…

百度副总裁陈洋:大模型让软件开发更高效、更安全

&#x1f381;&#x1f449;点击进入文心快码 Baidu Comate 官网&#xff0c;体验智能编码之旅&#xff0c;还有超多福利&#xff01;&#x1f381; 9月11日-12日&#xff0c;CCS 2024成都网络安全系列活动在成都举行。百度副总裁陈洋出席大会开幕式并进行主题分享。陈洋表示&a…

postgres_fdw访问存储在外部 PostgreSQL 服务器中的数据

文章目录 一、postgres_fdw 介绍二、安装使用示例三、成本估算四、 远程执行选项执行计划无法递推解决 参考文件&#xff1a; 一、postgres_fdw 介绍 postgres_fdw 模块提供外部数据包装器 postgres_fdw&#xff0c;可用于访问存储在外部 PostgreSQL 服务器中的数据。 此模块…

C语言代码练习(第二十三天)

今日练习&#xff1a; 65、有 n 个整数&#xff0c;使前面各数顺序向后移动 m 个位置&#xff0c;最后 m 个数变成最前面 m 个数&#xff0c;写一函数实现以上功能&#xff0c;在主函数中输入 n 个整数和输出调整后的 n 个数。&#xff08;要求用指针&#xff09; 66、 n 个人围…

JavaScript 基础 - 第17天_AJAX综合案例

文章目录 Day02_AJAX综合案例目录学习目标01.案例_图书管理-介绍目标讲解小结 02.Bootstrap 弹框_属性控制目标讲解小结 03.Bootstrap 弹框_JS控制目标讲解小结 04.案例_图书管理\_渲染列表目标讲解小结 05.案例_图书管理\_新增图书目标讲解小结 06.案例_图书管理\_删除图书目标…

网络编程Udp协议

文章目录 UDP协议1、什么是UDP协议&#xff1f;一、定义与基本概念二、主要特点三、报文格式四、应用场景五、总结 2、如何使用Java中的UDP套接字&#xff1f;一、UDP常用APIDatagramSocketDatagramPacket 二、UDP协议下的客户端-服务器服务器客户端 UDP协议 UDP协议&#xff…

电脑怎么录制视频?游戏直播、教学分享必备!

在数字化时代&#xff0c;电脑录屏已成为游戏直播、教学分享、会议记录等多种场景下的必备技能&#xff0c;但有些朋友可能不知道怎么高效又清晰的录制视频&#xff0c;下面就给大家汇总了几个简单方法&#xff0c;一起来学习下吧~ 1. 嗨格式录屏大师 录屏大师软件免费下载_高…

方位大模型教程:从基础入门到实战应用

2024年大西洋彼岸的OpenAi公司&#xff0c;首次向世界吹响「大模型主导未来世界变革」的号角。 AI大模型&#xff0c;正在构建的颠覆力&#xff0c;为了更好的入局AI大模型&#xff0c;这次我特意复盘和整理大模型学习脉络&#xff0c;开了30节大模型的课程&#xff0c;包含大…

推荐一个java屏幕共享项目

java 屏幕共享项目 https://github.com/SpringStudent/JavaDesktopShare

【JAVA入门】Day44 - 字节打印流和字符打印流

【JAVA入门】Day44 - 字节打印流和字符打印流 文章目录 【JAVA入门】Day44 - 字节打印流和字符打印流一、字节打印流二、字符打印流三、输出语句和打印流的关系 打印流也是一种高级包装流&#xff0c;但是它只有输出&#xff0c;没有输入。 打印流一般是指&#xff1a;…

Qt_控件的QWidget属性介绍

目录 1、QWidget的核心属性 2、enabled 3、geometry 3.1 代码测试geometry 4、windowTitle 4.1 代码测试windowTitle 5、windowIcon 5.1 QIcon设置图标 5.2 qrc机制 5.3 代码测试windowIcon 6、windowOpacity 6.1 代码测试windowOpacity 7、cursor 7.1 代码测试…

使用xjar+exe4j+inno setup把加密后的jar打包成exe应用程序并创建服务

1、使用xjar对jar包进行加密 在项目的pom.xml中加入xjar的插件依赖&#xff0c;最好用2.x.x版本&#xff0c;高版本不支持-Dxjar.mode&#xff0c;无法免密码启动 github地址&#xff1a;xjar-maven-plugin <plugin><groupId>com.github.core-lib</groupId>…

RDD2022 道路瑕疵检测数据集

RDD2022 道路瑕疵数据集 txt标签或者xml标签 一共23767张图片 D00 D01 D20 D40四类 D00纵向裂缝 D10横向裂缝 D20网状裂缝 D40坑洞。 RDD2022 道路瑕疵检测数据集介绍 数据集概述 RDD2022&#xff08;Road Defect Detection 2022&#xff09;是一个专门用于道路瑕疵检测的数…

【MATLAB GUI 设计第一篇 】

文章目录 前言一、MATLAB GUI 是什么&#xff1f;二、guide 的使用1.进入GUI界面2. 布置绘图3.修改字体4. 回调函数&#xff0c;完成功能5. 整个函数和回调函数6.修改回调函数7. 显示效果8. 补充 三、 APP 的使用1.进入APP界面2.在 APP 菜单下 新建空白APP3.创建回调函数4.显示…