springsecurity自动配置解析:
SecurityFilterAutoConfiguration:
spring security自动配置的源码简单分析 - 程序晓猿 - 博客园(详细但是也挺难的)
1
| @EnableConfigurationProperties({SecurityProperties.class})注解,Spring Boot会自动将这些属性加载到应用程序的上下文中,使得我们可以通过@Autowired注解或其他方式方便地访问和使用这些属性。
|
1
| 表示SecurityFilterAutoConfiguration的自动配置必须要在
|
请求到达spring security的处理过程:
一个请求到达spring security后首先会被DelegatingFilterProxy这个过滤器拦截到,这是一个代理过滤器,它会使用内部的代理目标delegate 也就是从容器中获取名称是springSecurityFilterChain的过滤器(而这个过滤器是由由WebSecurity 构建的FilterChainProxy这个类型的过滤器来处理请求,)总结下请求的处理路径就是:
1.DelegatingFilterProxy ----->2.FilterChainProxy
详细看下FilterChainProxy这个过滤器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
| public class FilterChainProxy extends GenericFilterBean {
private List<SecurityFilterChain> filterChains; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { boolean clearContext = request.getAttribute(FILTER_APPLIED) == null; if (clearContext) { try { request.setAttribute(FILTER_APPLIED, Boolean.TRUE); doFilterInternal(request, response, chain); } finally { SecurityContextHolder.clearContext(); request.removeAttribute(FILTER_APPLIED); } } else { doFilterInternal(request, response, chain); } } private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
FirewalledRequest fwRequest = firewall .getFirewalledRequest((HttpServletRequest) request); HttpServletResponse fwResponse = firewall .getFirewalledResponse((HttpServletResponse) response);
List<Filter> filters = getFilters(fwRequest);
if (filters == null || filters.size() == 0) { if (logger.isDebugEnabled()) { logger.debug(UrlUtils.buildRequestUrl(fwRequest) + (filters == null ? " has no matching filters" : " has an empty filter list")); }
fwRequest.reset();
chain.doFilter(fwRequest, fwResponse);
return; } VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters); vfc.doFilter(fwRequest, fwResponse); } private List<Filter> getFilters(HttpServletRequest request) { for (SecurityFilterChain chain : filterChains) { if (chain.matches(request)) { return chain.getFilters(); } } return null; } private static class VirtualFilterChain implements FilterChain {
private final FilterChain originalChain; private final List<Filter> additionalFilters; private final FirewalledRequest firewalledRequest; private final int size; private int currentPosition = 0;
private VirtualFilterChain(FirewalledRequest firewalledRequest, FilterChain chain, List<Filter> additionalFilters) { this.originalChain = chain; this.additionalFilters = additionalFilters; this.size = additionalFilters.size(); this.firewalledRequest = firewalledRequest; }
@Override public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if (currentPosition == size) { if (logger.isDebugEnabled()) { logger.debug(UrlUtils.buildRequestUrl(firewalledRequest) + " reached end of additional filter chain; proceeding with original chain"); }
this.firewalledRequest.reset(); originalChain.doFilter(request, response); } else { currentPosition++; Filter nextFilter = additionalFilters.get(currentPosition - 1);
if (logger.isDebugEnabled()) { logger.debug(UrlUtils.buildRequestUrl(firewalledRequest) + " at position " + currentPosition + " of " + size + " in additional filter chain; firing Filter: '" + nextFilter.getClass().getSimpleName() + "'"); } nextFilter.doFilter(request, response, this); } } } }
|
所以真正处理请求的其实是FilterChainProxy中维护的 SecurityFilterChain ,再更新下上边说的请求处理路径
1.DelegatingFilterProxy ----->2.FilterChainProxy ---->3.SecurityFilterChain
总结:
先是在SecurityFilterAutoConfiguration中给容器中加入了一个DelegatingFilterProxyRegistrationBean ,这个bean最后会给应用中注册一个DelegatingFilterProxy,这个代理过滤器会从容器中找出名称为springSecurityFilterChain的过滤器来执行它;
而这个springSecurityFilterChain是在WebSecurityConfiguration(这个类可以靠SecurityAutoConfiguration导入到容器中的)
)中由springSecurityFilterChain这个方法加到spring容器中的,在这个方法中会调用构建器WebSecurity.build方法创建出一个FilterChainProxy过滤器放到spring容器中;
当执行到WebSecurity.performBuild方法,会先调用SecurityFilterChain的构建器HttpSecurity的build方法构建出DefaultSecurityFilterChain对象,该对象内部会持有多个过滤器,然后把该对象作为参数创建出FilterChainProxy 对象返回。
可以看到这个过程中WebSecurity 是一个很关键的角色,它创建了DefaultSecurityFilterChain和FilterChainProxy
- [ ] SecurityFilterAutoConfiguration->DelegatingFilterProxyRegistrationBean->DelegatingFilterProxy->FilterChainProxy->DefaultSecurityFilterChain;这里其中的FilterChainProxy->DefaultSecurityFilterChain是在websecurity中创建的。
webSecurity这个构建器用来创建Filter,,其中它具体构造过滤链还是落在WebSecurityConfigurerAdapter上的configure方法。
WebSecurity是一个接口,它定义了Spring Security框架提供的安全功能的抽象接口。WebSecurityConfigurer是一个抽象类,用于配置Web应用程序的安全性。WebSecurityConfigurerAdapter是WebSecurityConfigurer的子类,它通过实现WebSecurityConfigurer中的抽象方法来配置Web应用程序的安全性。它还提供了默认实现来简化配置过程。因此,WebSecurityConfigurerAdapter是WebSecurityConfigurer的适配器类,用于方便地配置Web应用程序的安全性。
个人总结:我们一般配置安全信息配置类一般是要重写WebSecurityConfigurerAdapter,为什么要重写这个?因为spring从SecurityFilterAutoConfiguration自动加载过滤链的时候需要提取加载WebSecurityAutoConfiguration到容器中,而WebSecurityAutoConfiguration会加载WebSecurityConfiguration到容器中,其中真正过滤器链的创建还是要落在WebSecurityConfigurerAdapter上的configure方法上,当我们没有配置安全信息类时就启动时就加载WebSecurityConfigurerAdapter的其他默认配置类到容器中,比如:DefaultConfigurerAdapter。
🍃【Spring专题】「技术原理」Spring Security的核心功能和加载运行流程的原理分析 - 掘金(这个简单些)
Spring Security——认证、授权的工作原理_spring security 授权原理-CSDN博客

个人简单总结:就是安全的过滤链配置是通过重写WebSecurityConfigurerAdapter中的configure实现的,而这个过滤链接包括认证和授权:而下面的认证授权过滤链几乎都集中到WebSecurityConfigurerAdapter中了。
认证:

授权:

获取ip地址:
SpringSecurity配置过滤器的时候对.and的理解:
Spring Security 配置中的 and 到底该怎么理解?-腾讯云开发者社区-腾讯云
springsecurity有15个过滤器,and结束的地方是每个过滤器相关配置结束的地方。
其他:
1.我们往容器中注入了UserDetailsService后,我们配置文件中配置的
1 2 3 4 5
| spring: security: user: name: admin password: 888888
|
就会失效。这是因为在这种情况下,Spring Security将使用注入的UserDetailsService返回的UserDetails对象来进行身份验证,而不再使用配置文件中的用户名和密码。
2.当我们希望系统想从指定的数据源获取
3.
1 2 3
| UserDetails是一个接口,表示用户的身份信息。它包含了用户的基本信息,如用户名、密码、角色等。 WebAuthenticationDetails是一个类,表示Web应用程序中的身份验证细节。它包含了用户进行身份验证时的相关信息,如用户的IP地址、浏览器的 Referer 等 UserDetails是UserDetailsService获取的。
|
4.When should I override the configure(AuthenticationManagerBuilder auth) from Spring Security in a Spring Boot app?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 注入 UserDetailsService 实例和重写 configure(AuthenticationManagerBuilder auth) 方法之间的 区别在于配置的灵活性和控制权。
注入 UserDetailsService 实例:
通过将 UserDetailsService 实例注入到容器中,Spring Security 将自动使用该实例进行身份验证。 这种方式适用于简单的身份验证需求,例如基于数据库的验证,只需要提供用户详细信息的查询逻辑。 你无法直接在代码中对身份验证流程进行更详细的配置,因为验证过程是由 Spring Security 自动处理的。 重写 configure(AuthenticationManagerBuilder auth) 方法:
通过重写 configure(AuthenticationManagerBuilder auth) 方法,你可以更精确地配置身份验证过程。 你可以定义多个身份验证提供者,并为每个提供者指定不同的验证逻辑、密码编码器等。 可以使用多种身份验证方式,如数据库、LDAP、OAuth 等。 可以自定义用户详细信息的加载方式和逻辑。 可以添加其他的身份验证配置,如记住我功能、会话管理、多因素身份验证等。 你可以在代码中直接控制身份验证的细节,并根据需要进行自定义和扩展。
|
springsecurity中的 ThreadLocal如何理解:
1 2 3 4 5 6 7 8 9 10 11
| 在Spring Security中,ThreadLocal主要用于存储当前用户的信息。当用户进行认证并获得访问权限后, Spring Security会将用户信息存储到ThreadLocal中,以便在请求的处理过程中可以获取到当前用户的信息 。 具体来说,Spring Security提供了SecurityContextHolder类来管理用户信息的存储和获取。 SecurityContextHolder类中有一个getContext()方法返回一个SecurityContext对象,该对象中有一个 getAuthentication()方法返回一个Authentication对象,该对象中存储了用户的信息。 在每个请求的处理过程中,Spring Security会将Authentication对象存储到ThreadLocal中, 以便在需要访问用户信息的地方可以通过SecurityContextHolder.getContext().getAuthentication() 来获取到用户的信息。 需要注意的是,ThreadLocal的使用可以保证每个请求都有独立的用户信息 存储空间,避免了多个线程间共享用户信息导致的安全问题。但是,ThreadLocal的使用也会带来一定 的性能开销,因为每个线程都需要存储和获取自己的ThreadLocal对象。因此,在使用ThreadLocal 存储用户信息时,需要注意权衡安全和性能的关系。
|