【Spring Security】认证密码加密Token令牌CSRF的使用详解
🎉🎉欢迎来到我的CSDN主页!🎉🎉
🏅我是Java方文山,一个在CSDN分享笔记的博主。📚📚
🌟推荐给大家我的专栏《Spring Security》。🎯🎯
👉点击这里,就可以查看我的主页啦!👇👇
Java方文山的个人主页
🎁如果感觉还不错的话请给我点赞吧!🎁🎁
💖期待你的加入,一起学习,一起进步!💖💖
前言
我们都知道Spring Security是做认证的,那它到底是怎么认证的呢?它是怎么将明文密码加密的呢?Token令牌的使用与CSRF跨域请求伪造是什么等等我们都不知道,但是通过这篇文章我相信你会有所了解有所收获!!!
一、基于Security认证
1.前期准备
基于Spring Initializr
创建SpringBoot
项目,实现与MyBatisPlus
的项目整合。分别导入:CodeGenerator
和MyBatisPlusConfig
。
CodeGenerator
:用于MybatisPlus
代码生成;MyBatisPlusConfig
:MyBatisPlus
配置类,实现了分页和乐观锁相关配置。
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
spring:datasource:type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.jdbc.Driverusername: rootpassword: 1234url: jdbc:mysql://localhost:3306/vue?useUnicode=true&characterEncoding=utf8&useSSL=falsefreemarker:enabled: truesuffix: .ftltemplate-loader-path: classpath:/templates/
mybatis-plus:# Mybatis Mapper所对应的XML位置mapper-locations: classpath:mapper/*.xml# 别名包扫描路径type-aliases-package: com.zking.spbootauthc.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.zking.spbootauthc.mapper: debug
修改数据库相关账号、密码及数据库名。
1.3.导入相关数据表
表名 | 说明 |
---|---|
sys_user | 用户信息表 |
sys_role | 角色信息表 |
sys_module | 模块信息表(权限信息表) |
sys_user_role | 用户角色表 |
sys_role_module | 角色模块表 |
表之间的关系说明:
数据表中有以上字段即可,后续我们还要加的。
1.4.实现MP代码生成
直接运行CodeGenerator.java
类,生成sys_
开头的相关信息表。
package com.csdn.security.config;import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import lombok.extern.slf4j.Slf4j;import java.util.Arrays;
import java.util.Collections;
import java.util.List;@Slf4j
public class MySQLGenerator {private final static String URL = "jdbc:mysql://localhost:3306/goods?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC";private final static String USERNAME = "root";private final static String PASSWORD = "root789";private final static DataSourceConfig.Builder DATA_SOURCE_CONFIG =new DataSourceConfig.Builder(URL, USERNAME, PASSWORD);public static void main(String[] args) {FastAutoGenerator.create(DATA_SOURCE_CONFIG).globalConfig((scanner, builder) ->builder.author("Java方文山").outputDir(System.getProperty("user.dir") + "\\src\\main\\java").commentDate("yyyy-MM-dd").dateType(DateType.TIME_PACK)).packageConfig((builder) ->builder.parent("com.csdn.boot").entity("pojo").service("Service").serviceImpl("Service.impl").mapper("mapper").xml("mapper.xml").pathInfo(Collections.singletonMap(OutputFile.xml, System.getProperty("user.dir") + "\\src\\main\\resources\\mapper"))).injectionConfig((builder) ->builder.beforeOutputFile((a, b) -> log.warn("tableInfo: " + a.getEntityName()))).strategyConfig((scanner, builder) ->builder.addInclude(getTables(scanner.apply("请输入表名,多个英文逗号分隔?所有输入 all"))).addTablePrefix("tb_", "t_", "lay_", "meeting_", "sys_").entityBuilder().enableChainModel().enableLombok().enableTableFieldAnnotation().controllerBuilder().enableRestStyle().enableHyphenStyle().build()).templateEngine(new FreemarkerTemplateEngine()).execute();}protected static List<String> getTables(String tables) {return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));}}
1.5.为sys_user添加认证字段
前面说到有些字段需要后期加,至于是什么字段我事先没有给大家说的原因是想大家知道,为什么要加这些字段,这些字段是什么意思,首先在SecurityFilterChain过滤链编写重定向登录成功的首页方法
//设置登录成功后重定向到那个页面.successHandler((req,resp,auth)->{resp.sendRedirect("/index");})
方便我们查看里面到底有什么参数
可以看到以上除了username\password都是我们没有的,我们接下来就把这些属性加上即可,因为这里Security必须要用到UserDetails这个类,所以我们自己的user类就就需要实现这个类。
UserDetails
是Spring Security框架中的一个接口,它代表了应用程序中的用户信息。UserDetails
接口定义了一组方法,用于获取用户的用户名、密码、角色和权限等信息,以便Spring Security可以使用这些信息进行身份验证和授权。以下是
UserDetails
接口中定义的方法:
getUsername()
:获取用户的用户名。
getPassword()
:获取用户的密码。
getAuthorities()
:获取用户的角色和权限信息。
isEnabled()
:判断用户是否可用。
isAccountNonExpired()
:判断用户的账号是否过期。
isAccountNonLocked()
:判断用户的账号是否被锁定。
isCredentialsNonExpired()
:判断用户的凭证是否过期。自定义用户信息时,可以实现
UserDetails
接口并覆盖其中的方法来提供自己的用户信息。
我们直接定义这些属性即可,随后在数据库加上这些属性,注意默认值为1,否则认证不成功,因为除了0之外的数字都是真,如果为0就是假也就是false,那么我们认证的字段有一个为false必然是不成功的。
2.SpringSecurity之认证
2.1业务对象UserDetailsService
修改UserServiceImpl
并实现UserDetailsService
,重写loadUserByUsername(String username)
方法。
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService, 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
一起使用,后者是一个身份验证提供程序,用于验证用户的凭据。
2.2.SecurityConfig配置
创建WebSecurityConfig
配置类,配置SpringSecurity
结合数据库方式进行身份认证和权限鉴定。
package com.csdn.security.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;@Configuration
//开启SpringSecurity的默认行为
@EnableWebSecurity
public class WebSecurityConfig {/*** 配置密码编码器,首次采用明文密码方式进行比对校验*/@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}/*** 基于数据库方式进行身份认证和权限鉴定*/@Autowiredprivate UserDetailsService userDetailsService;/*** 获取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("/toLogin").permitAll()// 设置角色权限.antMatchers("/admin/**").hasRole("ADMIN").antMatchers("/user/**").hasAnyRole("ADMIN", "USER")// 其余所有请求全部需要鉴权认证.anyRequest().authenticated().and().formLogin()// 设置登录页面的 URL.loginPage("/toLogin")// 设置登录请求的 URL,即表单提交的 URL.loginProcessingUrl("/userLogin")// 设置登录表单中用户名字段的参数名,默认为username.usernameParameter("username")// 设置登录表单中密码字段的参数名,默认为password.passwordParameter("password")//设置登录成功后重定向到那个页面.successHandler((req,resp,auth)->{resp.sendRedirect("/index");}).and().logout()// 设置安全退出的URL路径.logoutUrl("/logout")// 设置退出成功后跳转的路径.logoutSuccessUrl("/");//跳转登录页重新登录问题:用于禁用 CSRF(Cross-Site Request Forgery)防护机制。http.csrf().disable();//定义访问被拒绝时的处理方式,会重定向到noauth(也就是没有权限的页面展示)http.exceptionHandling().accessDeniedPage("/noauth");return http.build();}
}
至此就可以完成了通过数据库的用户进行认证登录操作了,这里需要注意的是formLogin
认证失败后将不在使用failureForwardUrl()
方法转发,而是使用failureHandler
处理器方式处理错误信息并跳转页面。
-
如果使用
failureHandler
处理器方式,则可以自定义错误页面及错误信息:.failureHandler((request, response, exception) -> {//将认证错误信息保存到request作用域,取名为msgrequest.setAttribute("msg",exception.getMessage());//认证失败后转发到指定页面request.getRequestDispatcher("/").forward(request,response); })
修改登录页面
login.ftl
加入错误信息展示区域:<div>${msg!}</div>
二、密码加密
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密码编码器
BCryptPasswordEncoder
是Spring Security
中一种基于bcrypt
算法的密码加密方式。bcrypt
算法是一种密码哈希函数,具有防止彩虹表攻击的优点,因此安全性较高。
使用BCryptPasswordEncoder
进行密码加密时,可以指定一个随机生成的salt
值,将其与原始密码一起进行哈希计算。salt值可以增加密码的安全性,因为即使两个用户使用相同的密码,由于使用不同的salt
值进行哈希计算,得到的哈希值也是不同的。
在Spring Security
中,可以通过在SecurityConfig
配置类中添加以下代码来使用BCryptPasswordEncoder
进行密码加密:
@Bean
public PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();
}
这样就可以在Spring Security中使用BCryptPasswordEncoder
进行密码加密了,细心的同志在刚刚认证的时候应该就发现了。
3.BCrypt工作原理
BCrypt是一种单向哈希函数,它使用salt(盐)和cost(成本因子)来增加哈希的安全性。
下面是BCryptPasswordEncoder的工作原理:
-
加密过程:
- 生成一个随机的盐值。
- 使用盐值和密码作为输入,通过BCrypt算法进行哈希计算。
- 将盐值和计算得到的哈希值拼接在一起,并返回最终的加密结果。
这里假如有两个密码都为123但是salt不一样,可能是18293也可能是18392,但是他们的密码都是123,只是将原本的密码加上了“盐”后再进行哈希算法得到了一个值。
-
验证过程:
- 从存储中获取已加密的密码和对应的盐值。
- 使用输入的密码和盐值,通过BCrypt算法进行哈希计算。
- 将计算得到的哈希值与存储中的密码进行比较,如果匹配则验证成功,否则验证失败。
三、基于BCrypt完成Token
在实际开发中,为了用户登录方便常常会启用记住我(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
过滤器,通过该过滤器实现自动登录。
@Resourcepublic DataSource dataSource;/*** 配置持久化Token方式,注意tokenRepository.setCreateTableOnStartup()配置*/@Beanpublic PersistentTokenRepository persistentTokenRepository(){JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();tokenRepository.setDataSource(dataSource);// 设置为true要保障数据库该表不存在,不然会报异常哦// 所以第二次打开服务器应用程序的时候得把它设为falsetokenRepository.setCreateTableOnStartup(false);return tokenRepository;}
在SecurityFilterChain中添加规则
.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)
rememberMe
主要方法介绍:
方法 | 说明 |
---|---|
rememberMeParameter() | 指定在登录时“记住我”的 HTTP 参数,默认为 remember-me |
tokenValiditySeconds() | 设置 Token 有效期为 200s,默认时长为 2 星期 |
tokenRepository() | 指定 rememberMe 的 token 存储方式,可以使用默认的 PersistentTokenRepository 或自定义的实现 |
userDetailsService() | 指定 UserDetailsService 对象 |
rememberMeCookieName() | 指定 rememberMe 的 cookie 名称 |
-
修改登录页面
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-me
的 Cookie
不过期,我们就不需要填写登录表单,就能实现再次登录,并且 remember-me
自动登录成功之后,会生成新的 Token
替换旧的 Token
,相应 Cookie
的 Max-Age
也会重置。
下图是我们没有加Token令牌如果反复刷新会要我们重新提交表单
加了Token之后并且勾选复选框记住我将Token保存到cookie中
数据表中也会有数据
四、CSRF跨域伪请求
在Spring Security
中,防范CSRF
攻击可以通过启用CSRF
保护来实现。启用CSRF
保护后,Spring Security
会自动在每个表单中添加一个隐藏的CSRF Token
字段,并在服务器端进行验证。如果Token
验证失败,则会抛出异常,从而拒绝执行请求。启用CSRF
保护的方式是在Spring Security
配置文件中添加.csrf()
方法,例如:
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
将原来的关闭CSRF注释掉换成我们的
在上面的配置中,我们使用了CookieCsrfTokenRepository
作为CSRF Token
的存储方式,并设置了httpOnly
为false
,以便在客户端可以访问到该Token
。
.csrf()
主要方法介绍:
方法 | 说明 |
---|---|
disable() | 关闭CSRF 防御 |
csrfTokenRepository() | 设置CookieCsrfTokenRepository 实例,用于存储和检索CSRF 令牌。与HttpSessionCsrfTokenRepository 不同,CookieCsrfTokenRepository 将CSRF 令牌存储在cookie 中,而不是在会话中。 |
ignoringAntMatchers() | 设置一组Ant模式,用于忽略某些请求的CSRF 保护。例如,如果您想要忽略所有以/api/ 开头的请求,可以使用.ignoringAntMatchers("/api/**") 。 |
csrfTokenManager() | 设置CsrfTokenManager 实例,用于管理CSRF 令牌的生成和验证。默认情况下,Spring Security 使用DefaultCsrfTokenManager 实例来生成和验证CSRF 令牌。 |
requireCsrfProtectionMatcher() | 设置RequestMatcher 实例,用于确定哪些请求需要进行CSRF 保护。默认情况下,Spring Security 将对所有非GET、HEAD、OPTIONS和TRACE 请求进行CSRF 保护。 |
使用了 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防御
到这里我的分享就结束了,欢迎到评论区探讨交流!!
💖如果觉得有用的话还请点个赞吧 💖
相关文章:
【Spring Security】认证密码加密Token令牌CSRF的使用详解
🎉🎉欢迎来到我的CSDN主页!🎉🎉 🏅我是Java方文山,一个在CSDN分享笔记的博主。📚📚 🌟推荐给大家我的专栏《Spring Security》。🎯🎯 …...
python一点通: 一文讲清Post 和 Put操作区别!
当我们使用网络服务时,如果我们不能小心地区分 POST 和 PUT,有时可能会触发错误。 在 Web 开发世界中,特别是在处理 RESTful API 时,HTTP 方法 POST 和 PUT 经常被使用,但常常被误解。这两者都用于向服务器发送数据&a…...
通过 Higress Wasm 插件 3 倍性能实现 Spring-cloud-gateway 功能
作者:韦鑫,Higress Committer,来自南京航空航天大学分布式系统实验室 导读:本文将和大家一同回顾 Spring Cloud Gateway 是如何满足 HTTP 请求/响应转换需求场景的,并为大家介绍在这种场景下使用 Higress 云原生网关的…...
0.618算法和基于Armijo准则的线搜索回退法
0.618代码如下: import math # 定义函数h(t) t^3 - 2t 1 def h(t): return t**3 - 2*t 1 # 0.618算法 def golden_section_search(a, b, epsilon): ratio 0.618 while (b - a) > epsilon: x1 b - ratio * (b - a) x2 a ratio * (b - a) h_…...
DPDK单步跟踪(3)-项目配置和单步跟踪
项目配置 下面都是示例的情况,请大家根据自己的工程来修改 ## 首先是配置CMake build setting Debug setting 这里最重要的是: –proc-type secondary 表示这是以secondary模式启动的dpdk客户端。 ## path mapping 然后根据自己的需要,配置…...
.NET core 自定义过滤器 Filter 实现webapi RestFul 统一接口数据返回格式
之前写过使用自定义返回类的方式来统一接口数据返回格式,.Net Core webapi RestFul 统一接口数据返回格式-CSDN博客 但是这存在一个问题,不是所有接口会按照定义的数据格式返回,除非每个接口都返回我们自定义的类,这种实现起来不…...
vue3 使用addRoute动态添加路由,页面刷新就白屏解决办法
问题,通过接口动态添加路由,第一次从登录页跳转还是正常的,说明路由添加成功了,但是刷新后就白屏了,且控制台报错路由匹配不到,在项目的main.js,router和路由拦截器中添加了一大堆打印后发现&am…...
探索鸿蒙:了解华为鸿蒙操作系统的基础课程
目录 学习目标: 学习内容: 学习时间: 学习产出: 介绍鸿蒙操作系统的起源和发展历程。 理解鸿蒙操作系统的核心概念和体系结构。 学习如何搭建和配置鸿蒙开发环境。 掌握基础的鸿蒙应用开发技术,包括应用的创建、…...
【Linux】进程周边007之进程控制
👀樊梓慕:个人主页 🎥个人专栏:《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》 🌝每一个不曾起舞的日子,都是对生命的辜负 目录 前言 1.进程创建 2.进程终止 2.…...
【C++】vector容器的模拟实现
目录 一,框架设计 二,构造函数 三,析构函数 四,赋值运算符 五,容器接口的实现 1,迭代器实现 2,“ [] ”运算符的实现 3,swap交换和resize重设大小 4,insert插入…...
华为Harmony——ArkTs语言
文章目录 一、简单示例二、声明式UI描述创建组件无参有参数 配置属性配置事件配置子组件 三、自定义组件基本用法基本结构成员函数/变量 一、简单示例 我们以一个具体的示例来说明ArkTS的基本组成。如下图所示,当开发者点击按钮时,文本内容从“Hello Wo…...
uniapp使用colorUI
colorUI 微动画 | ColorUI 使用文档 1:把colorui里三个文件复制到自己项目中去 App.vue </script> <style> import url(colorui/icon.css); import url(colorui/main.css); import url("colorui/animation.css");-webkit-keyframes show {…...
浅谈测试自动化selenium之POM模式
基于本人也是一个初学者,在运用POM模式的时候记录一下自己的学习笔记。 如果你是大神,那么可以略过,如果你是初学者,希望对你有帮助。 本文阐述了以下几个问题: 什么叫POM模式 为什么要用POM模式 POM模式的思想 POM模…...
什么是事件传播、事件冒泡、事件捕获?
一、事件传播 1、概述 (1)当事件发生在DOM元素上时,该事件并不完全发生在那个元素 (2)在冒泡阶段中,事件冒泡或向上传播至父级、祖父级、祖父的父级,直到 window 为止 (3&#x…...
【uniapp】uniapp中本地存储sqlite数据库保姆级使用教程(附完整代码和注释)
数据库请求接口封装 uniapp中提供了plus.sqlite接口,在这里我们对常用的数据库请求操作进行了二次封装 这里的dbName、dbPath、recordsTable 可以根据你的需求自己命名 module.exports {/** * type {String} 数据库名称*/dbName: salary,/*** 数据库地址* type {…...
微软推出了GPT-RAG:这是一个机器学习库,为在Azure OpenAI上使用RAG模式生产部署大型语言模型(LLMs)提供了企业级参考架构
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
Centos系统升级gcc版本
自己环境的gcc版本太低,影响使用SAN全家桶进行内存泄露检查 当前环境gcc版本查看 gcc --version 进行升级: 1、安装EPEL存储库 yum install epel-release -y 2、确保系统已经更新到最新版本 yum update -y 3、安装GCC编译器及其相关工具包 yum g…...
Http---HTTP响应报文
1. HTTP响应报文分析 HTTP 响应报文效果图: 响应报文说明: --- 响应行/状态行 --- HTTP/1.1 200 OK # HTTP协议版本 状态码 状态描述 --- 响应头 --- Server: Tengine # 服务器名称 Content-Type: text/html; charsetUTF-8 # 内容类型 Transfer-Encoding: chunked # 发送给客…...
iOS 开发设计 App 上架符合要求的截图
1. 真机运行截屏 2. 可以在 Apple developer 官网 Design 下找到 iPhone 边框 https://developer.apple.com/design/resources/ 不用这个边框也行,可以参考已上架 App 的图片框 3. 使用 Procreate(PhotoShop)创建符合要求的画布大小 4. 导入…...
DRF之引入
目录 一、web应用模式 【1】前后端混合开发 【2】前后端分离 二、API接口 三、接口测试工具:Postman 四、RESTful API规范 【1】什么是RESTful 【2】RESTful API的规范 2.1 数据的安全保障 2.2 接口特征表现 2.3 多数据版本共存 2.4 数据即是资源&#…...
【Skynet 入门实战练习】事件模块 | 批处理模块 | GM 指令 | 模糊搜索
文章目录 前言事件模块批处理模块GM 指令模块模糊搜索最后 前言 本节完善了项目,实现了事件、批处理、模糊搜索模块、GM 指令模块。 事件模块 什么是事件模块?事件模块是用来在各系统之间传递事件消息的。 为什么需要事件模块?主要目的是…...
Web组态可视化编辑器-by组态
演示地址: http://www.by-lot.com http://www.byzt.net web组态可视化编辑器:引领未来可视化编辑的新潮流 随着网络的普及和快速发展,web组态可视化编辑器应运而生,为人们在网络世界中创建和编辑内容提供了更加便捷的操作方式。这…...
PDF.js介绍以及使用
一、PDF.js是什么 PDF.js是一个JavaScript库,可以在现代Web浏览器中渲染和显示PDF文件。它的主要作用是将PDF文件转换为HTML5格式,以便在浏览器上进行展示和交互。 PDF.js的主要功能包括: 在浏览器中显示PDF:PDF.js使用HTML5的…...
经常使用的排序算法
一、直接插入排序 #include <stdio.h>void insert_sort(int arr[], int n){int i, j, tmp;for (i 1; i < n; i){tmp arr[i];j i - 1;while (j > 0 && arr[j] > tmp){ // 将要插入的元素与数组中的元素比较(从后向前比)arr[j …...
msyql 24day 数据库主从 主从复制 读写分离 master slave 有数据如何增加
目录 环境介绍读写分离纵向扩展横向扩展 数据库主从准备环境主库环境(master)从库配置(slave)状态分析重新配置问题分析 报错解决从库验证 有数据的情况下 去做主从清理环境环境准备数据库中的锁的机制主库配置从库配置最后给主库解锁常见错误 环境介绍 将一个数据库的数据 复…...
使用 Taro 开发鸿蒙原生应用 —— 探秘适配鸿蒙 ArkTS 的工作原理
背景 在上一篇文章中,我们已经了解到华为即将发布的鸿蒙操作系统纯血版本——鸿蒙 Next,以及各个互联网厂商开展鸿蒙应用开发的消息。其中,Taro作为一个重要的前端开发框架,也积极适配鸿蒙的新一代语言框架 —— ArkTS。 本文将…...
Linux下 自定义多线程并发快速压缩解压缩脚本
文章目录 自定义多线程压缩解压缩脚本使用 Linux下 自定义多线程并发快速压缩解压缩脚本 Linux下常用的tar工具无法支持并行 压缩和解压,对于大量小文件的解压缩,可借助pigz工具实现多线程并行工作,实现更为高效的压缩和解压缩。 自定义多线…...
ubuntu20.04下安装pcl_ubuntu安装pcl
pcl点云数据库,用来进行3D信息的获取与处理,和opencv相比较,opencv是用来处理二维信息,他是学术界与工业界针对点云最全的库,且网络上相关的资料很多。以下是pcl的安装步骤以及遇到的问题。 提前说明,本人…...
阿里云常用配置:日志采集、OSS、RAM 权限策略
文章目录 引言I 日志采集1.1 具体查询语法1.2 查询示例1.3 设置token时间(登录过期时间)II OSS2.1 设置防盗链2.2 验证Referer防盗链是否生效III 通义灵码 (智能编码)IV RAM 权限策略4.1 短信策略4.2 内容风险检测引言 SLS I 日志采集...
回顾丨2023 SpeechHome 第三届语音技术研讨会
下面是整体会议的内容回顾: 18日线上直播回顾 18日上午9:30,AISHELL & SpeechHome CEO卜辉宣布研讨会开始,并简要介绍本次研讨会的筹备情况以及报告内容。随后,CCF语音对话与听觉专委会副主任、清华大学教授郑方,…...
网站建设唐山/关键词排名优化教程
点击上方蓝色字体,选择“标星公众号”优质文章,第一时间送达关注公众号后台回复pay或mall获取实战项目资料视频新款5G手机,王者上分就是这么爽!包邮送一个!文章作者: Jitwxs 链接: https://jitwxs.cn/4135e0a9.html一、…...
输入公司名字找不到公司网站/seo免费工具
iphonex 已经上线有一段时间了,作为业界刘海屏幕第一款机型,导致全屏不能正常的全屏显示了,,所以需要对 iphonx 适配,下面就详细说说如何适配 先看一张适配前后的图: iphonex 提供的 meta 头 <meta name"vi…...
佛山百度快照优化排名/株洲seo优化
0,Matlab语言的介绍 1.什么叫计算? 在汉语中,“计算”一词的含义: 谋划 ,考虑 , 算计。随着电子计算机的产生与应用,人们对“计算”的理解发生了很大的变化。 ÿ…...
网站 黄色/如何做网站建设
核心代码 File file new File(PATH_LOGCAT);if (!file.exists()) {file.mkdirs();Log.e("wy","创建文件夹1");}全部代码 private void init(Context context) {if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {// 优先保…...
网站论坛制作/郑州网站建设七彩科技
DevOps是一种实践方法,旨在保证高质量的前提下实现研发运维一体化,从而大幅缩短系统变更从提交到部署生产环境的时间。其中,持续集成是DevOps中非常重要的一环,它涉及集成多个开发人员的代码,并不断编译和测试…...
月季花app是哪家公司开发的/保定seo网络推广
前言 新上线系统,为公司业务发展助力,大家满怀期待, 系统除了在业务上满足需要,为客户带来价值。在上线后系统是否能承受住线上压力,在高峰会不会挂掉,给公司带来损失,因此在系统上线前越早做一些事情保障未来线上平稳…...