Spring Security 是 Spring 家族中的安全型开发框架,主要解决三大方面问题:认证(你是谁)、授权(你能干什么)、常见攻击保护(CSRF、HTTP安全响应头设置、Http防火墙)。它常用于 Spring Boot 及 Spring Cloud 框架中。总体架构核心非常简单,在国内大部分使用其Servlet应用的功能,其就在Filter一点上扩展,但学习来优点复杂,因为安全考虑的方面太多、应用场景也多,所以集成的功能也挺多,不过它提供了一个综合的解决方案,后面的 Spring Authorization Server 也是在 Security 基础上构建的,所以也是必学内容。
下面通过一个例子来简单测试下:
首先在 idea 中新建 Spring Initializr 应用,环境如下:
JDK 17
Spring Boot 3.2.2
Maven 3.8.8
加入 Spring Security 包,完整的 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.alex</groupId>
<artifactId>security-01-hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>security-01-hello</name>
<description>security-01-hello</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</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>
package com.alex.security01hello;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("hello")
private String hello() {
return "Hello, Spring Security!";
}
}
然后运行启动程序,这里是:Security01HelloApplication main
此时通过浏览器访问: http://127.0.0.1:8080/hello,页面会被重定向到 http://127.0.0.1:8080/login,此时查看 idea 的 Console ,会有生成的密码:
在 login 页面,输入 user 及上面的密码,就可以访问到 hello api 输出的内容。
Spring Security 还挺复杂,当在运行上面程序的时候,其在背后执行了很多个步骤,可以参考官方文档:
这里根据官方文档,可以固定一个 用户名、密码及角色,避免再查看控制台的复杂密码:
@EnableWebSecurity
@Configuration
public class DefaultSecurityConfig {
@Bean
@ConditionalOnMissingBean(UserDetailsService.class)
InMemoryUserDetailsManager inMemoryUserDetailsManager() {
UserDetails user = org.springframework.security.core.userdetails.User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
@Bean
@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
public DefaultAuthenticationEventPublisher defaultAuthenticationEventPublisher() {
return new DefaultAuthenticationEventPublisher();
}
}
再次重启程序,如果被重定向到登录页,直接输入 user+password 即可登录。
下面通过配置日志,查看 Spring Security 在请求时做了啥,首先在 resources/application.properties 配置 Spring Security 日志:
application.properties:
logging.level.org.springframework.security=TRACE
然后配置 logback 日志,在 resources下添加 logback.xml:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="WARN">
<appender-ref ref="STDOUT" />
</root>
</configuration>
重启程序,在浏览器中输入:127.0.0.1:8080/hello,观察控制台会输出相关日志:
通过上面可以看出,在访问的时候,一个 Filter 链中包含了16个 Filter, 最终因为在 AuthorizationFilter 的 doFilter 方法中抛出 AccessDeniedException 异常,并且重定向到 login 页面:
我们访问的 login 页面是在 DefaultLoginPageGeneratingFilter 中生成的:
参考:自定义登录页