SpringSecurity在SpringBoot中的自动装配

从SpringBoot的自动装配原理入手

  1. 找到META-INFO下的spring.factories文件

SpringSecurity作为Spring的亲儿子,自然在spring-boot-autoconfigure下的spring.factories文件中配置了

org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\

UserDetailsServiceAutoConfiguration

@Configuration(proxyBeanMethods = false
)
//条件注解
@ConditionalOnClass({AuthenticationManager.class})
@ConditionalOnBean({ObjectPostProcessor.class})
//提供拓展,没有提供以下三个实现类才使用默认的
@ConditionalOnMissingBean(value = {AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class},type = {"org.springframework.security.oauth2.jwt.JwtDecoder", "org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector"}
)
public class UserDetailsServiceAutoConfiguration {//密码不加密表示private static final String NOOP_PASSWORD_PREFIX = "{noop}";private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern.compile("^\\{.+}.*$");private static final Log logger = LogFactory.getLog(UserDetailsServiceAutoConfiguration.class);public UserDetailsServiceAutoConfiguration() {}@Bean@ConditionalOnMissingBean(type = {"org.springframework.security.oauth2.client.registration.ClientRegistrationRepository"})@Lazypublic InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties, ObjectProvider<PasswordEncoder> passwordEncoder) {//读取以spring.security开头的配置文件SecurityProperties.User user = properties.getUser();List<String> roles = user.getRoles();return new InMemoryUserDetailsManager(new UserDetails[]{User.withUsername(user.getName()).password(this.getOrDeducePassword(user, (PasswordEncoder)passwordEncoder.getIfAvailable())).roles(StringUtils.toStringArray(roles)).build()});}private String getOrDeducePassword(SecurityProperties.User user, PasswordEncoder encoder) {String password = user.getPassword();if (user.isPasswordGenerated()) {logger.info(String.format("%n%nUsing generated security password: %s%n", user.getPassword()));}return encoder == null && !PASSWORD_ALGORITHM_PATTERN.matcher(password).matches() ? "{noop}" + password : password;}
}
//标记读取配置文件夹信息
@ConfigurationProperties(prefix = "spring.security"
)
public class SecurityProperties {public static final int BASIC_AUTH_ORDER = 2147483642;public static final int IGNORED_ORDER = Integer.MIN_VALUE;public static final int DEFAULT_FILTER_ORDER = -100;private final Filter filter = new Filter();private User user = new User();public SecurityProperties() {}public User getUser() {return this.user;}public Filter getFilter() {return this.filter;}public static class User {//如果没有配置,用户名默认user,密码uuidprivate String name = "user";private String password = UUID.randomUUID().toString();private List<String> roles = new ArrayList();private boolean passwordGenerated = true;public User() {}public String getName() {return this.name;}public void setName(String name) {this.name = name;}public String getPassword() {return this.password;}public void setPassword(String password) {if (StringUtils.hasLength(password)) {this.passwordGenerated = false;this.password = password;}}public List<String> getRoles() {return this.roles;}public void setRoles(List<String> roles) {this.roles = new ArrayList(roles);}public boolean isPasswordGenerated() {return this.passwordGenerated;}}public static class Filter {private int order = -100;private Set<DispatcherType> dispatcherTypes;public Filter() {this.dispatcherTypes = new HashSet(Arrays.asList(DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.REQUEST));}public int getOrder() {return this.order;}public void setOrder(int order) {this.order = order;}public Set<DispatcherType> getDispatcherTypes() {return this.dispatcherTypes;}public void setDispatcherTypes(Set<DispatcherType> dispatcherTypes) {this.dispatcherTypes = dispatcherTypes;}}
}

SecurityFilterAutoConfiguration

约定大于配置,这里的内容就相当于在web.xml配置文件中配置springSecurityFilterChain的过程由spring自动实现.spring自动注入DelegatingFilterProxy对象,这样就可以将security中的过滤器切到spring中,在请求的时候会被DelegatingFilterProxyRegistrationBean拦截,然后去执行security中的过滤器链。


@Configuration(proxyBeanMethods = false
)
//web项目才加载
@ConditionalOnWebApplication(type = Type.SERVLET
)
@EnableConfigurationProperties({SecurityProperties.class})
@ConditionalOnClass({AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class})
//SecurityAutoConfiguration之后执行
@AutoConfigureAfter({SecurityAutoConfiguration.class})
public class SecurityFilterAutoConfiguration {//名字private static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain";public SecurityFilterAutoConfiguration() {}/*** 创建DelegatingFilterProxyRegistrationBean对象注入到spring容器中* @param securityProperties* @return*/@Bean@ConditionalOnBean(name = {"springSecurityFilterChain"})public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(SecurityProperties securityProperties) {//创建DelegatingFilterProxy对象DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean("springSecurityFilterChain", new ServletRegistrationBean[0]);registration.setOrder(securityProperties.getFilter().getOrder());registration.setDispatcherTypes(this.getDispatcherTypes(securityProperties));return registration;}private EnumSet<DispatcherType> getDispatcherTypes(SecurityProperties securityProperties) {return securityProperties.getFilter().getDispatcherTypes() == null ? null : (EnumSet)securityProperties.getFilter().getDispatcherTypes().stream().map((type) -> {return DispatcherType.valueOf(type.name());}).collect(Collectors.toCollection(() -> {return EnumSet.noneOf(DispatcherType.class);}));}
}

创建DelegatingFilterProxy的过程实际是通过ServletContextInitializer接口实现的,有一个方法onStartup,有一个实现类RegistrationBean

public abstract class RegistrationBean implements ServletContextInitializer, Ordered {private static final Log logger = LogFactory.getLog(RegistrationBean.class);private int order = Integer.MAX_VALUE;private boolean enabled = true;public RegistrationBean() {}public final void onStartup(ServletContext servletContext) throws ServletException {String description = this.getDescription();if (!this.isEnabled()) {logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");} else {//注册this.register(description, servletContext);}}protected abstract String getDescription();protected abstract void register(String description, ServletContext servletContext);public void setEnabled(boolean enabled) {this.enabled = enabled;}public boolean isEnabled() {return this.enabled;}public void setOrder(int order) {this.order = order;}public int getOrder() {return this.order;}
}

注册逻辑在父类DynamicRegistrationBean中

public abstract class DynamicRegistrationBean<D extends Registration.Dynamic> extends RegistrationBean {private static final Log logger = LogFactory.getLog(RegistrationBean.class);private String name;private boolean asyncSupported = true;private Map<String, String> initParameters = new LinkedHashMap();public DynamicRegistrationBean() {}public void setName(String name) {Assert.hasLength(name, "Name must not be empty");this.name = name;}public void setAsyncSupported(boolean asyncSupported) {this.asyncSupported = asyncSupported;}public boolean isAsyncSupported() {return this.asyncSupported;}public void setInitParameters(Map<String, String> initParameters) {Assert.notNull(initParameters, "InitParameters must not be null");this.initParameters = new LinkedHashMap(initParameters);}public Map<String, String> getInitParameters() {return this.initParameters;}public void addInitParameter(String name, String value) {Assert.notNull(name, "Name must not be null");this.initParameters.put(name, value);}protected final void register(String description, ServletContext servletContext) {//生成DelegatingFilterProxy对象D registration = this.addRegistration(description, servletContext);if (registration == null) {logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");} else {//在其父类AbstractFilterRegistrationBean中配置拦截/*请求this.configure(registration);}}protected abstract D addRegistration(String description, ServletContext servletContext);protected void configure(D registration) {registration.setAsyncSupported(this.asyncSupported);if (!this.initParameters.isEmpty()) {registration.setInitParameters(this.initParameters);}}protected final String getOrDeduceName(Object value) {return this.name != null ? this.name : Conventions.getVariableName(value);}
}

跟踪代码发现实际调用的了DelegatingFilterProxyRegistrationBean的getFilter方法

public class DelegatingFilterProxyRegistrationBean extends AbstractFilterRegistrationBean<DelegatingFilterProxy> implements ApplicationContextAware {private ApplicationContext applicationContext;private final String targetBeanName;public DelegatingFilterProxyRegistrationBean(String targetBeanName, ServletRegistrationBean<?>... servletRegistrationBeans) {super(servletRegistrationBeans);Assert.hasLength(targetBeanName, "TargetBeanName must not be null or empty");this.targetBeanName = targetBeanName;this.setName(targetBeanName);}public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}protected String getTargetBeanName() {return this.targetBeanName;}//创建DelegatingFilterProxy实例对象public DelegatingFilterProxy getFilter() {return new DelegatingFilterProxy(this.targetBeanName, this.getWebApplicationContext()) {protected void initFilterBean() throws ServletException {}};}private WebApplicationContext getWebApplicationContext() {Assert.notNull(this.applicationContext, "ApplicationContext be injected");Assert.isInstanceOf(WebApplicationContext.class, this.applicationContext);return (WebApplicationContext)this.applicationContext;}
}

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

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

相关文章

C语言指针函数指针

跟着这篇文章重新理解了一下&#xff1a;彻底攻克C语言指针 有一个例子感觉可以拿出来看看&#xff1a; char *(*c[10])(int **p); * 这段声明定义了一个长度为10的数组c&#xff0c;数组中的每个元素都是指向函数的指针。每个函数接受一个类型为int **&#xff08;指向指向…

拆分Transformer注意力,韩国团队让大模型解码提速20倍|大模型AI应用开始小规模稳步爆发|周伯文:大模型也有幻觉,全球AI创新指数公布

拆分Transformer注意力&#xff0c;韩国团队让大模型解码提速20倍AI正在颠覆AI上市不到两年&#xff0c;蜗牛游戏可能要退市了&#xff1f;世界人工智能大会结束了&#xff0c;百花齐放&#xff0c;但也群魔乱舞“串联OLED”被苹果带火了&#xff0c;比OLED强在哪里&#xff1f…

API-案例-放大镜效果

学习目标&#xff1a; 掌握案例-放大镜效果 学习内容&#xff1a; 业务分析思路分析放大镜完整代码 业务分析&#xff1a; 鼠标经过对应小盒子&#xff0c;左侧中等盒子显示对应中等图片&#xff1b;鼠标经过中盒子&#xff0c;右侧会显示放大镜效果的大盒子&#xff1b;黑色…

智能物联网鱼缸

硬件部分及接线图 工具 继电器、开发板、物联网os、云平台 微信小程序 结构&#xff1a;images、pages两个为主体。 标题头部分 <view class"container"> <view class"head_box"> <image src"/images/面性鱼缸.png"><…

【C++】 解决 C++ 语言报错:Invalid Use of Incomplete Type

文章目录 引言 在 C 编程中&#xff0c;“Invalid Use of Incomplete Type” 是一种常见错误。此错误通常在程序试图使用未完全定义的类或结构时发生。这种错误不仅会导致编译失败&#xff0c;还可能导致程序行为不可预测。本文将详细探讨无效使用不完整类型的成因、检测方法及…

【图论算法题记录】岛屿问题汇总

岛屿数量 题目&#x1f517; 题目描述 给定一个由 1&#xff08;陆地&#xff09;和 0&#xff08;水&#xff09;组成的矩阵&#xff0c;你需要计算岛屿的数量。岛屿由水平方向或垂直方向上相邻的陆地连接而成&#xff0c;并且四周都是水域。你可以假设矩阵外均被水包围。 …

【已解决】CentOS8安装lrzsz报错:Error: Failed to download metadata for repo ‘BaseOS‘

这里写自定义目录标题 系统信息安装lrzsz解决方案开始使用 系统信息 #cat /etc/redhat-release CentOS Linux release 8.1.1911 (Core) 安装lrzsz # yum install lrzsz CentOS-8 - AppStream …

【Threejs进阶教程-优化篇】4.Vue/React与threejs如何解决冲突和卡顿(续)

Vue/React与threejs如何解决冲突和卡顿-续 使用说明核心思路环境搭建(vuethree)vue运行机制分析业务分离使用threejs做背景 3D模块封装使用ES6的Class来让逻辑性更强Threejs尽量按需引入创建一个类扩展写法本次代码执行顺序 扩展内容添加orbitControls和辅助线解决事件覆盖 与V…

C++模板元编程(三)——类型萃取

类型萃取(type_traits)是一种编译时技术&#xff0c;用于在编译期间获取和操作类型的信息&#xff0c;主要用于泛型编程以及在编译时做出决策。 文章目录 常见的类型萃取内部实现std::is_integral\<T\>std::enable_if_t<_Test, T> 应用 常见的类型萃取 在C11的<…

Java请求webService,IDEA生成客户端调用代码

Axis是Apache开放源代码组织的一个项目&#xff0c;全称为Apache Extensible Interaction System&#xff0c;简称Axis。它是一个基于Java的SOAP&#xff08;Simple Object Access Protocol&#xff0c;简单对象访问协议&#xff09;引擎&#xff0c;提供创建服务器端、客户端和…

游戏开发面试题3

unity如何判断子弹射击到敌人&#xff0c;如果子弹特别快怎么办 使用物理学碰撞检测。使用Unity的物理组件&#xff0c;如Rigidbody和Collider&#xff0c;将子弹和敌人都设置为有一定的物理碰撞属性&#xff0c;当子弹碰到敌人的时候&#xff0c;就会触发OnCollisionEnter()事…

游戏开发面试题2

详细说下堆排序。 堆排序是一种选择排序算法&#xff0c;它的基本思想是&#xff1a;将待排序序列构造成一个大顶堆&#xff0c;此时&#xff0c;整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换&#xff0c;此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个…

作业训练二编程题6. 小A的计算器

【问题描述】 以往的操作系统内部的数据表示都是二进制方式&#xff0c;小A新写了一个操作系统&#xff0c;系统内部的数据表示为26进制&#xff0c;其中0-25分别由a-z表示。 现在小A要在这个操作系统上实现一个计算器&#xff0c;这个计算器要能实现26进制数的加法运算…

Ajax与Fetch API在Web开发中的性能、用法与未来趋势比较

Ajax和Fetch都是JavaScript中用于从客户端向服务器发送请求以获取数据的技术&#xff0c;但它们之间存在一些显著的区别。以下是对这两种技术的详细比较&#xff1a; 一、技术基础与实现方式 Ajax&#xff1a; 基础&#xff1a;Ajax全称为Asynchronous JavaScript and XML&…

LabVIEW的Actor Framework (AF) 结构介绍

LabVIEW的Actor Framework (AF) 是一种高级架构&#xff0c;用于开发并发、可扩展和模块化的应用程序。通过面向对象编程&#xff08;OOP&#xff09;和消息传递机制&#xff0c;AF结构实现了高效的任务管理和数据处理。其主要特点包括并发执行、动态可扩展性和强大的错误处理能…

ROS——多个海龟追踪一个海龟实验

目标 通过键盘控制一个海龟&#xff08;领航龟&#xff09;的移动&#xff0c;其余生成的海龟通过监听实现追踪定期获取领航龟和其余龟的坐标信息&#xff0c;通过广播告知其余龟&#xff0c;进行相应移动其余龟负责监听 疑惑点&#xff08;已解决&#xff09; int main(int…

k8s 部署RuoYi-Vue-Plus之redis搭建

1.直接部署一个pod 需要挂载存储款, 可参考 之前文章设置 https://blog.csdn.net/weimeibuqieryu/article/details/140183843 2.部署yaml 先创建命名空间ruoyi, 有就不用创建了 kubectl create namespace ruoyi创建部署文件 redis-deploy.yaml kind: PersistentVolume api…

程序员学长 | 快速学会一个算法,xLSTM

本文来源公众号“程序员学长”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;快速学会一个算法&#xff0c;xLSTM 今天给大家分享一个超强的算法模型&#xff0c;xLSTM。 xLSTM&#xff08;Extended Long Short-Term Memory&…

Spring Boot的无缝衔接:深入解析与实践

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ &#x1f680;The begin&#x1f697;点点关注&#xff0c;收藏不迷路&#x1f6a9; 引言 在快速迭代的软件开发环境中&#xff0c;无缝衔接是提升开发效率、降低维护成本、增强系统稳定性的关键。Spring Boo…

Electron开发 - 如何在主进程Main中让node-fetch使用系统代理

背景 开发过程中&#xff0c;用户设置的系统代理是不同的&#xff0c;比如公司内的服务器&#xff0c;所以就要动态地使用系统代理来访问&#xff0c;但是主进程默认为控制台级别的请求&#xff0c;不走系统代理&#xff0c;除非你指定系统代理配置&#xff0c;这个就就有了这…