【Spring Security】认证之案例的使用、MD5加密、CSRF防御

目录

一、引言

1、什么是SpringSecurity认证

2、为什么使用SpringSecurity之认证

3、实现步骤

二、快速实现(案例)

1、添加依赖

2、配置

3、导入数据表及相关代码

4、创建登录页及首页

5、创建配置Controller

6、用户认证

6.1、用户对象UserDetails

6.2、业务对象UserDetailsService

6.3、SecurityConfig配置

7、启动测试

三、密码方式

1、自定义MD5加密

2、BCryptPasswordEncoder密码编码器

四、RememberMe

五、CSRF防御

1、什么是CSRF

2、SpringSecurity中如何使用CSRF


一、引言

1、什么是SpringSecurity认证

        Spring Security认证是指基于Spring Security框架的身份验证过程。身份验证是指验证某个用户是否为系统中的合法主体,即用户能否访问该系统。在Spring Security中,通常要求用户提供用户名和密码进行认证。系统通过校验用户名和密码来完成认证过程。如果用户通过了身份验证,他们就可以访问受保护的资源。Spring Security支持多种身份认证模式,例如基于表单的身份认证、OpenID身份认证、Jasig中央认证服务等。

2、为什么使用SpringSecurity之认证

  1. 安全性:Spring Security是一个成熟的安全框架,提供了许多安全特性,如身份验证、授权、密码加密等。它可以有效地保护应用程序的安全,防止各种常见的安全威胁和漏洞。
  2. 易于集成:Spring Security与Spring框架深度集成,可以方便地与Spring的其他功能配合使用,如依赖注入、事务管理等。这使得在Spring环境下开发安全功能更加便捷。
  3. 灵活性:Spring Security提供了丰富的配置选项和扩展点,可以根据具体需求进行灵活的定制。开发者可以根据应用程序的具体需求进行调整和优化。
  4. 社区支持:Spring Security拥有庞大的用户基础和活跃的社区,可以提供丰富的支持和资源。在遇到问题时,可以快速找到解决方案或寻求帮助。
  5. 文档完善:Spring Security的官方文档非常完善,提供了详细的说明和示例,可以帮助开发者快速上手并实现安全功能。

3、实现步骤

  1. 配置数据源:提供用于存储用户名和密码的数据源,例如数据库或LDAP服务器。
  2. 配置认证模块:配置身份验证模块以从数据源读取用户名和密码,并验证它们是否匹配。
  3. 配置授权模块:配置授权模块以确定用户是否有权访问特定的资源。这通常基于角色或权限。
  4. 配置安全配置类:通过创建一个实现SecurityFilterChain接口的类,配置Spring Security的过滤器链。在这个类中,可以定义哪些URL需要身份验证和授权,以及如何处理安全相关的异常。
  5. 配置视图解析器:配置视图解析器以将URL映射到相应的视图。在Spring Security中,可以使用Thymeleaf或JSP等模板引擎来创建视图。

二、快速实现(案例)

1、添加依赖

<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><!--        security-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>

2、配置

配置application.yml

server:port: 8080
spring:datasource:username: rootpassword: 1234url: jdbc:mysql://localhost:3306/vue?useUnicode=true&characterEncoding=utf8&useSSL=falsedriver-class-name: com.mysql.jdbc.Driverfreemarker:enabled: truesuffix: .ftltemplate-loader-path: classpath:/templates/
mybatis-plus:# Mybatis Mapper所对应的XML位置mapper-locations: classpath:mapper/*.xml# 别名包扫描路径type-aliases-package: com.wfzldr.springsecurity01.model# 是否开启自动驼峰命名规则(camel case)映射configuration:map-underscore-to-camel-case: trueglobal-config:db-config:logic-delete-field: deleted # 全局逻辑删除的实体字段名logic-delete-value: 1       # 逻辑已删除值(默认为 1)logic-not-delete-value: 0   # 逻辑未删除值(默认为 0)
logging:level:com.wfzldr.springsecurity01.mapper: debug

3、导入数据表及相关代码

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

sys_module

sys_role

sys_role_module

sys_user

sys_user_role

表之间的关系说明:

生成对应表的代码层

4、创建登录页及首页

基于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

5、创建配置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";}}

6、用户认证

6.1、用户对象UserDetails

修改User并实现UserDetails

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

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

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

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

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

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

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

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

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

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

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

6.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*/@Overridepublic 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一起使用,后者是一个身份验证提供程序,用于验证用户的凭据。

6.3、SecurityConfig配置

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

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
​/*** 基于数据库方式进行身份认证和权限鉴定*/@Autowiredprivate UserDetailsService userDetailsService;/*** 配置密码编码器,首次采用明文密码方式进行比对校验*/@Beanpublic PasswordEncoder passwordEncoder(){return NoOpPasswordEncoder.getInstance();}/*** 获取AuthenticationManager(认证管理器),登录时认证使用(基于数据库方式)* @param* @return* @throws Exception*/@Beanpublic AuthenticationManager authenticationManager() throws Exception {//创建DaoAuthenticationProviderDaoAuthenticationProvider provider=new DaoAuthenticationProvider();//设置userDetailsService,基于数据库方式进行身份认证provider.setUserDetailsService(userDetailsService);//配置密码编码器provider.setPasswordEncoder(passwordEncoder());return new ProviderManager(provider);}@Beanpublic 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作用域,取名为msgrequest.setAttribute("msg",exception.getMessage());//认证失败后转发到指定页面request.getRequestDispatcher("/").forward(request,response);
})

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

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

7、启动测试

在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认证成功的相关信息。

三、密码方式

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

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

  • 采用MD5加密方式;

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

1、自定义MD5加密

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

public class CustomMd5PasswordEncoder implements PasswordEncoder {@Overridepublic String encode(CharSequence rawPassword) {//对密码进行 md5 加密String md5Password = DigestUtils.md5DigestAsHex(rawPassword.toString().getBytes());System.out.println(md5Password);return md5Password;}@Overridepublic 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加密密码保存到数据库表中。

2、BCryptPasswordEncoder密码编码器

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

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

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

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

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

四、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 {
​//省略其他配置,参考之前@Autowiredpublic UserDetailsService userDetailsService;@Resourcepublic DataSource dataSource;/*** 配置持久化Token方式,注意tokenRepository.setCreateTableOnStartup()配置*/@Beanpublic PersistentTokenRepository persistentTokenRepository(){JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();tokenRepository.setDataSource(dataSource);// 设置为true要保障数据库该表不存在,不然会报异常哦// 所以第二次打开服务器应用程序的时候得把它设为falsetokenRepository.setCreateTableOnStartup(false);return tokenRepository;}@Beanpublic 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 也会重置。

五、CSRF防御

1、什么是CSRF

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

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

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();

分享就到这里!欢迎大家评论区讨论!

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

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

相关文章

WEB 3D技术 three.js通过 GLTFLoader 导入并应用 gltf/glb 3D资源

上文 WEB 3D技术 three.js 雾 基础使用讲解我们讲了雾的基本使用方法 但是 如果我们要做一个树林 一颗一颗树去加 那真的是要累死了 我们一定是在建模软件上 建模好这样的模型 然后将模型导入到场景中 官网中搜索 GLTFLoader 在我们日常WEB开发中 用的最多的3D格式 就是 GLTF…

python练习2【题解///考点列出///错题改正】

一、单选题 【文件】 *1.【单选题】 ——文件&#xff1a;读取方法 下列哪个选项可以从文件中读取任意字节的内容&#xff1f;&#xff08;C &#xff09;A A.read() B.readline() C.readlines() D.以上全部 A\B\C三种方法都是可以读取文件中任意的字节内容的&#xff0…

Lesson 06 vector类(上)

C&#xff1a;渴望力量吗&#xff0c;少年&#xff1f; 文章目录 一、vector是什么&#xff1f;二、vector的使用1. 构造函数2. vector iterator3. vector 空间增长问题4. vector增删查改 三、vector实际使用 一、vector是什么&#xff1f; vector是表示可变大小数组的序列容器…

LVGL 在framebuffer设备上的移植

LVGL 在framebuffer设备上的移植 ItemDescDate2023-12-31Authorhongxi.zhuplatformNXP I.MX6ULLLCDSPI TFTLCD NV3030B 文章目录 LVGL 在framebuffer设备上的移植一、LVGL源码获取二、源码修改适配三、编译&运行补充 一、LVGL源码获取 新建lvgl_imx6ull文件夹&#xff0c…

低代码开发中业务蓝图的重要性:业务需求与系统实现的桥梁

在低代码应用开发领域&#xff0c;业务蓝图是一个至关重要的工具&#xff0c;它提供了组织业务流程需求的详细信息。它类似于一份指导开发人员进行应用开发的路线图&#xff0c;确保与业务的战略目标和需求保持一致。 低代码方法学&#xff0c;顾名思义&#xff0c;即减少了传…

Google机器人团队获ICRA 2023 机器人学习方向最佳论文奖:机器人实体控制的大语言模型程序

这篇论文主要讨论了大型语言模型&#xff08;LLM&#xff09;在机器人控制方面的应用。作者们指出&#xff0c;尽管LLM在理解和生成自然语言方面表现出色&#xff0c;但其在实际应用中&#xff0c;如机器人控制等领域的应用仍然有限。因此&#xff0c;他们提出了一种新的方法&a…

登峰造极,师出造化,Pytorch人工智能AI图像增强框架ControlNet绘画实践,基于Python3.10

人工智能太疯狂&#xff0c;传统劳动力和内容创作平台被AI枪毙&#xff0c;弃尸尘埃。并非空穴来风&#xff0c;也不是危言耸听&#xff0c;人工智能AI图像增强框架ControlNet正在疯狂地改写绘画艺术的发展进程&#xff0c;你问我绘画行业未来的样子&#xff1f;我只好指着Cont…

每日一题——LeetCode976

方法一 个人方法 找规律&#xff1a; 要求要围成三角形且周长最大&#xff0c;那么三条边自然是越大且越接近越好。那么我们就从最大的三条边开始看能不能围成三角形。如果不能组成三角形&#xff0c;则丢弃最长的那条&#xff0c;再取剩余边里最长的那条再看能不能组成三角形…

二叉树的中序遍历,力扣

目录 题目地址&#xff1a; 题目&#xff1a; 解题方法&#xff1a; 解题分析&#xff1a; 解题思路&#xff1a; 代码实现&#xff1a; 注&#xff1a; 代码实现&#xff08;递归&#xff09;&#xff1a; 代码实现&#xff08;迭代&#xff09;&#xff1a; 题目地址&#xf…

数据库——建立ER模型及关系模型转换

​ 【实验内容及要求】 使用画图工具或MySQL Workbench等建模工具设计出相应的ER图&#xff0c;将局部ER图合并为一个整体ER模型&#xff0c;在ER模型中填加多样性约束&#xff0c;建立显示主键的ER模型&#xff0c;标识实体的属性&#xff0c;确认主键、外键。将上述ER图转化…

Nx市工业数据洞察:Flask、MySQL、Echarts的可视化之旅

Nx市工业数据洞察&#xff1a;Flask、MySQL、Echarts的可视化之旅 背景数据集来源技术选型功能介绍创新点总结 背景 随着工业化的不断发展&#xff0c;Nx市工业数据的收集和分析变得愈发重要。本博客将介绍如何利用Flask、MySQL和Echarts等技术&#xff0c;从统计局获取的数据…

CentOS 7 lvm 裸盘的扩容和缩容减盘 —— 筑梦之路

背景介绍 之前写过比较多的关于lvm的文章&#xff1a; CentOS 7 lvm 更换坏盘操作步骤小记 —— 筑梦之路_centos更换硬盘操作-CSDN博客 xfs ext4 结合lvm 扩容、缩容 —— 筑梦之路_ext4扩盘-CSDN博客 LVM逻辑卷元数据丢失恢复案例 —— 筑梦之路_pve lvm数据恢复-CSDN博客…

各部门请注意,VELO维乐潮流骑士尼莫出街啦,快来加入吧!

VELO潮流骑士丨车界“小学生”尼莫&#xff0c;下面是来自她的自诉&#xff1a;      大家好&#xff01;我是尼莫&#xff0c;一枚骑车届的“小学生”&#xff0c;我爱上骑车已经有一年的时间啦&#xff01;在这一年的时间里&#xff0c;骑车改变了我很多&#xff1a;爱上…

单片机外设矩阵键盘之行列扫描识别原理与示例

单片机外设矩阵键盘之行列扫描识别原理与示例 1.概述 这篇文章介绍单片机通过行列扫描的方式识别矩阵键盘的按键&#xff0c;通过程序执行相应的操作。 2.行列扫描识别原理 2.1.独立按键识别原理 为什么需要矩阵按键 独立按键操作简单&#xff0c;当数量较多时候会占用单片机…

PAT 乙级 1033 旧键盘打字

旧键盘上坏了几个键&#xff0c;于是在敲一段文字的时候&#xff0c;对应的字符就不会出现。现在给出应该输入的一段文字、以及坏掉的那些键&#xff0c;打出的结果文字会是怎样&#xff1f; 输入格式&#xff1a; 输入在 2 行中分别给出坏掉的那些键、以及应该输入的文字。其…

机器视觉实战应用:手势、人脸、动作以及手势鼠标构建(一)

CV实战应用手势、人脸、动作以及手势鼠标构建&#xff08;一&#xff09;总起 核心思想 手势识别是一种常见的计算机视觉应用&#xff0c;它可以通过摄像头或者预先录制的视频图像来追踪和识别人类手势。手势识别的应用非常广泛&#xff0c;例如在游戏、虚拟现实、人机交互等…

【算法】数论---欧拉函数

什么是欧拉函数&#xff1f; 对于正整数n&#xff0c;欧拉函数是小于或等于n的正整数中与n互质的数的数目&#xff0c;记作φ(n) φ(1)1 当m,n互质时&#xff0c;φ(mn)φ(m)∗φ(n) 一、求一个正整数的欧拉函数---&#xff08;先对它分解质因数&#xff0c;然后套公式&#xf…

2024年,程序员有哪些危机,有什么应对方式?

在2024年&#xff0c;程序员可能面临的危机主要包括技术更新迅速、职业竞争激烈、工作与生活平衡困难等方面。 为了应对这些危机&#xff0c;程序员可以采取以下策略&#xff1a; 技术更新迅速&#xff1a;随着技术的不断发展&#xff0c;新的编程语言和工具不断涌现&#xff…

spring核心与思想

spring核心与思想 Spring 是什么&#xff1f;什么是容器&#xff1f;什么是 IoC&#xff1f;传统程序开发传统程序开发的缺陷解决传统开发中的缺陷控制反转式程序开发对⽐总结规律 理解 Spring IoCDI 概念说明 Spring 是什么&#xff1f; Spring 指的是 Spring Framework&…

Springcloud Alibaba使用Canal将Mysql数据实时同步到Redis保证缓存的一致性

目录 1. 背景 2. Windows系统安装canal 3.Mysql准备工作 4. 公共依赖包 5. Redis缓存设计 6. mall-canal-service 1. 背景 canal [kənl] &#xff0c;译意为水道/管道/沟渠&#xff0c;主要用途是基于 MySQL 数据库增量日志解析&#xff0c;提供增量数据订阅和消费。其诞…