新版SpringSecurity5.x使用与配置

news2024/9/22 11:38:13

目录

一、了解SpringSecurity

1.1 什么是Spring Security?

1.2 Spring Security功能

1.3 Spring Security原理

1.4 RABC (Role-Based Access Control)

二、SpringSecurity简单案例

2.1 引入SpringSecurity依赖

 2.2 创建一个简单的Controller

三、SpringSecurity配置

3.1 自定义用户与密码

3.2 允许匿名访问路径

3.3 数据库实现登陆校验

3.4 实现角色权限访问

3.5 对密码进行B加密

3.6 结合Jwt实现多重校验


一、了解SpringSecurity

1.1 什么是Spring Security?

Spring Security 是一个强大且高度可定制的身份验证和访问控制框架。它是 Spring 生态系统的一部分,为基于 Spring 的应用提供了全面的安全服务。Spring Security 的设计目标是为应用的安全需求提供一个完整的解决方案,同时保持高度的灵活性和可扩展性。

1.2 Spring Security功能

Spring Security 提供的功能包括但不限于:

  • 认证(Authentication):验证用户身份,通常需要用户名和密码。
  • 授权(Authorization):确定已认证的用户可以访问哪些资源或执行哪些操作。
  • CSRF 保护:防止跨站请求伪造攻击。
  • 会话管理:处理用户的会话,包括会话的创建、维护和销毁。
  • 加密和编码:提供加密和散列算法的支持。
  • OAuth2 和 OpenID Connect 支持:集成 OAuth2 和 OpenID Connect 协议,实现第三方认证。
  • CORS 支持:处理跨域资源共享(Cross-Origin Resource Sharing)请求。
  • 安全配置:允许通过 XML 或 Java 配置来定制安全策略。
1.3 Spring Security原理

Spring Security 的工作原理涉及几个关键组件:

  • SecurityContext:存储认证信息,如当前登录用户和他们的权限。
  • AuthenticationManager:负责用户认证,通常使用 UserDetailsService 来加载用户信息。
  • AccessDecisionManager:决定用户是否有权访问特定资源。
  • Filter Chain:一系列的过滤器处理请求和响应,例如 UsernamePasswordAuthenticationFilter 用于处理用户名和密码的提交。

1.4 RABC (Role-Based Access Control)

RABC,即基于角色的访问控制,是一种常见的访问控制机制,用于管理用户对资源的访问权限。在 RABC 中,权限不是直接授予用户,而是授予用户所属的角色。每个用户可以拥有一个或多个角色,而每个角色则有一组相应的权限。这种机制简化了权限管理,因为只需更改用户的角色就可以改变他们的权限集。


二、SpringSecurity简单案例

2.1 引入SpringSecurity依赖

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

 2.2 创建一个简单的Controller

@RestController
public class HelloController {
    @GetMapping("Hello")
    public String Hello(){
        return "Hello SpringSecurity";
    }
}

2.3 运行后访问localhost:8080/Hello,会自动跳转到localhost:8080/login

这里的用户密码都在启动时候的控制台上  (注意:每一次启动密码都不一样)

用户名:user

密码:    b82aa5e5-0a3a-466b-90a8-e94098877823(控制台上的一串密码)

登陆后成功访问


三、SpringSecurity配置

后面的配置都是基于小案例的基础上实现,请先完成上述的小案例

3.1 自定义用户与密码

由于每一次生成密码都是不固定的,对调试并不友好,springSecurity可以通过在application.yml中进行自定义设置用户和密码

spring:
  security:
    user:
      name: alphaMilk
      password: 123456

输入用户名和密码即可正常登陆并访问资源


3.2 允许匿名访问路径

        在Spring Security框架中,允许某些路径或资源在未经过身份验证的情况下被访问,通常称为“允许匿名访问”。这种配置对于公共页面、登录页面、注册页面、API文档等是非常必要的,因为这些页面或资源需要对所有用户开放,无论他们是否已经登录。

以下通过配置类实现,用户能够匿名访问login页面

// 使用@Configuration标记此类为Spring的配置类
@Configuration
// 启用WebSecurity的自动配置,以便Spring Security可以管理Web安全
@EnableWebSecurity
public class SecurityConfiguration {

    // 定义一个名为securityFilterChain的bean,该bean将负责构建和应用安全过滤器链
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // 配置HttpSecurity对象,定义安全规则
        http
                // 授权HTTP请求,定义哪些URL需要什么类型的访问控制
                .authorizeHttpRequests((authz) -> authz
                        // 允许"/user/login" URL匿名访问
                        .requestMatchers("/user/login").anonymous()
                        
                        // 所有其他请求都需要认证才能访问
                        .anyRequest().authenticated())
                
                // 启用HTTP Basic认证,默认情况下提供简单的用户名/密码认证
                .httpBasic(Customizer.withDefaults());

        // 构建并返回SecurityFilterChain
        return http.build();
    }
}

创建一个LoginController类

@RestController
@RequestMapping("user")
public class LoginController {
    
    @GetMapping("/login")
    public String Login(){
        return "这是登陆资源页面";
    }
}

重启系统后直接访问,不需要登陆即可获取资源.


3.3 数据库实现登陆校验

通过自己数据库的用户和密码,实现登陆。将之前的自定义用户密码(application.yml中)都删除掉,并执行以下操作:

用户表单:

-- 创建一个包含用户信息和角色的简化表
CREATE TABLE IF NOT EXISTS `users` (
    `id` INT AUTO_INCREMENT PRIMARY KEY,          -- 用户ID,自增主键
    `username` VARCHAR(255) NOT NULL UNIQUE,      -- 用户名,唯一且不能为空
    `password` VARCHAR(255) NOT NULL,             -- 密码,存储加密后的密码
    `role` ENUM('ROLE_USER', 'ROLE_ADMIN') NOT NULL, -- 角色,预定义为'ROLE_USER'或'ROLE_ADMIN'
    `enabled` TINYINT(1) NOT NULL DEFAULT 1       -- 用户状态,1表示启用,0表示禁用
);

注意:这里的role需要按照ROLE_身份 的方式进行存储以便springsecurity进行权限访问控制

插入案例数据 :

-- 插入示例用户数据
INSERT INTO `users` (`username`, `password`, `role`, `enabled`)
VALUES
    ('user1', '123456', 'ROLE_USER', 1),
    ('admin1', '123456', 'ROLE_ADMIN', 1),
    ('disabledUser', '123456', 'ROLE_USER', 0);

 引入依赖:

<!--        数据库依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.5</version>
            <exclusions>
                <exclusion>
                    <groupId>org.mybatis</groupId>
                    <artifactId>mybatis-spring</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>3.0.3</version>
        </dependency>
        
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>

配置数据源:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/ap_security?characterEncoding=utf-8&serverTimezone=UTC
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

 创建User实体与mapper并加上启动注释

User实体

@TableName(value ="users")
@Data
public class Users implements Serializable {
    /**
     * 
     */
    @TableId(type = IdType.AUTO)
    private Integer id;

    /**
     * 
     */
    private String username;

    /**
     * 
     */
    private String password;

    /**
     * 
     */
    private Object role;

    /**
     * 
     */
    private Integer enabled;

    @TableField(exist = false)
    private static final long serialVersionUID = 1L;

    @Override
    public boolean equals(Object that) {
        if (this == that) {
            return true;
        }
        if (that == null) {
            return false;
        }
        if (getClass() != that.getClass()) {
            return false;
        }
        Users other = (Users) that;
        return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
            && (this.getUsername() == null ? other.getUsername() == null : this.getUsername().equals(other.getUsername()))
            && (this.getPassword() == null ? other.getPassword() == null : this.getPassword().equals(other.getPassword()))
            && (this.getRole() == null ? other.getRole() == null : this.getRole().equals(other.getRole()))
            && (this.getEnabled() == null ? other.getEnabled() == null : this.getEnabled().equals(other.getEnabled()));
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
        result = prime * result + ((getUsername() == null) ? 0 : getUsername().hashCode());
        result = prime * result + ((getPassword() == null) ? 0 : getPassword().hashCode());
        result = prime * result + ((getRole() == null) ? 0 : getRole().hashCode());
        result = prime * result + ((getEnabled() == null) ? 0 : getEnabled().hashCode());
        return result;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(getClass().getSimpleName());
        sb.append(" [");
        sb.append("Hash = ").append(hashCode());
        sb.append(", id=").append(id);
        sb.append(", username=").append(username);
        sb.append(", password=").append(password);
        sb.append(", role=").append(role);
        sb.append(", enabled=").append(enabled);
        sb.append(", serialVersionUID=").append(serialVersionUID);
        sb.append("]");
        return sb.toString();
    }
}

UserMapper

public interface UsersMapper extends BaseMapper<Users> {

}

@MapperScan

@SpringBootApplication
@MapperScan("com.example.mysecurity.mapper")
public class MySecurityApplication {

    public static void main(String[] args) {
        SpringApplication.run(MySecurityApplication.class, args);
    }

}

测试mybatisPlus是否配置正确

@SpringBootTest
class MySecurityApplicationTests {

    @Autowired
    private UsersMapper usersMapper;

    @Test
    void contextLoads() {
        List<Users> users = usersMapper.selectList(null);
        System.out.println(users);
    }

}

通过后,即可开始实现通过自己数据库进行登陆功能:

先创建返回的验证类

 LoginUser 实现 UserDetails

@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginUser implements UserDetails {

    private Users user;


    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

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

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

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

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

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

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

登陆实现类

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UsersMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据用户名查询用户信息
        LambdaQueryWrapper<Users> wrapper = new LambdaQueryWrapper<>();

        wrapper.eq(Users::getUsername,username);
        Users user = userMapper.selectOne(wrapper);

        //如果查询不到数据就通过抛出异常来给出提示
        if(Objects.isNull(user)){
            throw new RuntimeException("用户名或密码错误");
        }


        Collection<? extends GrantedAuthority> authorities = Collections.singletonList(new SimpleGrantedAuthority(user.getRole()));

        //封装成UserDetails对象返回 
        return new LoginUser(user,authorities);
    }
}

        由于在Spring Boot 2.3及更高版本中,Spring Security默认不再提供任何内置的PasswordEncoder。这意味着如果在配置中直接使用明文密码或没有正确配置PasswordEncoder,你将看到这个异常。这里暂时先使用明文加密。后面将一步步完善加密功能.

在SecurityConfig中加入配置

@Configuration
// 启用WebSecurity的自动配置,以便Spring Security可以管理Web安全
@EnableWebSecurity
public class SecurityConfiguration {

//    设置密码加密为明文加密
    @Bean
    public org.springframework.security.crypto.password.PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
    
    // 定义一个名为securityFilterChain的bean,该bean将负责构建和应用安全过滤器链
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // 配置HttpSecurity对象,定义安全规则
        http
                // 授权HTTP请求,定义哪些URL需要什么类型的访问控制
                .authorizeHttpRequests((authz) -> authz
                        // 允许"/user/login" URL匿名访问
                        .requestMatchers("/user/login").anonymous()
                        
                        // 所有其他请求都需要认证才能访问
                        .anyRequest().authenticated())
                
                // 启用HTTP Basic认证,默认情况下提供简单的用户名/密码认证
                .httpBasic(Customizer.withDefaults());

        // 构建并返回SecurityFilterChain
        return http.build();
    }
}

再次访问localhost:8080/Hello后弹出登陆框:

输入任意的用户与密码即可正常访问


3.4 实现角色权限访问

在Controller中定义一个Admin资源类,只有admin用户才能进行访问

@RequestMapping("admin")
@RestController
@Slf4j
public class AdminController {

    @GetMapping("resourse")
    public String AdminRole(){
        return "这是只有管理员用户才能访问的资源";
    }

}

在设置中进行配置

@Configuration
// 启用WebSecurity的自动配置,以便Spring Security可以管理Web安全
@EnableWebSecurity
public class SecurityConfiguration {

//    设置密码加密为明文加密
    @Bean
    public org.springframework.security.crypto.password.PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    // 定义一个名为securityFilterChain的bean,该bean将负责构建和应用安全过滤器链
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // 配置HttpSecurity对象,定义安全规则
        http
                // 授权HTTP请求,定义哪些URL需要什么类型的访问控制
                .authorizeHttpRequests((authz) -> authz
                        .requestMatchers("/user/login").anonymous()
//                        需要有Admin身份的用户才能进行访问
                        .requestMatchers("/admin/**").hasRole("ADMIN")
                        // 所有其他请求都需要认证才能访问
                        .anyRequest().authenticated())
                
                // 启用HTTP Basic认证,默认情况下提供简单的用户名/密码认证
                .httpBasic(Customizer.withDefaults());

        // 构建并返回SecurityFilterChain
        return http.build();
    }
}

重启服务器后分别用两种身份进行访问

用户访问:

管理员访问:


3.5 对密码进行B加密

config中进行配置BCrypt

@Configuration
// 启用WebSecurity的自动配置,以便Spring Security可以管理Web安全
@EnableWebSecurity
public class SecurityConfiguration {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;


//    设置密码加密为B加密
    @Bean
    public PasswordEncoder passwordEncoder() {
     return new BCryptPasswordEncoder();
    }


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



    // 定义一个名为securityFilterChain的bean,该bean将负责构建和应用安全过滤器链
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // 配置HttpSecurity对象,定义安全规则
        http
                // 授权HTTP请求,定义哪些URL需要什么类型的访问控制
                .authorizeHttpRequests((authz) -> authz
                        .requestMatchers("/user/login").anonymous()
//                        需要有Admin身份的用户才能进行访问
                        .requestMatchers("/admin/**").hasRole("ADMIN")
                        // 所有其他请求都需要认证才能访问
                        .anyRequest().authenticated())
                
                // 启用HTTP Basic认证,默认情况下提供简单的用户名/密码认证
                .httpBasic(Customizer.withDefaults());

        // 构建并返回SecurityFilterChain
        return http.build();
    }
}

由于数据库中都是明文的密码,所以这里可以通过创建一个SpringbootTest类,将所有用户的密码改为B加密后的数据.

    @Test
    public void testUpdateAllPasswords() {
        // 创建一个BCryptPasswordEncoder实例
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

        // 更新所有用户的密码为加密后的"123456"
        String encodedPassword = encoder.encode("123456");

        // 构造更新条件
        LambdaUpdateWrapper<Users> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(Users::getPassword, encodedPassword);

        // 执行更新操作
        boolean result = usersMapper.update(null, updateWrapper) > 0;

        if (result) {
            System.out.println("所有用户的密码更新成功!");
        } else {
            System.out.println("密码更新失败!");
        }
        
    }

配置好后,重新进行登陆查看输入对应的admin1和123456


3.6 结合Jwt实现多重校验

 Spring Security 和 JSON Web Tokens (JWT) 可以协同工作来提供更灵活和安全的身份验证和授权机制。尽管 Spring Security 提供了一套全面的安全框架,但它默认使用基于会话的认证机制,这意味着服务器维护着与客户端的活动会话状态。而JWT提供了一种无状态的认证方式,这意味着每个请求都包含完整的认证信息,无需服务器保存会话状态。

pom文件中引入jwt所需要的依赖

<!--        JWT依赖-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

Jwt资源类:

@Component
@ConfigurationProperties(prefix = "jwt")
@Data
public class JwtProperties {

    private String SecretKey;
    private long Ttl;
    private String TokenName;


}

Jwt yml配置:

jwt:
  secret-key: Alphamilk
  token-name: Authorization
  ttl: 10800000

实现Jwt的工具类

@Component
public class JwtUtil {

    @Autowired
    private JwtProperties jwtProperties;

    public String createJWT(Map<String, Object> claims, long ttlMillis) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long expMillis = System.currentTimeMillis() + ttlMillis;
        Date exp = new Date(expMillis);

        return Jwts.builder()
                .setClaims(claims)
                .signWith(signatureAlgorithm, jwtProperties.getSecretKey().getBytes(StandardCharsets.UTF_8))
                .setExpiration(exp)
                .compact();
    }

    public Claims parseJWT(String token) {
        return Jwts.parser()
                .setSigningKey(jwtProperties.getSecretKey().getBytes(StandardCharsets.UTF_8))
                .parseClaimsJws(token)
                .getBody();
    }

    public boolean isTokenValid(String token, UserDetails userDetails) {
        final String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }

    private String getUsernameFromToken(String token) {
        Claims claims = parseJWT(token);
        return (String) claims.getSubject();
    }

    private boolean isTokenExpired(String token) {
        try {
            final Date expiration = parseJWT(token).getExpiration();
            return expiration.before(new Date());
        } catch (ExpiredJwtException e) {
            return true;
        }
    }
}

Jwt的校验Filter

@Component
public class JwtFilter extends OncePerRequestFilter {

    @Autowired
    private JwtProperties jwtProperties;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtUtil jwtUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token = request.getHeader(jwtProperties.getTokenName());

        if (token != null) {
            try {
                Claims claims = jwtUtil.parseJWT(token);
                String username = (String) claims.get("userName");

                if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                    UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
                    if (jwtUtil.isTokenValid(token, userDetails)) {
                        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                                userDetails, null, userDetails.getAuthorities());
                        SecurityContextHolder.getContext().setAuthentication(authentication);
                    }
                }
            } catch (ExpiredJwtException ex) {

                response.sendError(HttpServletResponse.SC_FORBIDDEN, "Token has expired.");
            } catch (JwtException ex) {

                response.sendError(HttpServletResponse.SC_FORBIDDEN, "Invalid token.");
            }
        }else {
            response.sendError(HttpServletResponse.SC_FORBIDDEN, "No token provided.");
        }
        filterChain.doFilter(request, response);
    }
}

将jwt校验规则加入到Spring的Filter中进行校验

@Configuration
// 启用WebSecurity的自动配置,以便Spring Security可以管理Web安全
@EnableWebSecurity
public class SecurityConfiguration {

    @Autowired

    private JwtFilter jwtFilter;


    @Autowired
    private UserDetailsServiceImpl userDetailsService;


//    设置密码加密为B加密
    @Bean
    public PasswordEncoder passwordEncoder() {
     return new BCryptPasswordEncoder();
    }


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



    // 定义一个名为securityFilterChain的bean,该bean将负责构建和应用安全过滤器链
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // 配置HttpSecurity对象,定义安全规则
        http
                // 授权HTTP请求,定义哪些URL需要什么类型的访问控制
                .authorizeHttpRequests((authz) -> authz
                        .requestMatchers("/user/login").anonymous()
//                        需要有Admin身份的用户才能进行访问
                        .requestMatchers("/admin/**").hasRole("ADMIN")
                        // 所有其他请求都需要认证才能访问
                        .anyRequest().authenticated())

                // 启用HTTP Basic认证,默认情况下提供简单的用户名/密码认证
                .httpBasic(Customizer.withDefaults());
//        加入jwtFIlter
        http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
        // 构建并返回SecurityFilterChain
        return http.build();
    }
}

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

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

相关文章

【数据结构】初识集合框架

&#x1f387;&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了 博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳&#xff0c;欢迎大佬指点&#xff01; 人生格言: 当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友…

自己编写一个谷歌浏览器插件, 模拟某音直播间自动发消息

闲来没事&#xff0c; 做个插件玩一玩&#xff0c;于是一顿学习。 按照浏览器插件规范&#xff0c;一顿代码编写&#xff0c; 搞了一个简单的插件。仅做学习。 可以实现在直播间自动发消息。 定时轮发。 实现原理&#xff1a; 利用谷歌popub.js 发送消息。 在content-script.…

【无人机】低空经济中5G RedCap芯片的技术分析报告

1. 引言 图一. 新基建&#xff1a;低空经济 低空经济作为一种新兴的经济形态&#xff0c;涵盖了无人机、电动垂直起降飞行器&#xff08;eVTOL&#xff09;、低空物流、空中交通管理等多个领域。随着5G网络的普及和演进&#xff0c;5G RedCap&#xff08;Reduced Capability&a…

【功能】DOTween动画插件使用

一、下载安装DOTween插件&#xff0c;下载地址&#xff1a;DOTween - Asset Store (unity.com) 使用 Free免费版本即可&#xff0c;导入成功后&#xff0c;Project视图中会出现 DOTween 文件夹 二、使用案例 需求1&#xff1a;控制材质球中的某个属性值&#xff0c;实现美术需…

MS17-010漏洞复现+利用

1、 漏洞简述 漏洞名称&#xff1a;“永恒之蓝”漏洞 漏洞编号&#xff1a;MS17-010&#xff0c;CVE-2017-0143/0144/0145/0146/0147/0148 漏洞类型&#xff1a;缓冲区溢出漏洞 漏洞影响&#xff1a;信息泄露 CVSS评分&#xff1a;9.3&#xff08;High&#xff09; 利用难…

ABAP使用SQL直接更新数据库与使用IN UPDATE TASK的区别

1. 背景 刚接触ABAP的小伙伴常常会有这样的疑问&#xff0c;为什么不直接使用Open SQL直接更新数据库&#xff0c;而要把对DB的操作封装到IN UPDATE TASK中呢&#xff1f; 对于这个问题&#xff0c;比较常见的解释是&#xff0c;IN UPDATE TASK的方式会保证数据更新的一致性。…

HTML零基础自学笔记(上)-7.18

HTML零基础自学笔记&#xff08;上&#xff09; 参考&#xff1a;pink老师一、HTML, Javascript, CSS的关系是什么?二、什么是HTML?1、网页&#xff0c;网站的概念2、THML的基本概念3、THML的骨架标签/基本结构标签 三、HTML标签1、THML标签介绍2、常用标签图像标签&#xff…

09 B端产品业务调研的分析框架(2)

产品经理要有建立从企业全局的视角去分析业务的思维模式和习惯&#xff0c;面对不熟悉的复杂业务&#xff0c;可参考下面业务分析架构图进行分析&#xff1a; 业务分析框架图 战略层 战略&#xff1a;价值方案和计划。 使命&#xff1a;公司因何而存在。 愿景&#xff1a;公…

R语言画散点图-饼图-折线图-柱状图-箱线图-等高线图-曲线图-热力图-雷达图-韦恩图(三D)

R语言画散点图-饼图-折线图-柱状图-箱线图-等高线图-曲线图-热力图-雷达图-韦恩图&#xff08;三D&#xff09; 散点图使用 plotly 包示例解析效果 使用 scatterplot3d 包示例解析效果 饼图使用 plotly 包示例解析效果 使用 plotrix 包示例解析效果 折线图使用 plotly 包示例解…

算法日记day 16(二叉树的广度优先遍历|反转、对称二叉树)

一、二叉树的层序遍历 题目&#xff1a; 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3]…

使用 PVE 自签 CA 证书签发新证书

前言 PVE 安装时会自动创建一个有效期 10 年的 CA 证书, 我们可以利用这个 CA 证书给虚拟机中的 Web 应用签发新的 TLS 证书用于提供 HTTPS 服务. 下面以 PVE 虚拟机中通过 Docker 跑的一个 雷池 应用为例进行演示. PVE 证书位置 官方文档: https://pve.proxmox.com/wiki/Pr…

【BUG】已解决:TypeError: Descriptors cannot not be created directly.

已解决&#xff1a;TypeError: Descriptors cannot not be created directly. 目录 已解决&#xff1a;TypeError: Descriptors cannot not be created directly. 【常见模块错误】 【错误原因】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来…

STM32项目分享:智能宠物喂食系统

目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 1.PCB图 五、程序设计 六、实验效果 七、资料内容 项目分享 一、前言 项目成品图片&#xff1a; 哔哩哔哩视频链接&#xff1a; https://www.bilibili.com/video/BV1zy411z7…

知名在线市场 Etsy 允许在其平台上销售 AI 艺术品,但有条件限制|TodayAI

近日&#xff0c;以手工和复古商品著称的在线市场 Etsy 宣布&#xff0c;将允许在其平台上销售 AI 生成的艺术品。这一举措引发了广泛关注和争议。尽管 Etsy 正在接受 AI 艺术的潮流&#xff0c;但平台对这一类商品的销售设置了一些限制。 根据 Etsy 新发布的政策&#xff0c;…

C#开发:PowerDesigner建表和Navicat导入数据

一、打开Powerdesigner&#xff0c;新建一个模型&#xff0c;点击ok 二、用工具面板拖拽出一个数据表 &#xff08;如果没有工具面板&#xff0c;请在如下操作中开启&#xff09; 三、双击刚刚的拖拽出来的表&#xff0c;设计表的字段&#xff0c;可以添加注释说明 【备注】…

开源智能助手平台Dify是什么?

1.背景 对于国内小公司&#xff0c;怎样通过Ai 将内部流程、产品重新做一次&#xff0c;从而提高人效、给客户带来价值&#xff0c;这是老板们在考虑的问题 &#xff1f; 当前市面上的你大模型例如&#xff1a;通义千问、文心一言、kimi、智谱清言、盘古 等&#xff0c;底层能…

【LeetCode】填充每个节点的下一个右侧节点指针 II

目录 一、题目二、解法完整代码 一、题目 给定一个二叉树&#xff1a; struct Node { int val; Node *left; Node *right; Node *next; } 填充它的每个 next 指针&#xff0c;让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点&#xff0c;则将 next 指针设置为 NUL…

【栈和队列】算法题 ---- 力扣

通过前面栈和队列的学习&#xff0c;现在来看这些算法题目 一、有效的括号 本题让判断括号是否有效 第一眼看可能没一点思路&#xff0c;但仔细分析一下&#xff1b; 我们学习过栈数据结构&#xff0c;知道栈先进后出的原则&#xff0c;那我们就可以使用啊&#xff1b;把题目的…

认识和安装R的扩展包,什么是模糊搜索安装,工作目录和空间的区别与设置

R语言以其强大的功能和灵活的扩展性,成为了无数数据分析师和研究者的首选工具。R的丰富功能和海量扩展包直接相关,但如何高效管理这些扩展包,进而充分发挥R的强大潜力?本文将为您揭示这些问题的答案。 一、R的扩展包 R的包(packages)是由R函数、数据和预编译代码组成的一…

PyQT6---环境搭建

1、虚拟环境搭建 创建虚拟环境 create -n pyqt6_39 python3.9 切换虚拟环境 conda activate pyqt6_39 2、安装pyqt6 安装pyqt6和pyqt6-tools pip install PyQt6 -i https://pypi.tuna.tsinghua.edu.cn/simplepip install pyqt6-tools -i https://pypi.tuna.tsinghua.edu.cn/…