SpringSecurity 快速入门

news2024/11/28 0:38:09

1. 认证授权概述

1.1 认证授权概念

1.1.1 认证

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

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

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

1.1.2 授权

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

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

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

1.2 权限数据模型

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

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

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

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

在这里插入图片描述

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

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

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

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

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

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

在这里插入图片描述

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

在这里插入图片描述

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)是按角色进行授权,比如:主体的角色为总经理可以查询企业运营报表,查询员工工资信息等,访问控制流程如下:

在这里插入图片描述

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

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

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

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

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

1.3.3 基于资源访问控制

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

在这里插入图片描述

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

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 信息进行校验;

在这里插入图片描述

弊端

  • 只能在 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 或其他存储;

在这里插入图片描述

优点

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(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能。

官网地址

2.2 入门

2.2.1 工程搭建

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

A. pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.4.RELEASE</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 StudySecurityApplication{
    public static void main(String[] args) {
        SpringApplication.run(StudySecurityApplication.class,args);
    }
}

C. Controller

@RestController
@RequestMapping("/hello")
public class HelloController {

    @GetMapping("/{msg}")
    public String Hello(@PathVariable("msg") String msg){
        return "hello:" + msg;
    }
    
    @GetMapping("/hello")
    public String hello(){
        return "hello security";
    }

    @GetMapping("/say")
    public String say(){
        return "say security";
    }

}

D. 测试

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

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

在这里插入图片描述

效果:

在这里插入图片描述

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

在这里插入图片描述

注意:

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

再次重启,我们就用配置的用户和密码登录了

2.2.2 认证配置

【1】自定义合法登录用户信息

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

@Configuration
//	开启web安全设置生效
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /**
     * 构建认证服务,并将对象注入spring IOC容器,用户登录时,会调用该服务进行用户合法信息认证
     * @return
     */
    @Bean
    public UserDetailsService userDetailsService(){
        //	定义加载用户信息的服务
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        //	构建用户
        UserDetails u1 = User
                .withUsername("admin")
                .password("{noop}123456")
                .authorities("P10", "ROLE_ADMIN").build();
        UserDetails u2 = User
                .withUsername("snow")
                .password("{noop}123456")
                .authorities("O10", "ROLE_USER").build();
        inMemoryUserDetailsManager.createUser(u1);
        inMemoryUserDetailsManager.createUser(u2);
        return inMemoryUserDetailsManager;
    }
}

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

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("admin").password("{noop}123456").authorities("P10","ROLE_ADMIN").build());
        inMemoryUserDetailsManager.createUser(User.withUsername("snow").password("{noop}123456").authorities("O10","ROLE_USER").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("USER") //具有SELLER 角色才可以访问
            .anyRequest().authenticated(); //其他的登录之后就可以访问
    }
}

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

再次重启项目 再访问就需要带相应的权限了

权限不足 如下:在这里插入图片描述


2). 注解方式

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

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

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

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

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

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

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


2.3 密码加密

2.3.1 可逆加密算法

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

1). 对称加密

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

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

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

2). 非对称加密
在这里插入图片描述

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

加密和解密:

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

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

2.3.2 不可逆加密算法

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

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

2.3.3 MD5与Bcrypt

1).MD5

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

在这里插入图片描述

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

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

2). 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$MLJ9f5jViB3rRsQTtxLclumRsK5mdhXsMxe9YBUMyaorTHKwgUEsW
$2a$10$TTAKm8GHEHahRtNDVj8jjeuuJ2AnDaEORHO.3qbeXEpQ8YboPyV3C
$2a$10$wydZqWCBuppICk3YvCw/pOGHVuwtp8qCPCZ1aRjr6904vKXtGnjI6
$2a$10$Cu4ckrKXfTOITh5rl4dLx.h59Gtfn9kdaayVfO/d//4avMQrbTHMe
$2a$10$SKpM1IHOvIUgPTuvTlAtEu9mxANFJUiKc2YNMVQY.Y2.fFVtAdpuG
$2a$10$fKBz3tBR4vt02afJscNa0ulPE.V0/DdNYrv.miuou4mo8vuDIC27u
$2a$10$IludxV8mcTLMnm0wEAOinuvYnJmzCUbvrcwsoSTRw3l9JBXuvSM1q
$2a$10$yAvHC4KobuuPMP10Z9XYh.jXo6y0orDsKSH6xFVe3oZlWiNhDNg/6
$2a$10$WBPYtRG19HkPDtUUJK57r.NjHQ/xocucNfZuinwZQlddFdxEiejoG
$2a$10$YZOzdhz03TMagK4xJRpF7ek51EWNkereo0nu01GRZjfJsKuxf0hEG

在这里插入图片描述

验证密码:

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("admin")
	               .password("$2a$10$qcKkkvsoClF9tO8c9wlR/ebgU8VM39GP5ZUdsts.XSPDmE40l.BP2").authorities("P10","ROLE_ADMIN").build());
    inMemoryUserDetailsManager.createUser(User.withUsername("snow")
                   .password("$2a$10$qcKkkvsoClF9tO8c9wlR/ebgU8VM39GP5ZUdsts.XSPDmE40l.BP2").authorities("O10","ROLE_USER").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, 'admin', '$2a$10$f43iK9zKD9unmgLao1jqI.VluZ.Rr/XijizVEA73HeOu9xswaUBXC', 'ROLE_ADMIN,P10');
INSERT INTO `tb_user` VALUES (2, 'snow', '$2a$10$f43iK9zKD9unmgLao1jqI.VluZ.Rr/XijizVEA73HeOu9xswaUBXC', 'ROLE_USER,O10');

A. pom.xml

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

B. application.yml
略略略…


C. 实体类


@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class TbUser implements Serializable {
    /**
     * 
     */
    private Integer id;

    /**
     * 
     */
    private String username;

    /**
     * 
     */
    private String password;

    /**
     * 
     */
    private String roles;

    private static final long serialVersionUID = 1L;
}

D. mapper
略略略…


D. 自定义UserDetailsService

package com.sss.security.config;

import com.sss.security.mapper.TbUserMapper;
import com.sss.security.pojo.TbUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @Description
 */
@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();
    }

整体访问流程如下:

在这里插入图片描述


2.5 认证原理分析(了解)

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

在这里插入图片描述

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

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

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

接处理用户的认 证,也不直接处理用户的授权,而是把它们交给了认证管理器
(AuthenticationManager)和决策管理器 (AccessDecisionManager)进行处理。

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

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

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

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

相关文章

项目开发工具【EditorConfig,Prettier,ESLint,Git Husky,Commitizen,Commitlint】

项目开发工具【六款】 前言 六款插件让你项目更加的规范✨✨✨ EditorConfig&#xff1a;为不同编辑器上处理同一项目的多个开发人员维护一致的编码风格Prettier&#xff1a;一款强大的代码格式化工具&#xff0c;是当下前端最流行的代码格式化工具ESLint&#xff1a;查找潜在…

图像增强库albumentations(v1.3.0, 2023.5)中所有图像增强方法记录(class)

整体概要 变换类型具体类模糊/噪声AdvancedBlur*&#xff0c;Blur*&#xff0c;Downscale*&#xff0c;Defocus*&#xff0c;GlassBlur*&#xff0c;GaussianBlur*&#xff0c;GaussNoise*&#xff0c;ImageCompression*&#xff0c;ISONoise*&#xff0c;MultiplicativeNoise…

MySQL基础-SQL介绍

本文介绍MySQL中SQL通用语法&#xff0c;包括&#xff1a;DDL&#xff0c;DML&#xff0c;DQL&#xff0c;DCL 文章目录 SQL分类DDL&#xff08;数据定义语言&#xff09;数据库操作表操作 DML&#xff08;数据操作语言&#xff09;添加数据更新和删除数据 DQL基础查询条件查询…

Linux sed流编辑器

sed流编辑器 sed是一种流编辑器&#xff0c;流编辑器会在编辑器处理数据之前基于预先提供的一组规则来编辑数据流。 sed编辑器可以根据命令来处理数据流中的数据&#xff0c;这些命令要么从命令行中输入&#xff0c;要么存储在一个命令文本文件中。 sed 的工作流程主要包括读取…

智能的本质人工智能与机器人领域的64个大问题阅读笔记(二)

目录 如果一个人不能理解所说语言的全部信息&#xff0c;那么说这种语言也是没有意义的。 “20年内&#xff0c;机器将能够胜任人类承担的所有工作”&#xff08;赫伯特西蒙&#xff0c; 1965&#xff09;。 以及21世纪的人类愿意相信超智能机器时代的到来&#xff0c;与前几…

项目中大批量数据查询导致OOM

项目中有时候一次性将大批量数据都查出来到内存中导致内存占用过多很可能会导致内存溢出 问题复现 搭建一个本地项目。需求描述&#xff1a;查询表call_task中待拨打的数据进行拨打&#xff0c;call_task中一次可能会有大批量数据需要处理。本次准备了1万条数据。 /*** 查询…

【Java校招面试】基础知识(九)——Redis

目录 前言一、基础概念二、Redis持久化三、Redis分布式存储后记 前言 本篇主要介绍Redis数据库的相关内容。 “基础知识”是本专栏的第一个部分&#xff0c;本篇博文是第九篇博文&#xff0c;如有需要&#xff0c;可&#xff1a; 点击这里&#xff0c;返回本专栏的索引文章点…

(c语言实现)数据结构链表oj题(2)

前言 &#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏: &#x1f354;&#x1f35f;&#x1f32f;C语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f349;本篇简介:>:分析力扣中有关链表的部分题目. 目录 前言一、链表中倒…

HTML第二天

第二天 表格 表格的主要作用 1.表格主要用于显示、展示数据&#xff0c;因为它可以让数据显示的非常的规整&#xff0c;可读性非常好。特别是后台展示数据的时候&#xff0c;能够熟练运用表格就显得很重要。一个清爽简约的表格能够把繁杂的数据表现得很有条理2.表格不是用来…

Java基础知识:1,DOS命令

1&#xff0c;盘名称 加 : 进入该盘目录下 例如&#xff1a;e: 进入e盘 2&#xff0c;dir 查看当前路径下文件和文件夹 3&#xff0c;md 文件夹名字 》 创建文件夹&#xff08;md后要加空格&#xff09; &#xff08;md make directory&#xff09; 4&#xff0c;c…

Oracle11g下载与安装

一、Oracle11g下载 官网下载地址&#xff1a;Oracle Database 11g Release 2 for Microsoft Windows (x64) 选择"Accept License Agreement"&#xff0c;点击"win64_11gR2_database_1of2.zip"和"win64_11gR2_database_2of2.zip"&#xff0c;进行…

Debian11之 K3s 部署K8S集群

K3S 架构方案 Server 节点指的是运行 k3s server 命令的主机&#xff0c;control plane 和数据存储组件由 K3s 管理Agent 节点指的是运行 k3s agent 命令的主机&#xff0c;不具有任何数据存储或 control plane 组件Server 和 Agent 都运行 kubelet、容器运行时和 CNI 基于 …

GPUAnimation的Mesh动画烘焙到贴图,学习笔记

0.动画烘焙贴图工具作者 Unity-GPU Animation - 知乎 1.安装学习项目 GitHub - striter/Unity3D-ToolChain_StriteR 复制到URP项目中&#xff0c;并删掉多余报错的和GPUAnimation无关的模块 2.把学习项目中的HLSL搬到低版本Unity的CG版本 安装FBX Exporter把烘焙好的mesh导…

【秒杀系统】高并发编程入门学习

文章目录 一、秒杀系统的基本构成二、秒杀系统的常见问题三、秒杀系统的解决技术 一、秒杀系统的基本构成 二、秒杀系统的常见问题 高并发 大量的请求进来&#xff0c;在Redis缓存层会发生缓存雪崩 缓存击穿 缓存穿透这些问题&#xff0c;这些问题就会打挂MySQL。这样就会导致…

23种设计模式之建造者模式(Builder Pattern)

前言&#xff1a;大家好&#xff0c;我是小威&#xff0c;24届毕业生&#xff0c;在一家满意的公司实习。本篇文章将23种设计模式中的建造者模式&#xff0c;此篇文章为一天学习一个设计模式系列文章&#xff0c;后面会分享其他模式知识。 如果文章有什么需要改进的地方还请大佬…

C++ STL 之 list 的模拟实现

文章目录 &#x1f4cd;前言&#x1f308;STL之list的模拟实现&#x1f388;list_node节点的定义&#x1f388;iterator迭代器&#x1f56f;️构造函数&#x1f56f;️*it&#x1f56f;️->&#x1f56f;️it/it&#x1f56f;️it--/--it&#x1f56f;️! / &#x1f388;l…

[MAUI程序设计]界面多态与实现

文章目录 需求一&#xff1a;在不同设备上使用不同 UI 外观定义控件 UI 外观基于平台自定义配置 需求二&#xff1a;在不同数据类别中使用不同的 UI 外观定义视图 UI 外观创建数据模板创建选择器定义数据 需求三&#xff1a;在不同数据状态中使用不同的 UI 外观使用绑定模型更改…

【腾讯云FinOps Crane 集训营】Crane 助力云原生用户充分发挥云上资源的最大价值,帮助企业降本增效之利器

文章目录 Crane 助力云原生用户充分发挥云上资源的最大价值&#xff0c;帮助企业降本增效之利器前言云上资源成本虚高的现状一、云原生的崛起&#xff1a;服务上云二、服务上云的魔咒&#xff1a;服务健壮了&#xff0c;钱袋子却空了三、钱都去哪了&#xff1a;云资源无序投入的…

golang 微服务的负载均衡

上次我们说了一下 微服务的容错处理&#xff0c;是用的 断路器 这一次我们来一起看看 微服务的负载均衡是如何做的 负载均衡 负载均衡是什么呢&#xff1f; 他能够将大量的请求&#xff0c;根据负载均衡算法&#xff0c;将不同的请求分发到多台服务器上进行处理&#xff0c…

Git Mac设置系统命令别名和Git命令别名

有时候git命令的别名过长要如何&#xff0c;在命令行不方便输入&#xff0c;这时候我们可以设置命令别名。 设置系统命令别名 设置系统命令别名可以在.bash_profile文件中配置&#xff0c;这个文件也是我们经常配置环境变量的地方&#xff0c;这个文件本身是不可见的&#xf…