赞
踩
[[toc]]
版本配置:
- SpringBoot : 3.2.2
- SpringSecurity : 6.2.0
- JDK : 17
- MySQL : 8.0.35
SpringSecurity官方文档
https://spring.io/projects/spring-security
SpringSecurity官方示例
https://github.com/spring-projects/spring-sec
urity-samples
Spring Security 是一个提供身份验证
、授权
和针对常见攻击的保护
的框架。 凭借对保护命令式和反应式应用程序的一流支持,它成为保护基于 Spring 的应用程序的事实上的标准。
身份认证:是指确认用户身份是否有效的过程,确保用户是其声称的那个人,并且具有访问系统资源的权限。
Spring Security 提供了各种身份认证的方式和机制,包括但不限于:
授权:用户身份认证完成后,SpringSecurity能够控制用户所能够访问的资源
防御常见攻击:
项目结构:
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>SpringSecurityLearn</artifactId> <version>0.0.1-SNAPSHOT</version> <name>SpringSecurityLearn</name> <description>SpringSecurityLearn</description> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
IndexController
package com.example.springsecuritylearn.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class IndexController {
@GetMapping("/")
public String index() {
return "index.html";
}
}
启动项目后会跳转到如下页面:
Username:user
Password:在控制台自动生成(很明显这是一个UUID格式)
正确输入两项之后便能够得到如下结果:
登录页面渲染异常或者无法页面卡死无法进入等页面,则是由于SpringSecurity自动为我们生成的登陆页面中的bootstra.min.css资源无法加载导致的,需要科学。
Spring Security 的 Servlet 支持是基于 Servlet Filter 的。下图显示了单个 HTTP 请求的处理程序的典型分层。
客户端向应用程序发送一个请求,容器创建一个 FilterChain
,其中包含 Filter
实例和 Servlet
,应该根据请求URI的路径来处理 HttpServletRequest
。在Spring MVC应用程序中,Servlet是 DispatcherServlet
的一个实例。一个 Servlet
最多可以处理一个 HttpServletRequest
和 HttpServletResponse
。然而,可以使用多个 Filter
来完成如下工作。
Filter
实例或 Servlet
被调用。在这种情况下,Filter
通常会使用 HttpServletResponse
对客户端写入响应。Filter
实例和 Servlet
所使用的 HttpServletRequest
或 HttpServletResponse
。前面的IndexController底层实际上正是Servlet
一般情况下都是采用SpringBoot来集成SpringSecurity,而SpringBoot底层还是Spring那一套,因此我们很自然的想到可以将过滤器实例作为Bean交给Spring上下文管理。那么在对Filter的使用上就会更加灵活。
Spring 提供了一个名为 DelegatingFilterProxy
的 Filter
实现,允许在 Servlet 容器的生命周期和 Spring 的 ApplicationContext
之间建立桥梁。Servlet容器允许通过使用自己的标准来注册 Filter
实例,但它不知道 Spring 定义的 Bean。你可以通过标准的Servlet容器机制来注册 DelegatingFilterProxy
,但将所有工作委托给实现 Filter
的Spring Bean。
下面是 DelegatingFilterProxy
如何融入 Filter实例和FilterChain
例如上图,DelegatingFilterProxy从ApplicationContext中查找到Bean Filter0,然后待用Bean Filter0。
也就是说,它允许延迟查找Filter Bean实例。在容器启动之前,容器需要注册Filter实例。然而,Spring通常使用ContextLoaderListener来加载Spring Bean,这在需要注册Filter实例之后才会完成。
DelegatingFilterProxy的伪码
public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain){
Filter delegate = getFilterBean(someBeanName);
delegate.doFilter(request,response);
}
DelegatingFilterProxy就是一个Filter可以被注册在Servlet的过滤器链中,然后注册在Spring容器中的Bean Filter就可以被DelegatinFilterProxy调用,从而工作在整个Servlet生命周期中。
这么做,对于Filter的启用等操作就会方便灵活很多。
Spring Security 的 Servlet 支持包含在 FilterChainProxy
中。FilterChainProxy
是 Spring Security 提供的一个特殊的 Filter
,允许通过 SecurityFilterChain
委托给许多 Filter
实例。由于 FilterChainProxy是一个Bean,它通常被包裹在 DelegatingFilterProxy中。
实际应用情况
SecurityFilterChain被FilterChainProxt用来确定当前请求应该调用哪些Spring Security Filter实例
SecurityFilterChain
中的 Security Filter
通常是Bean
,但它们是用 FilterChainProxy
而不是 DelegatingFilterProxy
注册的。与直接向Servlet容器或 DelegatingFilterProxy
注册相比,FilterChainProxy
有很多优势。首先,它为 Spring Security 的所有 Servlet 支持提供了一个起点。由于这个原因,如果你试图对 Spring Security 的 Servlet 支持进行故障诊断,在 FilterChainProxy
中添加一个调试点是一个很好的开始。
其次,由于 FilterChainProxy
是 Spring Security 使用的核心,它可以执行一些不被视为可有可无的任务。 例如,它清除了 SecurityContext
以避免内存泄漏。它还应用Spring Security的 HttpFirewall
来保护应用程序免受某些类型的攻击。
此外,它在确定何时应该调用 SecurityFilterChain
方面提供了更大的灵活性。在Servlet容器中,Filter
实例仅基于URL被调用。 然而,FilterChainProxy
可以通过使用 RequestMatcher
接口,根据 HttpServletRequest
中的任何内容确定调用。
下图为多SecurityFilterChain实例。
FilterChainProxt会根据请求决定使用哪个SecurityFilterChain,而且最多调用一个。如果请求的URL是/api/message/,那么按顺序来它首先与SpringSecurityn0相匹配,尽管他还和最后一个SecurityFilterChain匹配,但是FilterChainProxy仍然只调用第一次模式匹配的SecurityFilterChain即n0。
首先在IDEA中搜索DefaultSecurityFilterChain
/* * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.security.web; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import jakarta.servlet.Filter; import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.core.log.LogMessage; import org.springframework.security.web.util.matcher.RequestMatcher; /** * Standard implementation of {@code SecurityFilterChain}. * * @author Luke Taylor * @since 3.1 */ public final class DefaultSecurityFilterChain implements SecurityFilterChain { private static final Log logger = LogFactory.getLog(DefaultSecurityFilterChain.class); private final RequestMatcher requestMatcher; private final List<Filter> filters; public DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters) { this(requestMatcher, Arrays.asList(filters)); } public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters) { if (filters.isEmpty()) { logger.info(LogMessage.format("Will not secure %s", requestMatcher)); } else { logger.info(LogMessage.format("Will secure %s with %s", requestMatcher, filters)); } this.requestMatcher = requestMatcher; this.filters = new ArrayList<>(filters); } public RequestMatcher getRequestMatcher() { return this.requestMatcher; } @Override public List<Filter> getFilters() { return this.filters; } @Override public boolean matches(HttpServletRequest request) { return this.requestMatcher.matches(request); } @Override public String toString() { return this.getClass().getSimpleName() + " [RequestMatcher=" + this.requestMatcher + ", Filters=" + this.filters + "]"; } }
启动SpringBoot项目,发现在控制台打印了这样一条语句
后面的具体内容为:
Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@409a76d4, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@3e768587, org.springframework.security.web.context.SecurityContextHolderFilter@5c0bb8a, org.springframework.security.web.header.HeaderWriterFilter@82d2d7c, org.springframework.web.filter.CorsFilter@244d6082, org.springframework.security.web.csrf.CsrfFilter@7a39e757, org.springframework.security.web.authentication.logout.LogoutFilter@11a10c17, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@494c89b2, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@5c9f48fa, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@fae6ead, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@231bb139, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@9d27d01, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@7c212ae2, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@1c91955a, org.springframework.security.web.access.ExceptionTranslationFilter@1b82d3af, org.springframework.security.web.access.intercept.AuthorizationFilter@5b7bcedf]
这些内容的格式似乎有点眼熟?
构造方法的调用顺序是显而易见的,默认过滤器链中也能清楚的看到16个Filter。
默认过滤器当然不可能完全符合使用需求,因此有必要对其进行一定的修改。
org.springframework.security.web.access.ExceptionTranslationFilter@1b82d3af, org.springframework.security.web.access.intercept.AuthorizationFilter@5b7bcedf]
这些内容的格式似乎有点眼熟?
构造方法的调用顺序是显而易见的,默认过滤器链中也能清楚的看到16个Filter。
默认过滤器当然不可能完全符合使用需求,因此有必要对其进行一定的修改。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。