SpringSecurity

SpringSecurity

[[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

一. SpringSecurity简述

在这里插入图片描述

Spring Security 是一个提供身份验证授权针对常见攻击的保护的框架。 凭借对保护命令式和反应式应用程序的一流支持,它成为保护基于 Spring 的应用程序的事实上的标准。

  • 身份认证:是指确认用户身份是否有效的过程,确保用户是其声称的那个人,并且具有访问系统资源的权限。

    Spring Security 提供了各种身份认证的方式和机制,包括但不限于:

    1. 基于表单的认证: 用户通过输入用户名和密码来进行身份验证,通常在 Web 应用程序中使用。
    2. HTTP 基本认证和摘要认证: 基于 HTTP 协议的基本身份认证和摘要身份认证。
    3. OAuth 认证: 支持 OAuth 协议,允许用户通过第三方身份提供者进行身份验证。
    4. JWT 认证: 使用 JSON Web Token(JWT)进行身份验证和授权。
    5. LDAP 认证: 通过 Lightweight Directory Access Protocol(LDAP)进行身份验证,常用于企业环境中集中管理用户身份。
    6. CAS 认证: 使用 Central Authentication Service(CAS)进行单点登录和身份验证。
  • 授权:用户身份认证完成后,SpringSecurity能够控制用户所能够访问的资源

  • 防御常见攻击:

    • CSRF

二、第一个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资源无法加载导致的,需要科学。

三、SpringSecurity底层原理

3.1 Filter复习

Spring Security 的 Servlet 支持是基于 Servlet Filter 的。下图显示了单个 HTTP 请求的处理程序的典型分层。

img

客户端向应用程序发送一个请求,容器创建一个 FilterChain,其中包含 Filter 实例和 Servlet,应该根据请求URI的路径来处理 HttpServletRequest。在Spring MVC应用程序中,Servlet是 DispatcherServlet 的一个实例。一个 Servlet 最多可以处理一个 HttpServletRequestHttpServletResponse。然而,可以使用多个 Filter 来完成如下工作。

  • 防止下游的 Filter 实例或 Servlet 被调用。在这种情况下,Filter 通常会使用 HttpServletResponse 对客户端写入响应。
  • 修改下游的 Filter 实例和 Servlet 所使用的 HttpServletRequestHttpServletResponse

前面的IndexController底层实际上正是Servlet

一般情况下都是采用SpringBoot来集成SpringSecurity,而SpringBoot底层还是Spring那一套,因此我们很自然的想到可以将过滤器实例作为Bean交给Spring上下文管理。那么在对Filter的使用上就会更加灵活。

3.2 DelegatingFilterProxy

Spring 提供了一个名为 DelegatingFilterProxyFilter 实现,允许在 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的启用等操作就会方便灵活很多。

3.3 FilterChainProxy

Spring Security 的 Servlet 支持包含在 FilterChainProxy 中。FilterChainProxy 是 Spring Security 提供的一个特殊的 Filter,允许通过 SecurityFilterChain委托给许多 Filter 实例。由于 FilterChainProxy是一个Bean,它通常被包裹在 DelegatingFilterProxy中。

在这里插入图片描述

3.4 SecurityFilterChain

实际应用情况
在这里插入图片描述

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。

四、DefaultSecurityFilterChain

4.1 简要分析

首先在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。

4.2 修改默认过滤器

默认过滤器当然不可能完全符合使用需求,因此有必要对其进行一定的修改。
org.springframework.security.web.access.ExceptionTranslationFilter@1b82d3af, org.springframework.security.web.access.intercept.AuthorizationFilter@5b7bcedf]

这些内容的格式似乎有点眼熟?
在这里插入图片描述

构造方法的调用顺序是显而易见的,默认过滤器链中也能清楚的看到16个Filter。

4.2 修改默认过滤器

默认过滤器当然不可能完全符合使用需求,因此有必要对其进行一定的修改。

  • 31
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值