认证授权-SpringSecurity

news2025/1/13 3:33:10

认证授权-SpringSecurity

1. 认证授权概述

1.1 认证授权概念

1.1.1 认证

在互联网中,我们每天都会使用到各种各样的 APP 和网站,在使用过程中通常还会遇到需要注册登录的情况,输入你的用户名和密码才能正常使用,也就是说成为这个应用的合法身份才可以访问应用的资源,这个过程就是认证。认证是为了保护系统的隐私数据与资源,用户的身份合法方可访问该系统的资源。

当然认证的方式有很多,常见的账号密码登录,手机验证码登录,指纹登录,刷脸登录等等。

简单说: 认证就是让系统知道我们是谁。

1.1.2 授权

认证是为了保护身份的合法性,授权则是为了更细粒度的对数据进行划分,授权是在认证通过的前提下发生的,控制不同的用户能够访问不同的资源。

授权是用户认证通过后根据用户的权限来控制用户访问资源的过程,拥有资源的访问权限则正常访问,没有权限则拒绝访问

例如视频网站的VIP用户,可以查看到普通用户看不到的资源信息。

1.2 权限数据模型

授权过程中,我们需要知道如何对用户访问的资源进行控制,需要了解一些简单的授权数据模型。

授权可以非常简单的理解成谁(Who)对什么(What)进行怎么样(How)的操作。

名词含义备注
Who主体(Subject)一般指用户,也可以是应用程序
What资源(Resource)例如商品信息,订单信息,页面按钮或程序中的接口等信息
How权限(Permission)规定了用户或程序对资源操作的许可。例如普通用户只能查看订单,管理员可修改或删除订单,这是因为普通用户和管理员用户对订单资源的操作权限不一样。

主体、资源、权限的关系图:

image-20230111210816204

主体、资源、权限相关的数据模型如下:

A. 主体(用户id、账号、密码、…)

B. 资源(资源id、资源名称、访问地址、…)

C. 权限(权限id、权限标识、权限名称、资源id、…)

D. 主体和权限关系(用户id、权限id、…)

主体、资源、权限的表结构关系:

image-20230111210957839

你会发现权限中包含了一个资源ID,多个权限可指向一个资源,我们是否可以直接在权限信息中把资源信息包含进来呢?当然,这也是很多企业开发中的做法,将权限和资源合并为权限(权限ID、权限标识、权限名称、资源名称、资源访问地址、…)

image-20230111211006882

1.3 RBAC权限模型

1.3.1 介绍

如何实现授权?业界通常基于 RBAC 模型(Role-Based Access Control -> 基于角色的访问控制)实现授权。 RBAC 认为授权实际就是who,what,how三者之间的关系(3W),即 who 对 what 进行 how 的操作。

1.3.2 基于角色访问控制

RBAC基于角色的访问控制(Role-Based Access Control)是按角色进行授权,比如:主体的角色为总经理可以查询企业运营报表,查询员工工资信息等,访问控制流程如下:

image-20230111211325247

根据上图中的判断逻辑,授权代码可表示如下:

if(主体.hasRole("总经理角色标识")){
    //查询工资 
}else{
    //权限不足
}

如果上图中查询工资所需要的角色变化为总经理和部门经理,此时就需要修改判断逻辑为“判断用户的角色是否是总经理或部门经理”,修改代码如下:

if(主体.hasRole("总经理角色标识") || 主体.hasRole("部门经理角色标识")){ 
	//查询工资 
}else{
    //权限不足
}

根据上边的例子发现,当需要修改角色的权限时就需要修改授权的相关代码,系统可扩展性差。

1.3.3 基于资源访问控制

RBAC 基于资源的访问控制(Resource-Based Access Control)是按资源(或权限)进行授权。

image-20230111211539550

同样是上面的需求,这时候我们的代码变成了

if(Subject.hasPermission("查询员工工资的权限标识")){
	// 查询员工工资
}

优点:系统设计时定义好查询工资的权限标识,即使查询工资所需要的角色变化为总经理和部门经理也不需要修授权代码,系统可扩展性强。

1.4 常见认证方式

认证(登录)几乎是任何一个系统的标配,web 系统、APP、PC客户端等都需要注册、登录、授权。

1.4.1 Cookie-Session

早期互联网以 web 为主,客户端是浏览器,所以 Cookie-Session 方式最那时候最常用的方式,直到现在,一些 web 网站依然用这种方式做认证。

认证过程大致如下:

A. 用户输入用户名、密码或者用短信验证码方式登录系统;

B. 服务端验证后,创建一个 Session 记录用户登录信息 ,并且将 SessionID 存到 cookie,响应回浏览器;

C. 下次客户端再发起请求,自动带上 cookie 信息,服务端通过 cookie 获取 Session 信息进行校验;

image-20230111211922926

弊端

  • 只能在 web 场景下使用,如果是 APP 中,不能使用 cookie 的情况下就不能用了
  • 即使能在 web 场景下使用,也要考虑跨域问题,因为 cookie 不能跨域;(域名或者ip一致,端口号一致,协议要一致)
  • cookie 存在 CSRF(跨站请求伪造)的风险
  • 如果是分布式服务,需要考虑 Session 同步(同步)问题

1.4.2 jwt令牌无状态认证

JSON Web Token(JWT-字符串)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。

**认证过程: **

A. 依然是用户登录系统;

B. 服务端验证,并通过指定的算法生成令牌返回给客户端;

C. 客户端拿到返回的 Token,存储到 local storage/Cookie中;

D. 下次客户端再次发起请求,将 Token 附加到 header 中;

E. 服务端获取 header 中的 Token ,通过相同的算法对 Token 进行验证,如果验证结果相同,则说明这个请求是正常的,没有被篡改。这个过程可以完全不涉及到查询 Redis 或其他存储;

image-20230111211933439

优点

A. 使用 json 作为数据传输,有广泛的通用型,并且体积小,便于传输;

B. 不需要在服务器端保存相关信息,节省内存资源的开销;

C. jwt 载荷部分可以存储业务相关的信息(非敏感的),例如用户信息、角色等;

1.5 技术实现

技术概述
Apache ShiroApache旗下的一款安全框架
SpringSecuritySpring家族的一部分, Spring体系中提供的安全框架, 包含认证、授权两个大的部分
CASCAS是一个单点登录(SSO)服务,开始是由耶鲁大学的一个组织开发,后来归到apereo去管
自行实现自行通过业务代码实现, 实现繁琐, 代码量大

2. SpringSecurity入门

2.1 介绍

Spring Security 是为基于 Spring 的企业应用系统提供声明式的安全访问控制解决方案的安全框架(包含: 认证 , 授权两个方面)。它提供了完整的安全性解决方案,可以在Web请求级别和方法调用级别处理身份认证和授权充分利用了Spring IOC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能。

官网地址: https://spring.io/projects/spring-security

2.2 入门

2.2.1 工程搭建

创建测试工程并引入依赖 ;

A. pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.7</version>
    <relativePath/>
</parent>

<dependencies>
    <!-- web起步依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
	<!-- springBoot整合Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    
	<!-- lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
  
</dependencies>

B. 引导类

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

C. Controller

@RestController
public class UserController {
    @GetMapping("/hello")
    public String hello(){
        return "hello security";
    }
    @GetMapping("/say")
    public String say(){
        return "say security";
    }
    @GetMapping("/register")
    public String register(){
        return "register security";
    }
}

D. 测试

访问: http://localhost:8080/hello

会自动拦截,并跳转到登录页面(SpringSecurity提供),登录之后才可以访问; 而登录的用户名和密码都是SpringSecurity中内置的默认的用户名密码, 用户名为user , 密码为控制台输出的一段随机数;

image-20230111213246220

效果:

image-20230111213302897

登录成功之后,会自动跳转到之前访问的地址:

image-20230111213319431

注意:

# 我们也可在配置文件中配置用户名和密码,实际开发中密码不应明文配置
spring.security.user.name=user
spring.security.user.password=6666

2.2.2 认证配置

自定义合法登录用户信息

上述的入门程序中, 用户名密码是框架默认帮我们生成的, 我们并没有指定, 如果我们想指定系统的访问用户名及密码, 可以通过配置的形式声明 , 声明一个 UserDetailsService 类型的 Bean。

@Configuration
@EnableWebSecurity // 开启 web 安全设置生效
public class SecurityConfig extends WebSecurityConfiguration {
    /**
     * 构建认证服务,并将对象注入spring IOC容器,用户登录时,会调用该服务进行用户合法信息认证
     */
    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        // 构建用户
        UserDetails u1 = User
                .withUsername("muziteng")
                .password("{noop}806823")
                .authorities("P1", "ROLE_ADMIN").build();
        UserDetails u2 = User
                .withUsername("lili")
                .password("{noop}806823")
                .authorities("O1", "ROLE_SELLER").build();
        inMemoryUserDetailsManager.createUser(u1);
        inMemoryUserDetailsManager.createUser(u2);
        return inMemoryUserDetailsManager;
    }
}

userDetailsService() 方法中 ,我们返回了一个 UserDetailsServicespring 容器,SpringSecurity会使用它来获取用户信息。我们暂时使用 InMemoryUserDetailsManager 实现类,并在其中分别创建了muziteng、lili两个用户,并设置密码和权限。

2.2.3 授权配置

1). 编码方式

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	
    @Bean
    public UserDetailsService userDetailsService(){
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(User.withUsername("itcast").password("{noop}123456").authorities("P1","ROLE_ADMIN").build());
        inMemoryUserDetailsManager.createUser(User.withUsername("itheima").password("{noop}123456").authorities("O1","ROLE_SELLER").build());
        return inMemoryUserDetailsManager;
    }
	
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
            .and()
            .logout()
            .and()
            .csrf().disable()
            .authorizeRequests()
            .antMatchers("/register").permitAll() //不登录即可访问
            .antMatchers("/hello").hasAuthority("P1") //具有P1权限才可以访问
            .antMatchers("/say").hasRole("SELLER") //具有SELLER 角色才可以访问
            .anyRequest().authenticated(); //其他的登录之后就可以访问
    }
}

CSRF(Cross-site request forgery)跨站请求伪造,也被称为"One Click Attack"或者 Session Riding,通常缩写为 CSRF 或者 XSRF,是一种对网站的恶意利用。

2).注解方式

在控制方法/URL的权限时, 可以通过配置类中配置的方式进行控制, 也可以使用注解 @PreAuthorize 来进行控制, 推荐使用注解:

 @GetMapping("/hello")
 @PreAuthorize("hasAuthority('P5')")
 public String hello(){
 	return "hello security";
 }

@GetMapping("/say")
@PreAuthorize("hasRole('SELLER')")
public String say(){
	return "say security";
}

使用@PreAuthorize,需要开启全局方法授权开关,加上注解@EnableGlobalMethodSecurity(prePostEnabled=true)

经过上述的入门程序的演示,我们对于SpringSecurity的基本使用有了一定的了解,但是在入门程序中存在两个问题:

A. 密码采用的是明文的,不安全 ;

B. 用户名/密码直接通过程序硬编码,不够灵活 ;

2.3 密码加密

2.3.1 可逆加密算法

加密后, 密文可以反向解密得到密码原文;

1) 对称加密

image-20230111214354250

指加密和解密使用相同密钥的加密算法。

优点: 对称加密算法的优点是算法公开、计算量小、加密速度快、加密效率高。
缺点: 没有非对称加密安全。

常见的对称加密算法:DES、3DES、DESX、Blowfish、RC4、RC5、RC6和AES

2) 非对称加密

image-20230111214413227

指加密和解密使用不同密钥的加密算法,也称为公私钥加密。假设两个用户要加密交换数据,双方交换公钥,使用时一方用对方的公钥加密,另一方即可用自己的私钥解密。

加密和解密:

  • 私钥加密,持有私钥或公钥才可以解密
  • 公钥加密,持有私钥才可解密

优点: 非对称加密与对称加密相比,其安全性更好;
缺点: 非对称加密的缺点是加密和解密花费时间长、速度慢,只适合对少量数据进行加密。

2.3.2 不可逆加密算法

一旦加密就不能反向解密得到密码原文 。通常用于密码数据加密。

常见的不可逆加密算法有:MD5、SHA、HMAC

2.3.3 MD5与Bcrypt

MD5

MD5是比较常见的加密算法,广泛的应用于软件开发中的密码加密,通过MD5生成的密文,是无法解密得到明文密码的。但是现在在大数据背景下,很多的网站通过大数据可以将简单的MD5加密的密码破解。

image-20230111214541479

网址: https://www.cmd5.com/

可以在用户注册时,限制用户输入密码的长度及复杂度,从而增加破解难度。

Bcrypt

用户表的密码通常使用 MD5 等不可逆算法加密后存储,为防止彩虹表破解,会先使用一个特定的字符串(如域名)加密,然后再使用一个随机的 salt(盐值)加密。 特定字符串是程序代码中固定的,salt 是每个密码单独随机,一般给用户表加一个字段单独存储,比较麻烦。

BCrypt 算法将 salt 随机并混入最终加密后的密码,验证时也无需单独提供之前的salt,从而无需单独处理 salt 问题。

加密密码:

BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
for (int i = 0; i < 10; i++) {
	System.out.println(bCryptPasswordEncoder.encode("123456"));
}

得到结果:

$2a$10$C6YynRFeJsSy7D/kg3d30OWnuwko7KQIEK5JrX0mWND.vuz2TqwpK
$2a$10$aSJfxH2oBtopFMbkMJ.PQ.sbSBXJH9g.9bv1mCyte/BtcU9VTs7lG
$2a$10$nVoB.eV5Uhc9FNUC36Pn0OosGh7aKlp7Sjfxaiml8NCSJ6PX1q6.m
$2a$10$2RM3mRNjz1LoZ5eeLdj.Hu15vlWIIj2zJC09vwTevBlIi5rjJStam
$2a$10$5bTOnk9hITzJd6EJMsX47uX9UdjASrPl4sEG6GJjfZGTk9f/37Q/q
$2a$10$0.PfbDnlBBWzpsw8PBjDcOtjUnwRgbSPCmhrAg5APUWor/4eQ0VVy
$2a$10$jfpPFH0DuTENicQ6vv38BeBO5YUXolS03bk1Ti3fmCrhQmBL1hYj.
$2a$10$pxR.jhV79v1po1vbhWi8CudiLTaw.W5lpl.E/dOEodfGXCJIPrJ4i
$2a$10$MvWb5LvCojzloYX9QLA8buL2Mkci2qaiMIdIH2PzGDssHUzEU21R2
$2a$10$7HLclohKrBZHvsBLDm8U/eTqe0KP2qV4F9d6jNvP4vO0pJG4wmeQy

验证密码:

BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
boolean matches = 
   bCryptPasswordEncoder.matches("123456", "$2a$10$c2sZT/LtM1ExWfZjO0yIPeTGSqMSlX7oi.SvliMbeZpT9Y4qIBDue");
System.out.println(matches);//返回值为true, 则代表验证通过; 反之, 验证不通过

2.4 程序完善

2.4.1 密码加密处理

在配置类 SecurityConfig 中配置Bean:

//配置密码加密器 ;
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
	return new BCryptPasswordEncoder();
}

//配置认证信息 , 密码使用BCryptPasswordEncoder加密 ;
@Bean
public UserDetailsService userDetailsService(){
	InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
	inMemoryUserDetailsManager.createUser(User.withUsername("muziteng")
	            .password("$2a$10$qcKkkvsoClF9tO8c9wlR/ebgU8VM39GP5ZUdsts.XSPDmE40l.BP2").authorities("P1","ROLE_ADMIN").build());
    inMemoryUserDetailsManager.createUser(User.withUsername("lili")
                   .password("$2a$10$qcKkkvsoClF9tO8c9wlR/ebgU8VM39GP5ZUdsts.XSPDmE40l.BP2").authorities("O1","ROLE_SELLER").build());
    return inMemoryUserDetailsManager;
}

2.4.2 动态查询用户

上述的案例中, 用户名密码都是在代码中写死的, 现在实际项目中, 是需要动态从数据库查询;简易的数据库表如下:

create database security_demo default charset=utf8mb4;
use security_demo;

CREATE TABLE `tb_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(100) DEFAULT NULL,
  `password` varchar(100) DEFAULT NULL,
  `roles` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT INTO `tb_user` VALUES (1, 'itcast', '$2a$10$f43iK9zKD9unmgLao1jqI.VluZ.Rr/XijizVEA73HeOu9xswaUBXC', 'ROLE_ADMIN,P1');
INSERT INTO `tb_user` VALUES (2, 'itheima', '$2a$10$f43iK9zKD9unmgLao1jqI.VluZ.Rr/XijizVEA73HeOu9xswaUBXC', 'ROLE_SELLER,O1');

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yt0x1F8h-1673445425102)(assets/image-20210117143544431.png)]

自定义UserDetailsService

@Component
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private TbUserMapper tbUserMapper;

    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        TbUser user = tbUserMapper.findByUserName(userName);
        if (user==null) {
            throw new UsernameNotFoundException("用户不存在");
        }
        //构建认证明细对象
        //获取用户权限
        List<GrantedAuthority> list = AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRoles());
        User user1 = new User(user.getUsername(),user.getPassword(),list);
        return user1;
    }
}

UserDetails是一个接口,User是该接口的实现类,封装用户的数据及用户的权限数据, 注意不要导错包 ;

在SecurityConfig中注释掉inMemoryUserDetailsManager bean,并配置加密bean:

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

整体访问流程如下:

image-20230111215150515

2.5 认证原理分析

SpringSecurity 所解决的问题就是安全访问控制,而安全访问控制功能其实就是对所有进入系统的请求进行拦截,校验每个请求是否能够访问它所期望的资源。根据前边知识的学习,可以通过 FilterAOP 等技术来实现,SpringSecurityWeb 资源的保护是靠 Filter 实现的,所以从这个 Filter 来入手,逐步深入 SpringSecurity 原理。 当初始化 SpringSecurity 时,会创建一个名为 SpringSecurityFilterChainServlet 过滤器,类型为 org.springframework.security.web.FilterChainProxy,它实现了javax.servlet.Filter,因此外部的请求会经过此类,下图是 SpringSecurity 过虑器链结构图:

image-20230111215414360

FilterChainProxy 是一个代理,真正起作用的是 FilterChainProxySecurityFilterChain 所包含的各个 Filter,同时这些Filter作为 Bean Spring 管理,它们是 SpringSecurity 核心,各有各的职责,但他们并不直接处理用户的认证,也不直接处理用户的授权,而是把它们交给了**认证管理器(AuthenticationManager)决策管理器 (AccessDecisionManager)**进行处理。

下面介绍过滤器链中主要的几个过滤器及其作用:

  • SecurityContextPersistenceFilter 这个Filter是整个拦截过程的入口和出口(也就是第一个和最后一个拦截 器),会在请求开始时从配置好的 SecurityContextRepository 中获取SecurityContext,然后把它设置给 SecurityContextHolder。在请求完成后将SecurityContextHolder 持有的 SecurityContext 再保存到配置好 的SecurityContextRepository,同时清除 securityContextHolder 所持有的 SecurityContext;
  • UsernamePasswordAuthenticationFilter用于处理来自表单提交的认证。该表单必须提供对应的用户名和密码,其内部还有登录成功或失败后进行处理的 AuthenticationSuccessHandlerAuthenticationFailureHandler,这些都可以根据需求做相关改变;
  • FilterSecurityInterceptor 是用于保护web资源的,使用 AccessDecisionManager 对当前用户进行授权访问,前面已经详细介绍过了;
  • ExceptionTranslationFilter 能够捕获来自 FilterChain 所有的异常,并进行处理。但是它只会处理两类异常: AuthenticationExceptioAccessDeniedException,其它的异常它会继续抛出。

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

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

相关文章

uniapp引入vantweapp踩坑笔记

vue-cli创建uniapp项目引入vantweapp踩坑笔记 uni-app中引入vantweappvue-cli创建uniapp项目引入vantweapp踩坑笔记一、环境准备二、项目搭建三、引入vant第一种方式第二种方式一、环境准备 我用的环境分别是 软件名称版本号下载命令\链接检查是否成功卸载命令Vue-clivue/cli…

JavaScript中的重要概念

JavaScript中的重要概念 1、标识符 标识符&#xff08;Identifier&#xff09;就是名字。JavaScript 中的标识符包括变量名、函数名、参数名、属性名、类名等。 合法的标识符应该注意以下强制规则&#xff1a; 第一个字符必须是字母、下划线&#xff08;_&#xff09;或美元…

发表计算机SCI论文,需要经历哪些阶段? - 易智编译EaseEditing

想在SCI期刊上发表一篇属于自己的论文一定是要经历四个阶段&#xff1a;论文选题、论文写作、期刊投稿和修稿反馈。 1.论文选题&#xff1a;前面已经说过在SCI期刊上发表论文是比较有难度的&#xff0c;是因为SCI期刊通常要求所接收稿件的选题具有较高的创新性和新颖性。 所以…

马蹄集 单位矩阵

单位矩阵 难度&#xff1a;白银 0时间限制&#xff1a;1秒 巴占用内存&#xff1a;64M 输入3X3的整型矩阵A,判断是否为单位矩阵&#xff0c;输出YES或者NO。 格式 输入格式&#xff1a;输入矩阵&#xff0c;空格分隔 输出格式&#xff1a;输出YES或者NO #include<bits/stdc.…

第09讲:Docker镜像制作

我们可以在容器去部署应用&#xff0c;然后把容器转为镜像给测试人员用&#xff0c;由于镜像是不能直接传输的&#xff0c;所以我们将新的镜像转成压缩文件&#xff0c;给测试人员。测试人员把我们的压缩文件还原成一个新的镜像。 一、Linux文件系统组成 Linux文件系统组成由…

mysql快速生成100W条测试数据(2)公司员工信息

首先这个就是我们需要生成的数据类型&#xff0c;这种只是我们用于测试以及学习时候使用&#xff0c;正常来讲公司架构会比这个要复杂的多 #mermaid-svg-fxDINAOpv25PiDLG {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid…

数据库,计算机网络、操作系统刷题笔记31

数据库&#xff0c;计算机网络、操作系统刷题笔记31 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle…

SpringBoot中JPA的基本使用

1、Jpa 是什么 JPA顾名思义就是Java Persistence API的意思&#xff0c;是JDK 5.0注解或XML描述对象&#xff0d;关系表的映射关系&#xff0c;并将运行期的实体对象持久化到数据库中。 2、优势 2.1标准化 JPA 是 JCP 组织发布的 Java EE 标准之一&#xff0c;因此任何声称符…

美国空运专线 美国空运专线时效多久

美国空运专线是国内到美国的优势渠道&#xff0c;为跨境卖家专门研发的货物海关清关快递/卡车配送的专线运输服务&#xff0c;范畴覆盖全部美国地区&#xff0c;各路经安排发货次数更高一些&#xff0c;时效更有保障。一、美国空运专线是什么 美国空运专线是专门将货物运送到美…

开学季如何运营跑腿系统?

现在学生们都在放寒假中&#xff0c;再过一段时间就又到开学季了&#xff0c;开学季是运营好跑腿项目的关键时刻&#xff0c;刚开学的时候如何让同学继续使用呢&#xff1f;这时候我们可以采取一些优惠政策来吸引同学&#xff0c;我们可以在后台设置一些优惠券&#xff0c;满减…

nacos1.4.0与seata搭建实现分布式物

下载nacos https://github.com/alibaba/nacos/tagshttps://github.com/alibaba/nacos/releases/tag/1.4.0下载seata https://github.com/seata/seata/releases/tag/v1.4.0注意&#xff1a;最好使用相同版本&#xff0c;不然出问题会找很久的 创建seata数据库 1.创建数据库导…

Termius ssh ubuntu、kali、centos

目录Termius ssh ubuntu、centos、kali一、Ubuntu开启ssh服务及允许root登录1、Ubuntu默认没有安装ssh的server&#xff0c;需要安装2、允许远程使用root账号ssh连接本机修改/etc/ssh/sshd_config文件3、需要重启系统或者ssh服务&#xff0c;刚安装完ssh-sserver服务默认开启4、…

【自然语言处理】实验1答案:Word2Vec TransE案例

NLP_class 学堂在线《自然语言处理》实验课代码报告&#xff0c;授课老师为刘知远老师。课程链接&#xff1a;https://www.xuetangx.com/training/NLP080910033761/1017121?channeli.area.manual_search。 持续更新中。 所有代码为作者所写&#xff0c;并非最后的“标准答案…

MyBatisPlus-注解的使用

注解 | MyBatis-PlusMyBatis-Plus 官方文档https://baomidou.com/pages/223848/#idtype因为在MyBatisPlus中都是通过实体类映射到数据库的字段中&#xff0c;所以处理好实体类与数据库表中字段的映射关系就很重要&#xff0c;MyBatisPlus提供的注解可以解决数据库中表名、字段名…

简道云教学 | 零代码应用开发软件助力应用型高校学生创新能力培养

“高校的数字化转型两大抓手&#xff0c;一个是学校管理系统的数字化建设&#xff0c;另一个是学生数字化素养的提升&#xff0c;简道云在我校信息化管理中的广泛应用是我们使用零代码平台进行创新能力培养的良好基础。” ————河南工学院 刘丹教授 河南工学院位于豫北工业名…

CSS初级教程(图例-链接-列表-表格)【第八天 完】

文章目录【1】CSS 图标【2】CSS 链接【3】CSS 列表【4】所有 CSS 列表属性【5】CSS 表格【6】CSS 所有表格属性CSS上回学习链接 CSS初级教程 颜色【第一天】 CSS初级教程 背景【第二天】 CSS初级教程 边框【第三天】 CSS初级教程 边距、高度、宽度【第四天】 CSS初级教程(轮廓…

使用批处理文件和mkvtoolnix批量修改默认音轨和音轨属性并重新混流

工具和版本 windows10 x64 MkvToolNix v73 待输出的Mkv格式文件&#xff0c;要求是同类型的文件&#xff0c;比如从迅雷下载下来同一来源的连续剧 步骤 一、添加文件 打开MkvToolNix GUI&#xff0c;添加输入文件&#xff0c;选中一个待输出的文件A 二、编辑音轨 选中音…

IOC底层实现原理介绍,手动实现IOC容器

面试官特别爱问SpringIOC底层实现&#xff0c;Spring源码晦涩难懂怎么办呢? 跟着老师手动实现一个mini ioc容器吧&#xff0c;实现后再回头看Spring源码事半功倍哦&#xff0c;就算直接和面试官讲也完全可以哦&#xff0c;类名完全按照源码设计&#xff0c;话不多说开干~!手动…

葡聚糖-叶酸;Dextran-FA;Dextran-Folicacid 结构式;科研试剂简介

名称&#xff1a;Dextran-Folicacid 中文名&#xff1a;叶酸修饰的葡聚糖 别称&#xff1a;葡聚糖-FA,葡聚糖-叶酸 存储条件&#xff1a;-20C&#xff0c;避光&#xff0c;避湿 外观: 固体或类白色絮状&#xff0c;取决于分子量 溶剂&#xff1a;溶于大部分有机溶剂&#…

HTAP 能够取代 OLAP 吗?

HTAP是什么 HTAP(Hybrid Transaction and Analytical Processing)数据库&#xff0c;也称混合型关系数据库&#xff0c;是能同时提供OLTP和OLAP的混合关系型数据库。在互联网浪潮出现之前&#xff0c;企业的数据量普遍不大&#xff0c;特别是核心的业务数据&#xff0c;通常一个…