Springsecurty【2】认证连接MySQL

1.前期准备

基于Spring Initializr创建SpringBoot项目(基于SpringBoot 2.7.12版本),实现与MyBatisPlus的项目整合。分别导入:CodeGeneratorMyBatisPlusConfig

CodeGenerator:用于MybatisPlus代码生成; MyBatisPlusConfigMyBatisPlus配置类,实现了分页和乐观锁相关配置。

1.1.添加pom.xml依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
    <version>5.1.44</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.2</version>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.5.2</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>2.0.32</version>
</dependency>

1.2.配置application.yml

server:
    port: 8080
spring:
    freemarker:
        # 设置freemarker模板后缀
        suffix: .ftl
        # 设置freemarker模板前缀
        template-loader-path: classpath:/templates/
        enabled: true
#    security:
#        user:
#            name: admin
#            password: 123456
    datasource:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: 123456
        url: jdbc:mysql://localhost:3306/bookshop?useUnicode=true&characterEncoding=utf8&useSSL=false

修改数据库相关账号、密码及数据库名。

1.3.导入相关数据表

将课件资料中的sql/db.sql导入到数据库中。

表名说明
sys_user用户信息表
sys_role角色信息表
sys_module模块信息表(权限信息表)
sys_user_role用户角色表
sys_role_module角色模块表

表之间的关系说明:

1.4.实现MP代码生成

直接运行CodeGenerator.java类,生成sys_开头的相关信息表。

此处省略一万字...

2.SpringSecurity之认证

2.1.导入依赖

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

2.2.创建登录页及首页

基于freemarker模板引擎创建登录页login.ftl和首页index.ftl

login.ftl首页部分代码如下:

<h1>用户登录</h1>
<form action="/user/userLogin" method="post">
    <label>账号:</label><input type="text" name="username"/><br/>
    <label>密码:</label><input type="password" name="password"/><br/>
    <input type="submit" value="登 录"/>
</form>

index.ftl代码如下:

<h1>首页</h1>
<div style="position: absolute;top:15px;right:15px;"><a href="/logout">安全退出</a></div>

href="/logout"SpringSecurity配置的退出请求路径。主要完成以下几个操作:

  1. 清除指定 CookieCookieClearingLogoutHandler#logout

  2. 清除 remember-mePersistentTokenBasedRememberMeServices#logout

  3. 使当前 Session无效,清空当前的 SecurityContext 中认证用户信息 Authentication

2.3.创建配置Controller

创建IndexController配置跳转首页和登录页的接口。

@Controller
public class IndexController {
​
    @RequestMapping("/")
    public String toLogin(){
        return "login";
    }
​
    @RequestMapping("/index")
    public String toIndex(){
        return "index";
    }
}

UserController中配置请求登录接口:

@RestController
@RequestMapping("/user")
public class UserController {
​
    @RequestMapping("/userLogin")
    public String userLogin(User user){
        return "login";
    }
    
}

2.4.用户认证

2.4.1.用户对象UserDetails(UserDetails用来处理前端发送的账号密码请求)

修改User并实现UserDetails

@Getter
@Setter
@TableName("sys_user")
public class User implements Serializable, UserDetails {
    ...
​
    /**
     * 用户权限集合
     * @return
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }
​
    /**
     * 用户没过期返回true,反之则false
     */
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    /**
     * 用户没锁定返回true,反之则false
     */
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    /**
     * 用户凭据(通常为密码)没过期返回true,反之则false
     */
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    /**
     * 用户是启用状态返回true,反之则false
     */
    @Override
    public boolean isEnabled() {
        return true;
    }
}

UserDetails是Spring Security框架中的一个接口,它代表了应用程序中的用户信息。UserDetails接口定义了一组方法,用于获取用户的用户名、密码、角色和权限等信息,以便Spring Security可以使用这些信息进行身份验证和授权。

以下是UserDetails接口中定义的方法:

  • getUsername():获取用户的用户名。

  • getPassword():获取用户的密码。

  • getAuthorities():获取用户的角色和权限信息。

  • isEnabled():判断用户是否可用。

  • isAccountNonExpired():判断用户的账号是否过期。

  • isAccountNonLocked():判断用户的账号是否被锁定。

  • isCredentialsNonExpired():判断用户的凭证是否过期。

自定义用户信息时,可以实现UserDetails接口并覆盖其中的方法来提供自己的用户信息。

2.4.2.业务对象UserDetailsService

修改UserServiceImpl并实现UserDetailsService,重写loadUserByUsername(String username)方法。

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
            implements UserService,UserDetailsService {
​
    /**
     * 实现Spring Security内置的UserDetailService接口,重写loadUserByUsername方法实现数据库的身份校验
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据用户名查询数据库中用户信息
        User user = this.getOne(new QueryWrapper<User>().eq("username", username));
        //判断用户是否存在
        if(Objects.isNull(user))
            throw new UsernameNotFoundException("用户不存在");
        //权限校验TODO,后续讲解
        return user;
    }
}

UserDetailsService是Spring Security中的一个接口,它用于从特定数据源(如数据库)中获取用户详细信息,以进行身份验证和授权。实现该接口的类需要实现loadUserByUsername方法,该方法根据给定的用户名返回一个UserDetails对象,该对象包含有关用户的详细信息,例如密码、角色和权限等。在Spring Security中,UserDetailsService通常与DaoAuthenticationProvider一起使用,后者是一个身份验证提供程序,用于验证用户的凭据。

2.4.3.SecurityConfig配置

创建WebSecurityConfig配置类,配置SpringSecurity结合数据库方式进行身份认证和权限鉴定。

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
​
    /**
     * 基于数据库方式进行身份认证和权限鉴定
     */
    @Autowired
    private UserDetailsService userDetailsService;
    
    /**
     * 配置密码编码器,首次采用明文密码方式进行比对校验
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }
    
     /**
     * 获取AuthenticationManager(认证管理器),登录时认证使用(基于数据库方式)
     * @param
     * @return
     * @throws Exception
     */
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        //创建DaoAuthenticationProvider
        DaoAuthenticationProvider provider=new DaoAuthenticationProvider();
        //设置userDetailsService,基于数据库方式进行身份认证
        provider.setUserDetailsService(userDetailsService);
        //配置密码编码器
        provider.setPasswordEncoder(passwordEncoder());
        return new ProviderManager(provider);
    }
    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
​
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .anyRequest().authenticated()
            .and()
                .formLogin()
                .loginPage("/")
                .loginProcessingUrl("/user/userLogin")
                .passwordParameter("password")
                .usernameParameter("username")
                // 认证成功 redirect跳转,根据上一保存请求进行成功跳转
                .defaultSuccessUrl("/index")
                // 认证成功 forward跳转,始终在认证成功之后跳转到指定请求
//                .successForwardUrl("/index")
//                .failureForwardUrl("/")
               /* .successHandler((request, response, exception)->{
                    response.sendRedirect("/index");
                })*/
                .failureHandler((request, response, exception) -> {
                    request.setAttribute("msg",exception.getMessage());
                    request.getRequestDispatcher("/").forward(request,response);
                })
            .and()
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/")
            .and()
                .csrf().disable()
                ;
        return http.build();
    }
}

这里需要注意的是formLogin认证失败后将不在使用failureForwardUrl()方法转发,而是使用failureHandler处理器方式处理错误信息并跳转页面。

  • 如果使用failureForwardUrl()方式,请到ForwardAuthenticationFailureHandler源码中查看错误信息封装:

SpringSecurity将认证错误信息保存到Request作用域中,并取名为SPRING_SECURITY_LAST_EXCEPTION,所以直接可以到前端页面使用${SPRING_SECURITY_LAST_EXCEPTION}方式进行获取展示。

  • 如果使用failureHandler处理器方式,则可以自定义错误页面及错误信息:

.failureHandler((request, response, exception) -> {
    //将认证错误信息保存到request作用域,取名为msg
    request.setAttribute("msg",exception.getMessage());
    //认证失败后转发到指定页面
    request.getRequestDispatcher("/").forward(request,response);
})

本次案例使用的是自定义failureHandler处理器方式,修改登录页面login.ftl加入错误信息展示区域:

<div>${msg!}</div>

2.5.启动测试

在sys_user表中添加测试用户信息,此处请使用明文密码方式进行身份验证。

修改IndexController中的跳转到首页的方法,加入获取SpringSecurity认证信息并带入前段页面进行展示。

@RequestMapping("/index")
public String toIndex(Model model){
    //获取Spring Security认证成功后的相关信息
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    //将认证信息转换成JSON数据
    String json = JSON.toJSONString(authentication);
    //存入数据模型中带入前端页面进行展示
    model.addAttribute("auth",json);
    return "index";
}

启动项目并进行登录测试。

index.ftl首页中通过${auth}展示SpringSecurity认证成功的相关信息。

3.密码方式

Spring Security提供了多种密码加密方式,大致可以归类于以下几种:

  • 对密码进行明文处理,即不采用任何加密方式;

  • 采用MD5加密方式;

  • 采用哈希算法加密方式;

3.1.自定义MD5加密

创建自定义MD5加密类并实现PasswordEncoder

public class CustomMd5PasswordEncoder implements PasswordEncoder {
    @Override
    public String encode(CharSequence rawPassword) {
        //对密码进行 md5 加密
        String md5Password = DigestUtils.md5DigestAsHex(rawPassword.toString().getBytes());
        System.out.println(md5Password);
        return md5Password;
    }
​
    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        // 通过md5校验
        System.out.println(rawPassword);
        System.out.println(encodedPassword);
        return encode(rawPassword).equals(encodedPassword);
    }
}
修改SecurityConfig配置类,更换密码编码器:

@Bean
public PasswordEncoder passwordEncoder(){
    // 自定义MD5加密方式:
    return new CustomMd5PasswordEncoder();
}

数据库中的用户密码也需要更换成对应自定义MD5加密密码:

//MD5自定义加密方式:
String pwd =  DigestUtils.md5DigestAsHex("123456".getBytes());
System.out.println(pwd);

最后,将生成的MD5加密密码保存到数据库表中。

3.2.BCryptPasswordEncoder密码编码器

BCryptPasswordEncoderSpring Security中一种基于bcrypt算法的密码加密方式。bcrypt算法是一种密码哈希函数,具有防止彩虹表攻击的优点,因此安全性较高。

使用BCryptPasswordEncoder进行密码加密时,可以指定一个随机生成的salt值,将其与原始密码一起进行哈希计算。salt值可以增加密码的安全性,因为即使两个用户使用相同的密码,由于使用不同的salt值进行哈希计算,得到的哈希值也是不同的。

Spring Security中,可以通过在SecurityConfig配置类中添加以下代码来使用BCryptPasswordEncoder进行密码加密:

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

这样就可以在Spring Security中使用BCryptPasswordEncoder进行密码加密了。

4.RememberMe

在实际开发中,为了用户登录方便常常会启用记住我(Remember-Me)功能。如果用户登录时勾选了“记住我”选项,那么在一段有效时间内,会默认自动登录,免去再次输入用户名、密码等登录操作。该功能的实现机理是根据用户登录信息生成 Token 并保存在用户浏览器的 Cookie 中,当用户需要再次登录时,自动实现校验并建立登录态的一种机制。

Spring Security提供了两种 Remember-Me 的实现方式:

  • 简单加密 Token:用散列算法加密用户必要的登录系信息并生成 Token 令牌。

  • 持久化 Token:数据库等持久性数据存储机制用的持久化 Token 令牌。

基于持久化Token配置步骤如下:

  • 创建数据库表 persistent_logins,用于存储自动登录信息

CREATE TABLE `persistent_logins` (
  `username` varchar(64) NOT NULL,
  `series` varchar(64) PRIMARY KEY,
  `token` varchar(64) NOT NULL,
  `last_used` timestamp NOT NULL
);

该步骤可以不做,在后续的配置过程中可以交由SpringSecurity自动生成。

  • 基于持久化Token配置,修改SecurityConfig配置类:

Remember-Me 功能的开启需要在configure(HttpSecurity http)方法中通过http.rememberMe()配置,该配置主要会在过滤器链中添加 RememberMeAuthenticationFilter 过滤器,通过该过滤器实现自动登录。

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
​
    //省略其他配置,参考之前
    
    @Autowired
    public UserDetailsService userDetailsService;
    
    @Resource
    public DataSource dataSource;
    
    /**
     * 配置持久化Token方式,注意tokenRepository.setCreateTableOnStartup()配置
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        // 设置为true要保障数据库该表不存在,不然会报异常哦
        // 所以第二次打开服务器应用程序的时候得把它设为false
        tokenRepository.setCreateTableOnStartup(false);
        return tokenRepository;
    }
    
     @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
​
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .anyRequest().authenticated()
            .and()
                .formLogin()
                .loginPage("/")
                .loginProcessingUrl("/user/userLogin")
                .passwordParameter("password")
                .usernameParameter("username")
                // 认证成功 redirect跳转,根据上一保存请求进行成功跳转
                .defaultSuccessUrl("/index")
                // 认证成功 forward跳转,始终在认证成功之后跳转到指定请求
                //.successForwardUrl("/index")
                //failureForwardUrl("/")
                /*.successHandler((request, response, exception)->{
                    response.sendRedirect("/index");
                })*/
                .failureHandler((request, response, exception) -> {
                    request.setAttribute("msg",exception.getMessage());
                    request.getRequestDispatcher("/").forward(request,response);
                })
            .and()
                .rememberMe()
                // 指定 rememberMe 的参数名,用于在表单中携带 rememberMe 的值。
                //.rememberMeParameter("remember-me")
                // 指定 rememberMe 的有效期,单位为秒,默认2周。
                .tokenValiditySeconds(30)
                // 指定 rememberMe 的 cookie 名称。
                .rememberMeCookieName("remember-me-cookie")
                // 指定 rememberMe 的 token 存储方式,可以使用默认的 PersistentTokenRepository 或自定义的实现。
                .tokenRepository(persistentTokenRepository())
                // 指定 rememberMe 的认证方式,需要实现 UserDetailsService 接口,并在其中查询用户信息。
                .userDetailsService(userDetailsService)
            .and()
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/")
            .and()
                .csrf().disable()
                ;
        return http.build();
    }
}

rememberMe主要方法介绍:

方法说明
rememberMeParameter()指定在登录时“记住我”的 HTTP 参数,默认为 remember-me
tokenValiditySeconds()设置 Token 有效期为 200s,默认时长为 2 星期
tokenRepository()指定 rememberMe 的 token 存储方式,可以使用默认的 PersistentTokenRepository 或自定义的实现
userDetailsService()指定 UserDetailsService 对象
rememberMeCookieName()指定 rememberMecookie 名称

  • 修改登录页面login.ftl,添加remember-Me记住我的checkbox选项框。

<form action="/user/userLogin" method="post">
    <label>账号:</label><input type="text" name="username"/><br/>
    <label>密码:</label><input type="password" name="password"/><br/>
    <input type="checkbox" name="remember-me"/>记住我<br/>
    <input type="submit" value="登 录"/>
</form>

注意:配置的checkbox复选框的name属性名要与上面配置的rememberMeParameter("属性名")一致,默认就叫remember-me

总结:remember-me 只有在 JSESSIONID 失效和SecurityContextPersistenceFilter 过滤器认证失败或者未进行认证时才发挥作用。此时,只要 remember-meCookie 不过期,我们就不需要填写登录表单,就能实现再次登录,并且 remember-me 自动登录成功之后,会生成新的 Token 替换旧的 Token,相应 CookieMax-Age 也会重置。

5.CSRF防御

5.1.什么是CSRF

CSRFCross-Site Request Forgery,跨站请求伪造)是一种利用用户已登录的身份在用户不知情的情况下发送恶意请求的攻击方式。攻击者可以通过构造恶意链接或者伪造表单提交等方式,让用户在不知情的情况下执行某些操作,例如修改密码、转账、发表评论等。

为了防范CSRF攻击,常见的做法是在请求中添加一个CSRF Token(也叫做同步令牌、防伪标志),并在服务器端进行验证。CSRF Token是一个随机生成的字符串,每次请求都会随着请求一起发送到服务器端,服务器端会对这个Token进行验证,如果Token不正确,则拒绝执行请求。

5.2.SpringSecurity中如何使用CSRF

Spring Security中,防范CSRF攻击可以通过启用CSRF保护来实现。启用CSRF保护后,Spring Security会自动在每个表单中添加一个隐藏的CSRF Token字段,并在服务器端进行验证。如果Token验证失败,则会抛出异常,从而拒绝执行请求。启用CSRF保护的方式是在Spring Security配置文件中添加.csrf()方法,例如:

http
    .csrf()
        .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());

在上面的配置中,我们使用了CookieCsrfTokenRepository作为CSRF Token的存储方式,并设置了httpOnlyfalse,以便在客户端可以访问到该Token

.csrf()主要方法介绍:

方法说明
disable()关闭CSRF防御
csrfTokenRepository()设置CookieCsrfTokenRepository实例,用于存储和检索CSRF令牌。与HttpSessionCsrfTokenRepository不同,CookieCsrfTokenRepositoryCSRF令牌存储在cookie中,而不是在会话中。
ignoringAntMatchers()设置一组Ant模式,用于忽略某些请求的CSRF保护。例如,如果您想要忽略所有以/api/开头的请求,可以使用.ignoringAntMatchers("/api/**")
csrfTokenManager()设置CsrfTokenManager实例,用于管理CSRF令牌的生成和验证。默认情况下,Spring Security使用DefaultCsrfTokenManager实例来生成和验证CSRF令牌。
requireCsrfProtectionMatcher()设置RequestMatcher实例,用于确定哪些请求需要进行CSRF保护。默认情况下,Spring Security将对所有非GET、HEAD、OPTIONS和TRACE请求进行CSRF保护。

重启项目进行测试。

  • 问题一:开启了SpringSecurityCSRF防御之后导致登录的POST请求失败?

原因分析:使用了 spring-security 后,默认开启了防止跨域攻击的功能,任何 POST 提交到后台的表单都要验证是否带有 _csrf 参数,一旦传来的 _csrf 参数不正确,服务器便返回 403 错误。

解决方案:修改login.ftl页面代码,加入_csrf隐藏域。

<form action="/user/userLogin" method="post">
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
    <label>账号:</label><input type="text" name="username"/><br/>
    <label>密码:</label><input type="password" name="password"/><br/>
    <input type="checkbox" name="remember-me"/>记住我<br/>
    <input type="submit" value="登 录"/>
</form>

如果针对一些特定的请求接口,不需要进行CSRF防御,可以通过以下配置忽略:

http.csrf().ignoringAntMatchers("/upload"); // 禁用/upload接口的CSRF防御

  • 问题二:SpringSecurity退出登录/logout提示404问题

退出登录logout实现方式:

http.logout().logoutUrl("/logout").
                logoutSuccessUrl("/").permitAll();

结果运行项目,执行安全退出时提示页面404错误。

错误原因:SpringSecurity3.2开始,默认会启动CSRF防护,一旦启动了CSRF防护,“/logout” 需要用post的方式提交,SpringSecurity才能过滤。

  • 方式一:配置文件直接关闭CSRF防护

http.csrf().disable();  //关闭csrf防护

注意:当关闭CSRF防护时,部分的页面可能会无法刷新,甚至报错。这时可以在页面的<header>标签之间加入以下代码:

<meta name="_csrf" content="${_csrf.token}"/>
<meta name="_csrf_header" content="${_csrf.headerName}"/>

  • 方式二:官方建议使用POST请求退出登陆,并携带CRSF令牌

http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).
                logoutSuccessUrl("/").permitAll();

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Netty Review - 深入理解Netty: ChannelHandler的生命周期与事件处理机制

文章目录 概述CodeLifeCycleInBoundHandler 验证 概述 Netty的ChannelHandler是处理网络事件&#xff08;如数据读取、数据写入、连接建立、连接关闭等&#xff09;的核心组件。 在Netty中&#xff0c;ChannelHandler的生命周期与Channel的状态紧密相关&#xff0c;主要涉及到…

基于信道探测(Channel Sounding)的新一代低功耗蓝牙高精度测距技术原理详解

目录 1. 介绍 2. BLE HADM步骤 3. BLE HADM步骤一:设备同步 Synchronization 4. BLE HADM步骤二:跳频测相 Ranging 5. BLE HADM步骤三:交互相位信息,并计算距离 6. 发起设备和反射设备间的同步偏差问题 7. 总结 8. 参考文献 1. 介绍 在现今高度数字化与互联网普及的…

【AI提示词艺术】第12期 摄影艺术构图处理和人像生成的技巧

摄影艺术构图 星空宇宙 关键词&#xff1a; 强烈的明暗对比,8k,精细的描述,相片纸,超高分辨率,无建筑的,大自然,星空&#xff0c;云朵&#xff0c;刺眼流星&#xff0c;群星&#xff0c;银河&#xff0c;仰视视角&#xff0c;广角镜头 以下是按照提示词类别整理的相关描述&a…

常见的Ubuntu命令30条(一)

Ubuntu命令是指在Ubuntu操作系统中用于执行各种任务和操作的命令行指令。这些命令可以用于管理系统、配置网络、安装软件、浏览文件等。Ubuntu命令通常在终端&#xff08;Terminal&#xff09;应用程序中输入并执行。 在Ubuntu中&#xff0c;有许多常用的命令和指令。以下是一…

Windows漏洞利用开发——利用ROP绕过DEP保护

实验6 Windows漏洞利用开发 6.1实验名称 Windows漏洞利用开发 6.2实验目的 学习windows漏洞利用开发&#xff0c;使用kali linux相关工具对windows内目标程序进行漏洞利用 6.3实验步骤及内容 第三阶段&#xff1a;利用ROP绕过DEP保护 了解DEP保护理解构造ROP链从而绕过DEP…

RabbmitMQ基础

RabbmitMQ基础 1.1 什么是MQ MQ(Message Queue)&#xff0c;从字面意思看&#xff0c;本质是个队列&#xff0c;FIFO先入先出&#xff0c;队列中存放的是message。是一种跨进程的通信机制&#xff0c;用于上下游传递消息。在互联网架构中&#xff0c;MQ是一种非常常见的上下游…

springboot整合JPA 多表关联 :一对多 多对多

补充一下自定义SQL 这是连表查询&#xff0c;可以任意查出字符&#xff0c;用Map接收 Testvoid test3() {JPAQueryFactory jpaQueryFactory new JPAQueryFactory(em);QStudent student QStudent.student;QMessage message QMessage.message;//constructor(StuMesDto.class, …

前端扫盲:什么是API网关?为什么它有用?

API 通常被称为应用程序从后端服务访问数据和业务逻辑的前门。API 本质上是一个软件向其他人或程序提供的接口&#xff0c;允许他们与该软件进行交互。 在创建 API 时&#xff0c;需要选择编程语言(Java、Python、PHP 等)来编写 API 逻辑&#xff0c;还需要将 API 部署到服务器…

VRF -- 虚拟路由转发

VRF VRF&#xff08;Virtual Routing and Forwarding&#xff09;虚拟路由转发&#xff0c;是一种网络虚拟化技术&#xff0c;用于在同一物理网络设备上创建多个虚拟路由表&#xff0c;每个虚拟路由表都是相互独立的&#xff0c;就像是在同一设备上运行了多个独立的路由器一样…

C# 判断两个时间段是否重叠

public static bool IsOverlap(DateTime startTime1, DateTime endTime1, DateTime startTime2, DateTime endTime2){// 判断两个时间段是否有重叠return !(endTime1 < startTime2 || startTime1 > endTime2);//根据德摩根定律&#xff0c;等效为&#xff1a;endTime1 &g…

由于被认为是客户端对错误(例如:畸形的请求语法、无效的请求信息帧或者虚拟的请求路由),服务器无法或不会处理当前请求。

问题描述&#xff1a; 由于被认为是客户端对错误&#xff08;例如&#xff1a;畸形的请求语法、无效的请求信息帧或者虚拟的请求路由&#xff09;&#xff0c;服务器无法或不会处理当前请求。 在实现向数据库中添加记录时&#xff0c;请求发送无效&#xff0c;参数也未传递到控…

git首次使用--去公司第一次拉取

文章目录 一&#xff0c; 在企业中首次拉取项目二&#xff0c;提交项目1. 提交----新添加的文件2. 将分支上的代码同步到master3. 提交----更改后的文件 三&#xff0c;常见问题1. Git Pull Failed 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一&am…

C语言之字符串数组

目录 字符串数组 读取字符串数组中字符串 注意 字符串可以用数组来表示&#xff0c;所以字符串的集合也可以用数组的数组&#xff08;即多维数组&#xff09;来表示&#xff0c;下面我们就来学习字符串数组。 字符串数组 类型相同的数据集合适合用数组来实现&#xff0c;所…

javascript变量提升机制

变量提升&#xff1a;当栈内存&#xff08;作用域&#xff09;形式&#xff0c;js代码自上而下执行之前&#xff0c;浏览器首先会把所有带VAR/FUNCTION关键词的进行提前的“声明”或者“定义”。这种预先处理机制称为变量提升。 声明&#xff08;declare&#xff09;&#xff…

图片转excel:“保留数字格式”在什么场景下该勾

保留数字格式是什么意思呢&#xff1f;顾名思义&#xff0c;就是将转出来的数字保留为数字格式&#xff0c;而不是文本格式。我们知道&#xff0c;OCR程序将图片上的文字识别为电脑可编辑的文字后&#xff0c;如果导入到excel不加处理&#xff0c;则单个数字过长的文字就会被ex…

华为HCIA认证H12-811题库新增

801、[单选题]178/832、在系统视图下键入什么命令可以切换到用户视图? A quit B souter C system-view D user-view 试题答案&#xff1a;A 试题解析&#xff1a;在系统视图下键入quit命令退出到用户视图。因此答案选A。 802、[单选题]“网络管理员在三层交换机上创建了V…

Flutter中鼠标 onEnter onExit onHover 实现代码分析

生活会给你任何最有益的经历&#xff0c;以助你意识的演变。 转载请注明出处: 这里对最近用到的一些 Flutter 开源的东西进行总结积累&#xff0c;希望能帮助到大家。 文章目录 背景测试代码flutter 代码onEnter & onExitonHover end 背景 Android设备在使用的时候&#…

【Linux】Linux常见指令解析上

目录 1. 前言2. ls指令3. pwd指令4. cd指令3.1 cd常见快捷指令 4. touch指令5. mkdir指令6. rmdir指令 && rm指令 &#xff08;重要&#xff09;6.1 rmdir指令6.2 rm指令 7. man指令 1. 前言 这篇文章我们将详细介绍一下Linux下常见的基本指令。 2. ls指令 语法: ls [选…

抖音网红罗盘时钟改良版

文章目录 💕效果展示💕代码展示HTML💕效果展示 💕代码展示 HTML <!DOCTYPE html> <html lang=