超级详细的SpringSecurity

news2024/11/16 8:58:36

文章目录

  • 概述
  • 与shiro对比
  • 快速入门
  • 底层原理
    • Filter
    • DelegatingFilterProxy
    • FilterChainProxy
    • SecurityFilterChain
    • Multiple SecurityFilterChain
  • 自定义登录
    • 流程解析
    • 基于内存的用户认证实现
    • 基于数据库的用户登录
  • 实现用户新增功能
    • controller
    • service
    • 修改配置
    • 关闭csrf攻击防御
    • 修改默认的密码编码器
    • Swagger测试
  • 密码加密算法
    • 密码加密方式
      • 明文密码
      • Hash算法
      • 彩虹表
      • 加盐密码
      • 自适应单向函数
    • PasswordEncoder(单向)
      • BCryptPasswordEncoder
      • Argon2PasswordEncoder
      • Pbkdf2PasswordEncoder
      • SCryptPasswordEncoder
    • 密码加密测试
    • DelegatingPasswordEncoder
  • 自定义登录界面
    • 创建登录Controller
    • 创建登录界面
    • 配置SecurityFilterChain
  • 用户认证
    • 认证成功
    • 认证失败
  • 用户授权
    • 用户-权限-资源
    • 用户-角色-资源
    • 注解方式
      • 开启方法授权
      • 给用户授予角色和权限
      • 常用授权注解
    • 用户授权失败

概述

Spring 是⾮常流⾏和成功的 Java 应⽤开发框架,Spring Security 正是 Spring 家族中的 成员。Spring Security 基 于 Spring 框架,提供了⼀套 Web 应⽤安全性的完整解决⽅ 案。
正如你可能知道的关于安全⽅⾯的两个主要区域 是“认证”和“授权”(或者访问控 制),⼀般来说,Web 应⽤的安全性包括⽤户认证(Authentication)和⽤户授权 (Authorization)两个部分,这两点也是 Spring Security 重要核⼼功能。
(1)⽤户认证指的是:验证某个⽤户是否为系统中的合法主体,也就是说⽤户能否访问 该系统。⽤户认证⼀般要 求⽤户提供⽤户名和密码。系统通过校验⽤户名和密码来完成认 证过程。通俗点说就是系统认为⽤户是否能登录
(2)⽤户授权指的是验证某个⽤户是否有权限执⾏某个操作。在⼀个系统中,不同⽤户 所具有的权限是不同的。 ⽐如对⼀个⽂件来说,有的⽤户只能进⾏读取,⽽有的⽤户可以 进⾏修改。⼀般来说,系统会为不同的⽤户分配不 同的⻆⾊,⽽每个⻆⾊则对应⼀系列的 权限。通俗点讲就是系统判断⽤户是否有权限去做某些事情。

与shiro对比

Shiro和Spring Security⽐较 Shiro⽐Spring更容易使⽤、实现和理解。
Spring Security更加知名的唯⼀原因是因为品牌名称“Spring”以简单⽽闻名,但讽刺的是很多⼈发现使⽤Spring Security很困难。但是,Spring Security却有更好的社区⽀持。
Apache Shiro与Spring Security处理密码学⽅⾯相⽐有⼀个额外的模块。
Spring Security 与Spring 结合地较好,如果项⽬⽤的springmvc ,使⽤起来很⽅便。但是如果项⽬中没有⽤到 spring框架,那就不要考虑它了。
Shiro 功能强⼤、且简单、灵活。是Apache基⾦会下的项⽬,⽐较可靠,且不跟任何框架或者容器绑定,可以独⽴运⾏。
官网: 介绍

快速入门

  1. 创建spring脚手架项目(导入web)
  2. 添加依赖(在脚手架中添加serurity就不需要添加依赖)
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
  1. 运行Application类
  2. 会出现一个需要用户名密码的页面,用户名为user,密码为控制台打印的密码,输入即可进入index页面

底层原理

官方文档:底层原理
image.png
image.png

Filter

DelegatingFilterProxy

DelegatingFilterProxy 是 Spring Security 提供的⼀个 Filter 实现,可以在 Servlet 容器和 Spring 容器之间建⽴桥 梁。通过使⽤ DelegatingFilterProxy,这样就可以将Servlet容器中的 Filter 实例放在 Spring 容器中管理。

FilterChainProxy

复杂的业务中不可能只有⼀个过滤器。因此FilterChainProxy是Spring Security提供的⼀个特殊的Filter,它允许通 过SecurityFilterChain将过滤器的⼯作委托给多个Bean Filter实例。

SecurityFilterChain

SecurityFilterChain 被 FilterChainProxy 使⽤,负责查找当前的请求需要执⾏的Security Filter列表。

Multiple SecurityFilterChain

可以有多个SecurityFilterChain的配置,FilterChainProxy决定使⽤哪个SecurityFilterChain。如果请求的URL 是/api/messages/,它⾸先匹配SecurityFilterChain0的模式/api/**,因此只调⽤SecurityFilterChain 0。假设没有 其他SecurityFilterChain实例匹配,那么将调 SecurityFilterChain n。

自定义登录

流程解析

官方文档:Java自定义配置
1. 登录
1.⾃定义登录接⼝
调⽤ProviderManager的⽅法进⾏认证如果认证通过⽣成jwt把⽤户信息存⼊redis中
2.⾃定义UserDetailsService
在这个实现列中去查询数据库

  1. 校验:

1.定义jwt认证过滤器
获取token
解析token获取其中的userid从redis中获取⽤户信息 存⼊SecurityContextHolde

基于内存的用户认证实现

自定义接口

package com.ry.securitydemo.config;
@Configuration
//@EnableWebSecurity//Spring项⽬中需要添加此注解,SpringBoot项⽬中不需要
public class WebSecurityConfig {
    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser( //此⾏设置断点可以查看创建的user对象
            User
            .withDefaultPasswordEncoder()
            .username("admin") //⾃定义⽤户名
            .password("123456") //⾃定义密码
            .roles("USER") //⾃定义⻆⾊
            .build()
        );
        return manager;
    }
}

登录界面只需要输入自定义的用户名和密码即可

基于数据库的用户登录

实现步骤

  • 程序启动时:
    • 创建 DBUserDetailsManager 类,实现接⼝ UserDetailsManager, UserDetailsPasswordService
    • 在应⽤程序中初始化这个类的对象
  • 校验⽤户时:
    • SpringSecurity⾃动使⽤ DBUserDetailsManager 的 象 loadUserByUsername ⽅法从 数据库中获取User对
    • 在UsernamePasswordAuthenticationFilter过滤器中的 attemptAuthentication ⽅法中将⽤户输 ⼊的⽤户名密码和从数据库中获取到的⽤户信息进⾏⽐较,进⾏⽤户认证

实现代码:基于springboot+mp

  1. **定义 DBUserDetailsManager **
@Component
public class DBUserDetailsManager implements UserDetailsManager {

    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        QueryWrapper qw = new QueryWrapper();
        qw.eq("username",username);
        User user = userMapper.selectOne(qw);
        return org.springframework.security.core.userdetails.User.withDefaultPasswordEncoder()
                .username(user.getUsername())
                .password(user.getPassword())
                .roles("USER")
                .build();

    }

    @Override
    public void createUser(UserDetails user) {

        PasswordEncoder encoder = new BCryptPasswordEncoder();

        userMapper.insert(new User(user.getUsername(),encoder.encode(user.getPassword())));
    }

    @Override
    public void updateUser(UserDetails user) {

    }

    @Override
    public void deleteUser(String username) {

    }

    @Override
    public void changePassword(String oldPassword, String newPassword) {

    }

    @Override
    public boolean userExists(String username) {
        return false;
    }


}

  1. ** 初始化UserDetailsService **

将WebSecurityConfig中的userDetailsService⽅法注释掉

  1. ** SpringSecurity的默认配置 **

在WebSecurityConfig中写如下代码:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    //authorizeRequests():开启授权保护
    //anyRequest():对所有请求开启授权保护
    //authenticated():已认证请求会⾃动被授权
    http
    .authorizeRequests(authorize -> authorize.anyRequest().authenticated())
    .formLogin(withDefaults())//表单授权⽅式
    .httpBasic(withDefaults());//基本授权⽅式
}

实现用户新增功能

controller

@Controller
public class UserController {


    @Autowired
    private UserService userService;

    @PostMapping("/addUser")
    @ResponseBody
    public String add(@RequestBody User user){

        userService.addUser(user);
        return "新增成功";
    }
}

service

接口

public interface UserService extends IService<User> {

    void addUser(User user);
}

实现类

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private DBUserDetailsManager dbUserDetailsManager;

    @Override
    public void addUser(User user) {
        dbUserDetailsManager.createUser(org.springframework.security.core.userdetails.User.withDefaultPasswordEncoder()
                .username(user.getUsername())
                .password(user.getPassword())
                .authorities(AuthorityUtils.createAuthorityList("ADMIN"))
                .build());
    }
}

修改配置

DBUserDetailsManager中添加⽅法

@Override
public void createUser(UserDetails userDetails) {
User user = new User();
user.setUsername(userDetails.getUsername());
user.setPassword(userDetails.getPassword());
user.setEnabled(true);
userMapper.insert(user);
}

关闭csrf攻击防御

默认情况下SpringSecurity开启了csrf攻击防御的功能,这要求请求参数中必须有⼀个隐藏的_csrf字段,如下:
在filterChain⽅法中添加如下代码,关闭csrf攻击防御

//关闭csrf攻击防御
http.csrf((csrf) -> {
 csrf.disable();
 });

换到写的代码中就是(在WebSecurityConfig中)
image.png

修改默认的密码编码器

实际项⽬中我们不会把密码明⽂存储在数据库中,默认使⽤的PasswordEncoder要求数据库中的密码格式:(id}password。
它会根据id去判断密码的加密⽅式。
但是我们⼀般不会采⽤这种⽅式。所以就需要替换PasswordEncoder
我们⼀般使⽤SpringSecurity为我们提供的BCryptPasswordEncoder 我们只需要使⽤把BCryptPasswordEncoder对象注⼊Spring容器中,SpringSecurity就会使⽤该PasswordEncoder 来进⾏密码校验我们可以定义⼀个SpringSecurity的配置类,SpringSecurity要求这个配置类要继承 WebsecurityConfigurerAdapter。

@Bean
 public PasswordEncoder passwordEncoder() {
 return new BCryptPasswordEncoder(); // 或者其他的PasswordEncoder实现
  }

或者(在DBUserDetailsManager中的createUser方法中进行密码修改)

 @Override
    public void createUser(UserDetails user) {

        PasswordEncoder encoder = new BCryptPasswordEncoder();

        userMapper.insert(new User(user.getUsername(),encoder.encode(user.getPassword())));
    }

Swagger测试

依赖

<!--swagger测试-->
<dependency>
  <groupId>com.github.xiaoymin</groupId>
  <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
  <version>4.0.0</version>
</dependency>

在application.yml配置

knife4j:
  enable: true

密码加密算法

参考文档:官网

密码加密方式

明文密码

最初,密码以明⽂形式存储在数据库中。但是恶意⽤户可能会通过SQL注⼊等⼿段获取到明⽂密码,或者程序员将 数据库数据泄露的情况也可能发⽣。

Hash算法

Spring Security的 PasswordEncoder 接⼝⽤于对密码进⾏ 要⽤到 单向转换,从⽽将密码安全地存储。对密码单向转换需 哈希算法,例如MD5、SHA-256、SHA-512等,哈希算法是单向的, 只能加密,不能解密。
因此, 数据库中存储的是单向转换后的密码,Spring Security在进⾏⽤户身份验证时需要将⽤户输⼊的密码进⾏单向 转换,然后与数据库的密码进⾏⽐较。
因此,如果发⽣数据泄露,只有密码的单向哈希会被暴露。由于哈希是单向的,并且在给定哈希的情况下只能通过 暴⼒破解的⽅式猜测密码。

彩虹表

恶意⽤户创建称为 彩虹表 的查找表
彩虹表就是⼀个庞⼤的、针对各种可能的字⺟组合预先⽣成的哈希值集合,有了它可以快速破解各类密码。越是复杂的密 码,需要的彩虹表就越⼤,主流的彩虹表都是100G以上,⽬前主要的算法有LM, NTLM, MD5, SHA1, MYSQLSHA1, HALFLMCHALL, NTLMCHALL, ORACLE-SYSTEM, MD5-HALF

加盐密码

为了减轻彩虹表的效果,开发⼈员开始使⽤加盐密码。不再只使⽤密码作为哈希函数的输⼊,⽽是为每个⽤户的密 码⽣成随机字节(称为盐)。盐和⽤户的密码将⼀起经过哈希函数运算,⽣成⼀个唯⼀的哈希。盐将以明⽂形式与 ⽤户的密码⼀起存储。然后,当⽤户尝试进⾏身份验证时,盐和⽤户输⼊的密码⼀起经过哈希函数运算,再与存储 的密码进⾏⽐较。唯⼀的盐意味着彩虹表不再有效,因为对于每个盐和密码的组合,哈希都是不同的。

自适应单向函数

随着硬件的不断发展,加盐哈希也不再安全。原因是,计算机可以每秒执⾏数⼗亿次哈希计算。这意味着我们可以 轻松地破解每个密码。 现在,开发⼈员开始使⽤⾃适应单向函数来存储密码。使⽤⾃适应单向函数验证密码时, 量的CPU、内存或其他资源)。⾃适应单向函数允许配置⼀个 故意占⽤资源(故意使⽤⼤ “⼯作因⼦”,随着硬件的改进⽽增加。我们建议将“⼯作 因⼦”调整到系统中验证密码需要约⼀秒钟的时间。这种权衡是为了 让攻击者难以破解密码。 ⾃适应单向函数包括 bcrypt、PBKDF2、scrypt和argon2。

PasswordEncoder(单向)

BCryptPasswordEncoder

使⽤⼴泛⽀持的bcrypt算法来对密码进⾏哈希。为了增加对密码破解的抵抗⼒,bcrypt故意设计得较慢。和其他⾃ 适应单向函数⼀样,应该调整其参数,使其在您的系统上验证⼀个密码⼤约需要1秒的时间。 BCryptPasswordEncoder的默认实现使⽤强度10。建议您在⾃⼰的系统上调整和测试强度参数,以便验证密码时 ⼤约需要1秒的时间。

Argon2PasswordEncoder

使⽤Argon2算法对密码进⾏哈希处理。Argon2是密码哈希⽐赛的获胜者。为了防⽌在⾃定义硬件上进⾏密码破 解,Argon2是⼀种故意缓慢的算法,需要⼤量内存。与其他⾃适应单向函数⼀样,它应该在您的系统上调整为⼤ 约1秒来验证⼀个密码。当前的Argon2PasswordEncoder实现需要使⽤BouncyCastle库。

Pbkdf2PasswordEncoder

使⽤PBKDF2算法对密码进⾏哈希处理。为了防⽌密码破解,PBKDF2是⼀种故意缓慢的算法。与其他⾃适应单向 函数⼀样,它应该在您的系统上调整为⼤约1秒来验证⼀个密码。当需要FIPS认证时,这种算法是⼀个很好的选择。

SCryptPasswordEncoder

使⽤scrypt算法对密码进⾏哈希处理。为了防⽌在⾃定义硬件上进⾏密码破解,scrypt是⼀种故意缓慢的算法,需 要⼤量内存。与其他⾃适应单向函数⼀样,它应该在您的系统上调整为⼤约1秒来验证⼀个密码。

密码加密测试

在测试类中编写一个测试方法

@Test
void testPassword() {
    // ⼯作因⼦,默认值是10,最⼩值是4,最⼤值是31,值越⼤运算速度越慢
    PasswordEncoder encoder = new BCryptPasswordEncoder(4);
    //明⽂:"password"
    //密⽂:result,即使明⽂密码相同,每次⽣成的密⽂也不⼀致
    String result = encoder.encode("password");
    System.out.println(result);

    //密码校验
    Assert.isTrue(encoder.matches("password", result), "密码不⼀致");

}

DelegatingPasswordEncoder

  • 表中存储的密码形式
    • {bcrypt} $2a 10 10 10GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW
  • 通过源码可以知道:可以通过 {bcrypt} 前缀动态获取和密码的形式类型⼀致的PasswordEncoder对象
  • 目的:方便随时做密码策略的升级,兼容数据库中的⽼版本密码策略⽣成的密码

自定义登录界面

使用Thymeleaf(不懂的话可以看springboot整合web开发)

创建登录Controller

@Controller
public class LoginController {
    @PostMapping("/login")
    public String login() {
        return "login";
    }
}

创建登录界面

resources/templates/login.html

<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org">
  <head>
    <title>登录</title>
  </head>
  <body>
    <h1>登录</h1>
    <div th:if="${param.error}">
      错误的⽤户名和密码.</div>
    <!--method必须为"post"-->
    <!--th:action="@{/login}" ,
    使⽤动态参数,表单中会⾃动⽣成_csrf隐藏字段,⽤于防⽌csrf攻击
    login: 和登录⻚⾯保持⼀致即可,SpringSecurity⾃动进⾏登录认证-->
    <form th:action="@{/login}" method="post">
      <div>
        <!--name必须为"username"-->
        <input type="text" name="username" placeholder="⽤户名"/>
      </div>
      <div>
        <!--name必须为"password"-->
        <input type="password" name="password" placeholder="密码"/>
      </div>
      <input type="submit" value="登录" />
    </form>
  </body>
</html>

配置SecurityFilterChain

.formLogin( form -> {
    form
        .loginPage("/login").permitAll() //登录⻚⾯⽆需授权即可访问
        .usernameParameter("username") //⾃定义表单⽤户名参数,默认是username
        .passwordParameter("password") //⾃定义表单密码参数,默认是password
        .failureUrl("/login?error") //登录失败的返回地址
        ;
 }); //使⽤表单授权⽅式

image.png
登录成功进入index界面

用户认证

认证成功

 .successHandler(new MyAuthenticationSuccessHandler())//用户登录成功之后执行的内容、

image.png

public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {

        //转发
        String username = request.getParameter("username");
        request.setAttribute("username",username);
        request.getRequestDispatcher("/success").forward(request,response);
    }
}

认证失败

.failureHandler(new MyAuthenticationFailureHandler())//用户失败后干嘛

image.png

public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {

        //返回json数据给前端
        response.setContentType("application/json;charset=utf-8");
        String s = JSON.toJSONString("登录失败");
        response.getWriter().write(s);
    }
}

用户授权

授权管理的实现在SpringSecurity中⾮常灵活,可以帮助应⽤程序实现以下两种常⻅的授权需求:

  • 用户-权限-资源:例如张三的权限是添加⽤户、查看⽤户列表,李四的权限是查看⽤户列表
  • ** 用户-角色-权限-资源**:例如 张三是⻆⾊是管理员、李四的⻆⾊是普通⽤户,管理员能做所有操作,普通⽤户 只能查看信息

用户-权限-资源

在WebSecurityConfig中

//开启授权保护,这是springsecurity6的授权⽅式
http.authorizeRequests(
    authorize -> authorize
    //具有USER_LIST权限的⽤户可以访问/user/list
    .requestMatchers("/user/list").hasAuthority("USER_LIST")
    //具有USER_ADD权限的⽤户可以访问/user/add
    .requestMatchers("/user/add").hasAuthority("USER_ADD")
    //对所有请求开启授权保护
    .anyRequest()
    //已认证的请求会被⾃动授权
    .authenticated()
);
//springsecurity5授权⽅式
//      
http.authorizeRequests(authorize ->
                       授予权限 
                       DBUserDetailsManager中的loadUserByUsername⽅法:
                       请求未授权的接⼝ 
                       ⽤户授权

                       //                authorize.antMatchers("/user/list").hasAuthority("ADMIN")
                       //                        .antMatchers("/user/add").hasAuthority("USER")
                       //                        .anyRequest()
                       //                        .authenticated()
                       //        );
                       //springsecurity5官⽹授权⽅式
                       http.authorizeRequests(authorize ->
                                              authorize.mvcMatchers("/user/list").hasAuthority("ADMIN")
                                              .mvcMatchers("/user/add").hasAuthority("USER")
                                              .anyRequest()
                                              .authenticated()
                                             );

对应的在DBUserDetailsManager中loadUserByUsername方法返回值要写.authorities(AuthorityUtils.createAuthorityList(“ADMIN”))

用户-角色-资源

在WebSecurityConfig中

 http
                .authorizeHttpRequests(authorize ->
                        authorize
                                .mvcMatchers("/addUser").hasAnyRole("ADMIN","MEMBER")
                                .mvcMatchers("/delUser").hasAnyRole("ADMIN","MEMBER")
                                .mvcMatchers("/updUser").hasRole("ADMIN")
                                .mvcMatchers("/selUser").hasAnyRole("ADMIN","VISITOR")
                                .anyRequest()//对所有请求开启授权保护
                                .authenticated()//已认证的请求会被自动授权
                );

对应的在DBUserDetailsManager中loadUserByUsername方法返回值要写.roles(“ADMIN”)

注解方式

开启方法授权

在WebSecurityConfig加入下面注解

@EnableMethodSecurity(prePostEnabled = true)

给用户授予角色和权限

DBUserDetailsManager中的loadUserByUsername⽅法 :

return org.springframework.security.core.userdetails.User
  .withUsername(user.getUsername())
  .password(user.getPassword())
  .roles("ADMIN")
  .authorities("USER_ADD", "USER_UPDATE")
  .build();

常用授权注解

//⽤户必须有 ADMIN ⻆⾊ 并且 ⽤户名是 admin 才能访问此⽅法
@PreAuthorize("hasRole('ADMIN') and authentication.name == 'admim'")
 @GetMapping("/list")
 public List<User> getList(){
    return userService.list();
 }

 //⽤户必须有 USER_ADD 权限 才能访问此⽅法
@PreAuthorize("hasAuthority('USER_ADD')")
 @PostMapping("/add")
 public void add(@RequestBody User user){
    userService.saveUserDetails(user);
}

用户授权失败

在WebSecurityConfig

 http
                .exceptionHandling()
                .accessDeniedHandler(new MyAccessDeniedHandler());//用户登录成功,认证失败
 public class MyAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, 
AccessDeniedException accessDeniedException) throws IOException, ServletException {
        //获取异常信息
        String message = accessDeniedException.getMessage();
        //创建map集合,将提示信息存储到map集合中去
        Map map=new HashMap();
        map.put("code",403);
        map.put("msg","您当前未被授权,不能访问");
        //将数据转储成json字符串格式
        String jsonString = JSON.toJSONString(map);
        //将json字符串写回给客户端
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(jsonString);
    }
 }

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

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

相关文章

python拼接字符串方法

文章目录 1. 使用加号&#xff08;&#xff09;2. 使用str.join()方法3. 使用格式化字符串&#xff08;f-strings, % 操作符, .format() 方法&#xff09;4. 使用列表推导式和join()结合 性能对比 在Python中&#xff0c;字符串拼接是将两个或多个字符串合并成一个新字符串的过…

C++初学(3)

面向对象编程&#xff08;OOP&#xff09;的本质是设计并拓展自己的数据类型&#xff0c;设计自己的数据类型就是让类型与数据匹配。内置的C类型分为两组&#xff1a;基本类型和复合类型。这里我们将介绍基本类型的整数和浮点数 3.1、简单变量 3.1.1、变量名 C必须遵循几种简…

理解常见开源协议的区别

本文将介绍几种常见的开源许可证&#xff0c;包括GPL、LGPL、MIT、Apache、BSD 和 木兰协议&#xff08;Mulan PSL&#xff09;&#xff0c;并详细解释它们的区别。 1. GPL (GNU General Public License) GPL 是最著名和最常用的开源许可证之一&#xff0c;由自由软件基金会 …

【前端 17】使用Axios发送异步请求

Axios 简介与使用&#xff1a;简化 HTTP 请求 在现代 web 开发中&#xff0c;发送 HTTP 请求是一项常见且核心的任务。Axios 是一个基于 Promise 的 HTTP 客户端&#xff0c;适用于 node.js 和浏览器&#xff0c;它提供了一种简单的方法来发送各种 HTTP 请求。本文将介绍 Axio…

如何在 Excel 中恢复临时文件

如果您在退出 Microsoft Excel 之前忘记保存重要的工作簿&#xff0c;这会令人烦恼和头疼。此外&#xff0c;在某些意外情况下&#xff0c;包括计算机突然崩溃、软件本身崩溃等&#xff0c;您精心制作的工作簿可能会消失。但是&#xff0c;您仍然可以使用Excel 临时服务恢复 Ex…

中山大学与Pixocial联手提出CatVTON:轻量化架构与高效训练,助力虚拟试衣技术落地应用!

近日&#xff0c;中山大学和 Pixocial 联合发布了 CatVTON&#xff0c;提出更加轻量化的架构与参数高效训练策略&#xff0c;助力图像虚拟试衣技术向落地应用迈进&#xff01; 项目已公开论文并开源权重和代码&#xff0c;更有在线 Demo 可以试玩&#xff01; 给钢铁侠穿上奇异…

Qt Creator初识

目录 一、认识 Qt Creator 1.Qt Creator 概览 2.使用 Qt Creator 新建项目 2.1 新建项目 2.2 选择项目模板 2.3 选择项目路径 2.4 选择构建系统 2.5 填写类信息设置界面 2.6 选择语言和翻译文件 2.7 选择 Qt 套件 2.8 选择版本控制系统 2.9 最终效果 3.认识 Qt Cre…

【详细】Ubuntu下安装qt5

Ubuntu下安装qt5 一. QT安装环境准备1、判断gcc是否安装2、安装g3、安装clang编译器4、安装 clang 5、安装make6、安装make-guile7、安装cmake 二. QT5安装1、安装Qt5的组件2、安装Qt的开发工具3、安装qtcreator4、安装qt55、安装qt charts&#xff08;可选&#xff09; 三、安…

VS2022创建C C++ GTEST工程

原因 需要对带代码进行单元测试&#xff0c;选择在Visual studio 中使用GTEST 框架。 实施 创建一个常规的控制台可执行程序。然后使用NUGET安装包 安装GTEST 头文件和动态库&#xff0c;同时安装GTEST ADAPTER。 安装可能提示找不到包源&#xff0c;此时需要根据提示配置一…

【克隆图】python刷题记录

R2-图 目录 DFS BFS ​ps: 图遍历即可&#xff0c;使用字典来记录访问过的结点。 DFS """ # Definition for a Node. class Node:def __init__(self, val 0, neighbors None):self.val valself.neighbors neighbors if neighbors is not None else []…

苹果CMS:资源采集站如何设置定时采集详细教程讲解

我们搭建好站点之后&#xff0c;会自定义一些采集&#xff0c;但是需要每天去手动执行&#xff0c;有时候甚至会忘记&#xff0c;那我们如何处理呢&#xff1f;今天我们就来介绍一下如何设置定时器。 如果按照官方例子来设置定时器会遇到一个问题就是采集的资源未绑定类型&…

极简Springboot+Mybatis-Plus+Vue零基础萌新都看得懂的分页查询(富含前后端项目案例)

目录 springboot配置相关 依赖配置 yaml配置 MySQL创建与使用 &#xff08;可拿软件包项目系统&#xff09; 创建数据库 创建数据表 mybatis-plus相关 Mapper配置 ​编辑 启动类放MapperScan 启动类中配置 添加config配置文件 Springboot编码 实体类 mapperc(Dao…

Chiplet SPI User Guide 详细解读

目录 一. 基本介绍 1.1.整体结构 1.2. 结构细节与功能描述 二. 输入输出接口 2.1. IO Ports for SPI Leader 2.2. IO Ports for SPI Follower 2.3. SPI Mode Configuration 2.4. Leader IP和Follower IP功能图 三. SPI Programming 3.1. Leader Register Descripti…

ubuntu 配置opencv-python-imsow()报错

python调用imshow&#xff08;&#xff09;时出现下面的错误&#xff1a; error: (-2:Unspecified error) The function is not implemented. Rebuild the library with Windows, GTK 2.x or Cocoa support. If you are on Ubuntu or Debian, install libgtk2.0-dev and pkg-c…

六个开源的PDF转Markdown项目

✨ 1: gptpdf gptpdf 是一个利用VLLM解析PDF为Markdown的工具&#xff0c;几乎完美支持数学公式、表格等。 GPTPDF 是一个使用视觉大模型&#xff08;如 GPT-4o&#xff09;将 PDF 文件解析成 Markdown 文件的工具。它主要用于高效地解析 PDF 文档中的排版、数学公式、表格、…

springboot专利信息服务管理系统-计算机毕业设计源码97187

目录 摘要 1 绪论 1.1 选题背景与意义 1.2国内外研究现状 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1系统开发流程 2.2.2 用户登录流程 2.2.3 系统操作流程 2.2.4 添加信息流程 2.2.5 修改信息流程 2.2.6 删除信息流程 2.3 系统功能…

【王佩丰 Excel 基础教程】第三讲:查找、替换、定位

文章目录 前言一、查找与替换1.1、按值查找1.2、按格式查找1.3、是否开启单元格匹配1.4、模糊查询 二、定位工具2.1、名称框的相关操作2.2、批注的相关介绍2.2.1、批注的基本操作2.2.2、批注的格式 2.3、使用 “ 定位条件 ” 解决以下问题 总结 前言 跟着B站学习王佩丰 Excel …

LLM工具调用破局:Few-shot Prompting

在大型语言模型&#xff08;LLM&#xff09;的应用中&#xff0c;工具的使用至关重要。我们一直在研究如何提升LLM调用工具的性能。一种常见的提升方法是通过少量样本提示&#xff0c;即将一些模型输入的示例和期望的输出结果直接展示给模型。据Language Models are Few-Shot L…

Jmeter下载、安装、永久汉化(Windows环境)

1、JDK下载 JDK8下载地址https://www.oracle.com/java/technologies/downloads/#java8-windows JDK8的Windows的64位&#xff1a; 2、Jmeter下载 jmeter下载地址https://jmeter.apache.org/download_jmeter.cgi 3、配置环境变量 安装好后&#xff0c;把jdk和jmeter都配置到…

4.JAVA-运算符

算数运算符 隐式类型转换 强制转换 字符串操作 字符相加 小结 自增自减运算符 赋值运算符 关系运算符 逻辑运算符 短路逻辑运算 三元运算符 运算符优先级 这里小括号优先于所有&#xff0c;所以想要哪一个优先运算&#xff0c;就可以将哪一个用小括号扩起来&#xff0c;比较方便…