SpringSecurity 快速入门

文章目录

    • 1. 认证授权概述
      • 1.1 认证授权概念
        • 1.1.1 认证
        • 1.1.2 授权
      • 1.2 权限数据模型
      • 1.3 RBAC权限模型
        • 1.3.1 介绍
        • 1.3.2 基于角色访问控制
        • 1.3.3 基于资源访问控制
      • 1.4 常见认证方式
        • 1.4.1 Cookie-Session
        • 1.4.2 jwt令牌无状态认证
      • 1.5 技术实现
    • 2. SpringSecurity入门
      • 2.1 介绍
      • 2.2 入门
        • 2.2.1 工程搭建
        • 2.2.2 认证配置
          • 【1】自定义合法登录用户信息
        • 2.2.3 授权配置
      • 2.3 密码加密
        • 2.3.1 可逆加密算法
        • 2.3.2 不可逆加密算法
        • 2.3.3 MD5与Bcrypt
      • 2.4 程序完善
        • 2.4.1 密码加密处理
        • 2.4.2 动态查询用户
      • 2.5 认证原理分析(==了解==)


在这里插入图片描述


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: adminpassword: 123456

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

2.2.2 认证配置
【1】自定义合法登录用户信息

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

@Configuration
//	开启web安全设置生效
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {/*** 构建认证服务,并将对象注入spring IOC容器,用户登录时,会调用该服务进行用户合法信息认证* @return*/@Beanpublic 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 {@Beanpublic 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;}@Overrideprotected 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 {@Autowiredprivate TbUserMapper tbUserMapper;@Overridepublic 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:

    @Beanpublic 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.mzph.cn/news/739445.shtml

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

相关文章

突破编程_C++_设计模式(迭代模式)

1 迭代模式的基本概念 在 C 中&#xff0c;迭代模式是一种常见的设计模式&#xff0c;它用于遍历或处理集合中的元素。迭代模式允许程序员在不了解集合内部表示的情况下&#xff0c;以一种统一和一致的方式来访问集合中的元素。这种模式的核心是迭代器对象&#xff0c;它封装了…

「CISP题库精讲」CISP题库习题解析精讲20道

前言 本篇主要对CISP教材第九章《计算环境安全》的一些习题进行讲解&#xff0c;包括20道题&#xff0c;这里只是部分习题&#xff0c;针对第九章可能会多写几章的内容&#xff0c;如果我发布的这些习题里面没有你想找的那道题&#xff0c;你也可以直接私信我&#xff0c;我加…

「连载」边缘计算(二十九)03-11:边缘部分源码(源码分析篇)

&#xff08;接上篇&#xff09; EdgeCore之matamanager 前面对EdgeCore组件的edged、devicetwin、edgehub、eventbus功能模块进行了分析&#xff0c;本节对EdgeCore组件的另一个功能模块metamanager进行剖析。metamanager作为EdgeCore中的edged模块与edgehub模块进行交互的桥…

求根节点到叶节点数字之和

题目链接 求根节点到叶节点数字之和 题目描述 注意点 树中节点的数目在范围 [1, 1000] 内0 < Node.val < 9树的深度不超过10 解答思路 深度优先遍历计算从根节点到叶子节点组成的所有数字&#xff08;每向下一层乘以10&#xff09;&#xff0c;再计算所有的数字之和…

中小型生产企业工业数据采集分析平台 规划生产流程

工业数据采集分析平台是一款优秀的工控自动化软件&#xff0c;可以用于数据采集、实时监测和过程控制、数据传输、系统联动、远程监控等多种应用&#xff0c;数据采集平台通过对设备运行状态及相关参数监视实现保证每个环节都能按照既定方案进行&#xff0c;同时缩短非正常停机…

shiro整合thymeleaf(接上一篇抛出的问题)

在上一篇末尾&#xff0c;讲到如何实现不同身份的用户&#xff0c;有不同的权限&#xff0c;从而看到不同的页面&#xff0c;下面我们就来实现下这个功能 1.导入依赖 <!--shiro整合thymeleaf--><dependency><groupId>com.github.theborakompanioni</group…

python(ogr)处理geojson为本地shp文件

前言 本次所利用的geojson数据来自https://geo.datav.aliyun.com/areas_v3/bound/410000_full.json &#xff0c;如果觉得下方代码看起来不方便&#xff0c;可以来GitHub上来看&#xff0c;在这上面还有一些辅助内容便于理解 GISpjd/GIS-union-Python (github.com)https://gi…

14.WEB渗透测试--Kali Linux(二)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;13.WEB渗透测试--Kali Linux&#xff08;一&#xff09;-CSDN博客 netcat简介内容:13.WE…

精品基于Springboot的体育用品租赁租用管理系统的设计与实现

《[含文档PPT源码等]精品基于Springboot的体育用品管理系统的设计与实现[包运行成功]》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功&#xff01; 软件开发环境及开发工具&#xff1a; Java——涉及技术&#xff1a; 前端使用技术&…

c# DbHelper的封装

c# DbHelper的封装 基于ADO.NET框架&#xff0c;封装了适用于多个关系型数据库的DbHelper。通过简洁明了的代码&#xff0c;实现了对各种数据库的高效操作。 public class DbHelper{private readonly DataBase _dataBase;public DbHelper(DataBase dataBase){_dataBase data…

SpringCloud-实现基于RabbitMQ的消息队列

消息队列是现代分布式系统中常用的通信机制&#xff0c;用于在不同的服务之间传递消息。在Spring Cloud框架中&#xff0c;我们可以利用RabbitMQ实现强大而可靠的消息队列系统。本篇博客将详细介绍如何在Spring Cloud项目中集成RabbitMQ&#xff0c;并创建一个简单的消息队列。…

【Kotlin】类和对象

1 前言 Kotlin 是面向对象编程语言&#xff0c;与 Java 语言类似&#xff0c;都有类、对象、属性、构造函数、成员函数&#xff0c;都有封装、继承、多态三大特性&#xff0c;不同点如下。 Java 有静态&#xff08;static&#xff09;代码块&#xff0c;Kotlin 没有&#xff1…

Spring AOP常见面试题

目录 一、对于AOP的理解 二、Spring是如何实现AOP的 1、execution表达式 2、annotation 3、基于Spring API&#xff0c;通过xml配置的方式。 4、基于代理实现 三、Spring AOP的实现原理 四、Spring是如何选择使用哪种动态代理 1、Spring Framework 2、Spring Boot 五…

博士推荐 | 纤维与聚合物科学博士,功能性纺织品研发主管

编辑 / 木子 审核 / 朝阳 伟骅英才 伟骅英才致力于以大数据、区块链、AI人工智能等前沿技术打造开放的人力资本生态&#xff0c;用科技解决职业领域问题&#xff0c;提升行业数字化服务水平&#xff0c;提供创新型的产业与人才一体化服务的人力资源解决方案和示范平台&#x…

二分查找【详解】

本期介绍&#x1f356; 主要介绍&#xff1a;二分查找的简单思路&#xff0c;为什么必须在有序的前提下才能使用二分查找&#xff0c;该怎么用C程序来实现二分查找&#xff0c;二分查找的局限性&#x1f440;。 文章目录 1. 题目2. 思路3. 前提条件4. 编写程序 1. 题目 在一个有…

选择性遗忘可以帮助人工智能学得更好?

最近&#xff0c;一些计算机科学家创建了一种更灵活、更灵巧的机器学习模型。诀窍在于&#xff1a;它必须定期忘记它所知道的信息。虽然这种新方法不会取代支撑最大应用程序的庞大模型&#xff0c;但它能揭示这些程序如何理解语言的更多信息。 &#xff08;PS&#xff1a;如果…

日常007:alias给长命令起个简短的别名

alias 命令可以给那些太长的命令起一个简短的别名。便于记忆&#xff0c;提高输入效率。 用法和示例 1. 创建别名 创建别名&#xff0c;使用语法alias new_commandoriginal_command&#xff0c;例如 alias llls -lF使用别名 $ ll total 32 -rw-r--r-- 1 soulio soulio 1…

【动态规划】代码随想录算法训练营第四十六天 |139.单词拆分,关于多重背包,你该了解这些! ,背包问题总结篇!(待补充)

139.单词拆分 1、题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 2、文章讲解&#xff1a;代码随想录 3、题目&#xff1a; 给定一个非空字符串 s 和一个包含非空单词的列表 wordDict&#xff0c;判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词…

【Docker】Node 容器部署及配置参数

Node.js环境标准软件基于Bitnami node 构建。当前版本为20.11.1 你可以通过轻云UC部署工具直接安装部署&#xff0c;也可以手动按如下文档操作&#xff0c;该项目已经全面开源&#xff0c;可以从如下环境获取 配置文件地址: https://gitee.com/qingplus/qingcloud-platform Q…

解决报错The following packages have unmet dependencies

一、问题描述 终端输入&#xff1a; nvcc -V 提示&#xff1a; Command nvcc not found, but can be installed with:sudo apt install nvidia-cuda-toolkit按照提示安装&#xff0c;终端输入 sudo apt install nvidia-cuda-toolkit&#xff0c;报错内容如下&#xff1a; …