Spring Security 中重要对象汇总

news2024/11/30 0:46:43

前言

已经写了好几篇关于 Spring Security 的文章了,相信很多读者还是对 Spring Security 的云里雾里的。这是因为对 Spring Security 中的对象还不了解。本文就来介绍介绍一下常用对象。

认证流程

SecurityContextHolder

用户认证通过后,为了避免用户的每次操作都进行认证,可将用户的信息保存在会话中。Spring Security 提供会话管理,认证通过后将身份信息放入 SecurityContextHolder 上下文,SecurityContext 与当前线程进行绑定,方便获取用户身份。

// 获取当前登录的用户信息
Authentication authentication = 
SecurityContextHolder.getContext().getAuthentication();
复制代码

AuthenticationManager

认证管理器,AuthenticationManager 是认证相关的核心接口,是发起认证的入口,用于处理认证请求。接口只提供了一个认证方法,方法接收一个未通过认证 Authentication 对象,返回一个通过认证的 Authentication 对象。最常见的实现是ProviderManager

public interface AuthenticationManager {
    Authentication authenticate(Authentication var1) throws AuthenticationException;
}
复制代码

ProviderManager

提供商管理器,ProviderManagerAuthenticationManager 的一个实现类,提供了基本的认证逻辑和方法。它其中包含了一个 List 的 AuthenticationProvider 的属性,该属性存放多种认证方式!为什么需要这个属性呢?当Spring Security默认提供的认证方式不能满足需求时,就可以通过 AuthenticationProvider 接口来扩展出其他认证方式,比如邮箱+验证码,手机号码+验证码登录。

AuthenticationProvider

AuthenticationProvider(身份验证提供者),可以将多个AuthenticationProvider实例添加到ProviderManager中。其每个AuthenticationProvider可以执行特定的 Authentication (身份验证)类型。例如:DaoAuthenticationProvider支持基于用户名+密码的 UsernamePasswordAuthenticationToken 身份验证。也可以自定义认证方式,比如自定义EmailVerificationCodeAuthenticationProvider支持邮箱 + 验证码的 EmailVerificationCodeAuthenticationToken 身份验证。

public interface AuthenticationProvider {

    Authentication authenticate(Authentication authentication) throws AuthenticationException;

    boolean supports(Class<?> authentication);
}
复制代码

该接口中有两个方法,如下:

  • authenticate() 方法接收一个未通过认证 Authentication 对象,返回一个通过认证的 Authentication 对象。可以实现 authenticate() 方法来自定义身份验证逻辑。

  • supports(Class<?> authentication) 方法接收一个 Authentication(身份验证) 对象,如果 AuthenticationProvider 支持指定的身份验证对象,则返回 true。 但是返回 true 并不保证 AuthenticationProvider 能够对提供的 Authentization 类实例进行身份验证。它只是表明它可以支持对其进行更深入的验证。AuthenticationProvider 仍可以从 authenticate() 方法返回 null,以尝试其他的 AuthentitationProvider 进行验证。

Authentication

Authentication(身份验证) 接口是 Spring Security 中身份验证流程的顶级接口,该接口定义了如下方法:

public interface Authentication extends Principal, Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();

    Object getCredentials();

    Object getDetails();

    Object getPrincipal();

    boolean isAuthenticated();

    void setAuthenticated(boolean var1) throws IllegalArgumentException;
}
复制代码

方法含义如下:

方法描述
getAuthorities获取登录用户的权限列表
getCredentials获取凭据。用户密码登录,这个字段就是密码信息,在认证过后通常会被移除,用于保障安全。如果是手机号验证码登录,那这个字段存的就是验证码
getDetails包含了一些认证时的信息,默认的实现为 WebAuthenticationDetails,记录了访问者的远程地址和sessionId的值。
getPrincipal身份信息,默认情况下返回的是 UserDetails的实例
isAuthenticated是否通过认证,通过认证为 true
setAuthenticated设置是否已认证
getName用户名

具体响应内容可以参考如下:

{
	"authorities": [
		{
			"authority": "ROLE_admin"
		},
		{
			"authority": "ROLE_user"
		}
	],
	"details": {
		"remoteAddress": "0:0:0:0:0:0:0:1",
		"sessionId": "D77AF630A476DEE7A2A75B1D751C4CF1"
	},
	"authenticated": true,
	"principal": {
		"password": null,
		"username": "cxyxj",
		"authorities": [
			{
				"authority": "ROLE_admin"
			},
			{
				"authority": "ROLE_user"
			}
		],
		"accountNonExpired": true,
		"accountNonLocked": true,
		"credentialsNonExpired": true,
		"enabled": true
	},
	"credentials": null,
	"name": "cxyxj"
}
复制代码

Authentication 本身是一个接口,它有很多实现类:

在众多的实现类中,我们最常用的就是 UsernamePasswordAuthenticationToken(用户名密码身份验证令牌),但是这个类就只有简单的50行左右的代码,其中有两个属性,principal 代表用户名,credentials 代表密码。还有两个构造方法,一个是代表未认证的,一个是代表已认证的;三个set、get方法,一个擦除凭据方法。该类继承了 AbstractAuthenticationToken,其大部分逻辑在父类中,当然父类的逻辑也非常简单。 所以 UsernamePasswordAuthenticationToken(用户名密码身份验证令牌)的作用就是将用户输入的用户名和密码进行封装,并供给 AuthenticationManager 进行验证。

UserDetails

这个接口定义了用户的核心信息,比如用户名、密码、账号是否过期、是否锁定等!默认实现类org.springframework.security.core.userdetails.User。在 Spring Security 中,如果自定义认证逻辑时,需要实现该接口进行扩展,来保存自己系统的用户信息。接口定义如下方法:

方法描述
getAuthorities获取用户权限
getPassword获取用户密码
getUsername获取用户名
isAccountNonExpired账户是否未过期,true:未过期,false:过期
isAccountNonLocked账户是否未锁定,true:未锁定,false:锁定
isCredentialsNonExpired凭证(密码)是否未过期,true:未过期,false:过期
isEnabled账户是否启用,true:启用,false:禁用

一个正常能登录的账号,四个状态都是为 true 的。

UserDetailsService

在 Spring Security 中,什么也不进行配置时,账号和密码是由 Spring Security 自动生成的。 但在实际的项目中账号、密码是从数据库中查询出来的。所以我们需要自定义认证逻辑。 此时需要实现 UserDetailsService 接口。而 UserDetailsService 接口中只定义了一个方法,作用是根据用户名加载用户,获得 UserDetails 对象。

public interface UserDetailsService {

        // 按用户名加载用户
	UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
复制代码

可以参考自定义逻辑如下:

@Override
public UserDetails loadUserByUsername(String account) throws UsernameNotFoundException {
    LambdaQueryWrapper<SysUser> queryWrapper = Wrappers.lambdaQuery();
    queryWrapper.eq(SysUser::getAccount, account);
    // 根据用户名查询用户
    SysUser sysUsers = sysUserMapper.selectOne(queryWrapper);
    if (Objects.isNull(sysUsers)) {
        Assert.isTrue(true,"用户名或者密码错误");
    }
    // 获得用户角色信息
    List<String> roles = sysUserMapper.selectByUserId(sysUsers.getUserId());
    // 构建 SimpleGrantedAuthority 对象
    List<SimpleGrantedAuthority> authorities = roles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
    return new SysUserDetails(sysUsers, authorities);
}
复制代码

除了需要手动实现 UserDetailsService 接口的方式外,Spring Security 也内置了几种方式。 我们来看下 UserDetailsService 都有哪些实现类:

  • InMemoryUserDetailsManager:内存用户,这种方式在学习 Spring Security 的时候,用的非常多。

  • JdbcUserDetailsManager:通过 JDBC 的方式将数据库和 Spring Security 连接起来。它自己提供了一个数据库脚本,脚本路径如下:org/springframework/security/core/userdetails/jdbc/users.ddl。脚本的内容呢,应该是不符合项目实际开发的,所以这个也是在学习的时候可以使用使用。

PasswordEncoder

PasswordEncoder 接口用于执行密码的单向转换,以便安全地存储密码。

InteractiveAuthenticationSuccessEvent

身份验证成功后,发布一个名为InteractiveAuthenticationSuccessEvent的事件通知给到应用上下文,用于告知身份验证已经成功。

FilterChainProxy

在 Spring Security 的默认配置中,将创建一个名为 springSecurityFilterChain 的 servlet 过滤器作为bean。默认情况下,Spring Security 内置了一个过滤链,链中有 15 个过滤器。

想要了解更多请前往: 深入理解 FilterChainProxy【源码篇】

ExceptionTranslationFilter

ExceptionTranslationFilter 异常转换过滤器位于整个 springSecurityFilterChain 的后方,用来转换整个链路中出现的异常。此过滤器本身不处理异常,而是将认证过程中出现的异常交给内部维护的一些类去处理,一般处理两大类异常:AccessDeniedException 已登录无权限访问异常和 AuthenticationException 未认证访问异常。

HeaderWriterFilter

用来给http响应添加一些Header,比如X-Frame-Options, X-XSS- Protection*,X-Content-Type-Options.

CsrfFilter

用于防止csrf攻击(跨站点请求伪造(Cross- site request forgery))。

LogoutFilter

处理注销的过滤器。

RequestCacheAwareFilter

内部维护了一个RequestCache,用于缓存request请求。

SecurityContextHolderAwareRequestFilter

对ServletRequest进行了一次包装,使得request 具有更加丰富的API。

SessionManagementFilter

和session相关的过滤器,内部维护了一个 SessionAuthenticationStrategy,两者组合使用,常用来防止会话固定攻击保护( session- fixation protection attack ),以及限制同一用户开启多个会话的数量。

AnonymousAuthenticationFilter

匿名身份过滤器,spring security为了兼容未登录的访问,也走了一套认证流程,只不过是一个匿名的身份。

UsernamePasswordAuthenticationFilter

表单提交了username和password参数,被封装成token进行一系列的认证,便是主要通过这个过滤器完成的。在表单认证的流程中,这是最最关键的过滤器。

AbstractAuthenticationProcessingFilter

翻译为:抽象身份验证处理过滤器,这是一个抽象类,定义了认证处理的过程。是一个模板类。默认的实现为 UsernamePasswordAuthenticationFilter,根据用户名、密码进行身份验证,如果需要自定义身份验证,比如手机验证码登录,就需要继承该类。

授权

当用户访问 Spring Security 中一个受保护的资源时,需要使用投票器和表决机制,投票器根据用户的角色投出赞成或者反对票,表决方式则根据投票器的结果进行表决。

AccessDecisionManager

访问决策管理器,AccessDecisionManagerAbstractSecurityInterceptor调用。AccessDecisionManager 采用投票的方式来确定是否能够访问受保护资源。 AccessDecisionManager 中包含的多个 AccessDecisionVoter,Voter 将会被用来对Authentication是否有权访问受保护对象进行投票, AccessDecisionManager 根据投票结果,做出最终决策。

AccessDecisionManager 访问决策管理器还有三个子类决策器,分别是:

  • AffirmativeBased:存在多个投票器时,有一个投票器同意,则请求就允许访问,也就是一票通过;默认使用的决策器。
  • UnanimousBased:存在多个投票器时,如果有投票器拒绝,则请求不允许访问,也就是一票否决。
  • ConsensusBased:存在多个投票器时,大多数投票器同意。则请求就允许访问,也就是少数服从多数。如果是平局,则看 allowIfEqualGrantedDeniedDecisions 的值来判断是否通过,在默认情况下allowIfEqualGrantedDeniedDecisions值是true,也就是说平票的情况下,请求允许访问。

注意:不论是哪个决策器,如果所有投票器全部弃权则表示通过。

AccessDecisionVoter

翻译为:投票机制/投票器。在 Spring Security 中,投票机制是由 AccessDecisionVoter 接口来定义的,它有许多的实现:

实现有好多种,我们可以选择其中一种或多种投票机制,也可以自定义投票机制,默认的投票机制是 WebExpressionVoter。

public interface AccessDecisionVoter<S> {
	
	int ACCESS_GRANTED = 1;
	int ACCESS_ABSTAIN = 0;
	int ACCESS_DENIED = -1;


	boolean supports(ConfigAttribute attribute);

	boolean supports(Class<?> clazz);

	
	int vote(Authentication authentication, S object,
			Collection<ConfigAttribute> attributes);
}
复制代码
  • 三个常量含义分别为:1 表示同意;0 表示弃权;-1 表示拒绝。
  • 两个 supports 方法用来判断投票器是否支持当前请求。
    1. vote 则是具体的投票方法;在不同的实现类中实现。authentication 表示当前登录主体;object 表示正在调用的受保护接口;attributes 表示当前所访问的接口所需要的角色集合。

  • RoleVoter:判断当前请求是否具备该接口所需要的角色。
  • RoleHierarchyVoter 是 RoleVoter 的一个子类,在 RoleVoter 角色判断的基础上,引入了角色分层管理,也就是角色继承。
  • WebExpressionVoter:基于表达式权限控制
  • Jsr250Voter:处理 Jsr-250 权限注解的投票器,如 @PermitAll@DenyAll 等
  • PreInvocationAuthorizationAdviceVoter:使用 @PreFilter 和 @PreAuthorize 注解处理的权限,通过 PreInvocationAuthorizationAdvice 来授权。

AbstractSecurityInterceptor

根据注释翻译了一下:为安全对象实现安全拦截的抽象类,干的事情说白点就是对未放行的资源,根据用户的权限来控制是否能访问的拦截器。由于这是一个抽象类,所以只定义了一些逻辑方法,具体执行都是子类去调用的。

FilterSecurityInterceptor

从 FilterChainProxy 章节中的截图来看,FilterSecurityInterceptor 位于 Spring Security Filter Chain 中的最后一个 Filter。这是一个过滤器,它会拦截HTTP请求,进行鉴权处理。

MethodSecurityInterceptor

它还实现了 MethodInterceptor,所以这是一个方法拦截器,基于 Spring AOP 实现了方法拦截,对方法进行鉴权处理。比如在方法上标注了@RolesAllowed@PermitAll@PreAuthorize等等注解。

AspectJMethodSecurityInterceptor

继承了 MethodSecurityInterceptor,基于 Aspectj 实现方法拦截。

区别

  • 一个是过滤器,一个是方法拦截器,所以他们拦截对象是不同,一个是拦截Http请求,一个是拦截方法。
  • 两者都调用了父类的beforeInvocation方法,但是传入的参数是不一样的,FilterSecurityInterceptor传入的是FilterInvocation,MethodInterceptor传入的是MethodInvocation。
  • 两者维护的 SecurityMetadataSource 不一样,MethodSecurityInterceptor 中维护的是MethodSecurityMetadataSource,FilterSecurityInterceptor维护的是 FilterInvocationSecurityMetadataSource。

SecurityMetadataSource

获取授权配置的接口。可以自定义实现该接口,比如从数据库中加载ConfigAttribute。在 Spring Security中,给该接口提供了两个子类,继承图如下:

  • MethodSecurityMetadataSource:由Spring Security Core定义,用于表示安全对象是方法调用(MethodInvocation)的安全元数据源;存放的是所有类和方法,会根据当前执行的类和方法,去内存中遍历,查询到当前执行方法配置的权限注解,然后对其进行授权判断。

  • FilterInvocationSecurityMetadataSource:由Spring Security Web定义,用于表示安全对象是Web请求(FilterInvocation)的安全元数据源;存放的是 HttpSecurity 配置类中配置的授权规则。

配置如下:

http.authorizeRequests()
    // 如果用户具备 admin 权限,就允许访问。
    .antMatchers("/cxyxj/**").hasAuthority("ROLE_admin")
// 如果用户具备给定权限中某一个,就允许访问。
    .antMatchers("/admin/demo").hasAnyAuthority("ROLE_admin", "ROLE_System")
// 如果用户具备 user 权限,就允许访问。注意不需要手动写 ROLE_ 前缀,写了会报错
    .antMatchers("/security/**").hasRole("user")
//如果请求是指定的 IP 就允许访问。
    .antMatchers("/admin/demo").hasIpAddress("192.168.64.5")
    .anyRequest()   //其他请求
    .authenticated(); //需要认证才能访问
复制代码

参考文献

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

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

相关文章

【JavaWeb】Servlet系列 --- HttpServlet【底层源码分析】

HttpServlet一、什么是协议&#xff1f;什么是HTTP协议&#xff1f;二、HTTP的请求协议&#xff08;B -- > S&#xff09;1. HTTP的请求协议包括4部分&#xff08;记住&#xff09;2. HTTP请求协议的具体报文&#xff1a;GET请求3. HTTP请求协议的具体报文&#xff1a;POST请…

生成式模型和判别式模型

决策函数Yf(x)Y f(x)Yf(x)或者条件概率分布 P(Y∣X)P(Y|X)P(Y∣X) 监督学习的任务都是从数据中学习一个模型&#xff08;也叫做分类器&#xff09;&#xff0c;应用这一模型&#xff0c;对给定的输入xxx预测相应的输出YYY,这个模型的一般形式为:决策函数Yf(x)Y f(x)Yf(x)&…

java 每日一练(6)

java 每日一练(6) 文章目录单选不定项选择题编程题单选 1.关于抽象类与最终类&#xff0c;下列说法错误的是&#xff1f;   A 抽象类能被继承&#xff0c;最终类只能被实例化。   B 抽象类和最终类都可以被声明使用   C 抽象类中可以没有抽象方法&#xff0c;最终类中可以没…

Bean 管理(工厂bean)

IOC操作Bean 管理&#xff08;FactoryBean&#xff09; 下面是在Bean 管理&#xff08;工厂bean&#xff09;之前的学习&#xff0c;基于xml方式注入集合并实现 基于xml方式注入集合并实现 &#xff1a;http://t.csdn.cn/H0ipR Spring 有两种类型bean&#xff0c;一种普通bean…

第五章. 可视化数据分析分析图表—图表的常用设置2

第五章. 可视化数据分析分析图 5.2 图表的常用设置2 本节主要介绍图表的常用设置&#xff0c;设置标题和图例&#xff0c;添加注释文本&#xff0c;调整图表与画布边缘间距以及其他设置。 7.设置标题&#xff08;title&#xff09; 1).语法&#xff1a; matplotlib.pyplot.ti…

iOS15适配 UINavigationBar和UITabBar设置无效,变成黑色

今天更新了xcode13&#xff0c;运行项目发现iOS15以上的手机导航栏和状态栏之前设置的颜色等属性都不起作用了&#xff0c;都变成了黑色&#xff0c;滚动的时候才能变成正常的颜色&#xff0c;经确认得用UINavigationBarAppearance和UITabBarAppearance这两个属性对导航栏和状态…

开发SpringBoot+Jwt+Vue的前后端分离后台管理系统VueAdmin - 前端笔记

一个spring security jwt vue的前后端分离项目&#xff01;综合运用&#xff01; 关注公众号 MarkerHub&#xff0c;回复【 VueAdmin 】可以加群讨论学习、另外还会不定时安排B站视频直播答疑&#xff01; 首发公众号&#xff1a;MarkerHub 作者&#xff1a;吕一明 视频讲解&…

半年卖8万吨辣条,卫龙再闯IPO

“辣条大王”卫龙美味全球控股有限公司&#xff08;下称“卫龙”&#xff09;于11月23日重新递表&#xff0c;继续冲刺“辣条第一股”。 作为千禧一代撑起的童年“神话”&#xff0c;卫龙的上市之路却波折重重&#xff1b;它曾于2021年5月、2021年11月及此次重启IPO。 卫龙是…

线程池ThreadPoolExecutor

线程池的生命周期 private final AtomicInteger ctl new AtomicInteger(ctlOf(RUNNING, 0)); ThreadPoolExecutor使用一个ctl变量代表两个信息&#xff0c;线程池的运行状态 (runState) 和 线程池内有效线程的数量 (workerCount)&#xff0c;高三位表示状态。 workerCount&am…

MySQL 数据库存储引擎

目录 一、存储引擎简介 二、MyISAM存储引擎 1、MylSAM介绍 2、MyISAM表支持3种不同的存储格式 3、MylSAM的特点 4、MyISAM使用的生产场景 三、InnoDB存储引擎 1、InnoDB介绍 2、InnoDB的特点 3、InnoDB适用生产场景 4、MyISAM和InnoDB的区别 四、查看和修改存储引擎…

CloudAlibaba - Nacos服务注册与配置中心

文章目录一.CloudAlibaba简介1. 介绍2. 依赖3. 主要组件4. 资料文档二.Nacos服务注册与发现1. 简介2. Nacos安装3. Nacos服务注册3.1 注册服务生产者3.2 服务消费者注册和负载4. Nacos服务中心对比三.Nacos配置中心1. 基础配置搭建2. Nacos中添加配置信息2.1 Nacos中的匹配规则…

单商户商城系统功能拆解40—分销应用—分销设置

单商户商城系统&#xff0c;也称为B2C自营电商模式单店商城系统。可以快速帮助个人、机构和企业搭建自己的私域交易线上商城。 单商户商城系统完美契合私域流量变现闭环交易使用。通常拥有丰富的营销玩法&#xff0c;例如拼团&#xff0c;秒杀&#xff0c;砍价&#xff0c;包邮…

前端js传入Long类型精度丢失解决办法

目录 问题背景 解决过程 1.对比数据库数据 2.查询资料 解决方法 问题背景 在进行业务开发的 时候发现更新数据库中的一条数据没有成功&#xff0c;查看SQL日志发现SQL正常执行无错误信息&#xff0c;但是受影响行数为0&#xff0c;但是数据是从前端传过来的 &#xff0c;一…

面试官:MySQL explain你会关注哪些字段

编号字段名称字段说明值重要性1idSELECT查询序号&#xff0c;即为sql语句执行的顺序。数字越大越优先执行1、2、3...★★2select_typeselect类型&#xff0c;用于区分是普通查询、子查询或联合查询simple&#xff1a;简单的查询&#xff0c;不含子查询和联合查询primary&#x…

玩机搞机---卸载内置软件 无root权限卸载不需要的软件 安全卸载

目前的机型出厂都会自带一些内置软件。个别的还无法删除。各种弹窗广告 等等占用系统资源。操作卡顿。而很多卸载软件有需要root权限才可以运行。那么有没有方法可以无需root就可以卸载内置软件呢。答案是有的。而且方法很多。今天的帖子就列举一下常用的操作步骤 操作前了解的…

自己搭建网站【搭建网站】

现在搭建网站也不只是企业公司商家的专利&#xff0c;很多个人用户都想拥有属于自己的一个网站。那么怎么自己搭建网站呢&#xff1f;下面给大家简单说说。 一、申请域名 域名是访问网站的地址&#xff0c;这是必须要有的&#xff0c;可以在域名服务商网站上申请。申请域名需要…

科普-鸟哥私房菜中的“od -t oCc /etc/issue”的意思

文章摘录展示 看到这里是困惑的&#xff0c;毕竟看这里没有写oCc 的格式 既然文章不能找到答案&#xff0c;那么&#xff0c;我们看详细的说明吧&#xff08;man od&#xff09; 如图&#xff0c;想必我们应该很清楚了 &#xff0c;o[size] 对应的参数oC &#xff0c;就可以很…

2022.11.27Find a way HDU - 2612

Pass a year learning in Hangzhou, yifenfei arrival hometown Ningbo at finally. Leave Ningbo one year, yifenfei have many people to meet. Especially a good friend Merceki. Yifenfei’s home is at the countryside, but Merceki’s home is in the center of city.…

MySql索引详解-各种索引的定义与区别和应用

MySql索引详解-各种索引的定义与区别和应用一、索引基础&#xff1a;增删改查1.新增索引的几种方式2.删除索引的几种方式3.修改索引的几种方式4.查询索引的几种方式二、索引的分类1.主键索引2.唯一索引3.普通索引4.复合索引5.全文索引三、总结什么是索引&#xff1f;索引的作用…

【DL with Pytorch】第 6 章 : 用循环神经网络分析数据序列

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…