简介
Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它是保护基于Spring的应用程序的事实标准。
Spring Security是一个专注于为Java应用程序提供身份验证和授权的框架。与所有Spring项目一样,Spring Security的真正威力在于它可以容易地扩展以满足定制需求。
- spring是非常流行和成功的Java应用开发框架,Spring Security正是Spring家族中的成员。Spring Security基于Spring框架,提供了一套Web应用安全性的完整解决方案。
- 正如你可能知道的关于安全方面的两个主要区域是"认证"和"授权",一般来说,Web应用的安全性包括认证和授权两个部分,这两点也是Spring Security重要的核心功能。
- 1 用户认证:验证某个用户是否为系统中的合法主体,也就是说用户能够访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。通俗的说就是系统认为用户能够登录。
- 2 用户授权:验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。通俗的说就是系统判断用户是否有权限去做某些事情
示例搭建
创建springboot模块,并创建一个简单的restful接口
package com.shaoch.security.springsecurityhelloworld.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author shaoch
* @date 2022/2/9 2:48 PM
*/
@RestController
public class HelloController {
@GetMapping("hello")
public String hello(){
return "hello";
}
}
pom文件,主要引入了web starter和security starter
<?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>2.6.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.shaoch.security</groupId>
<artifactId>spring-security-helloworld</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-security-helloworld</name>
<description>spring-security-helloworld</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这时只是引入了一个spring security的starter,通过浏览器访问restful接口,就能够看到spring security对接口进行了保护,它默认是对所有请求进行保护的
那默认的用户名和密码是什么呢,用户名可以使用 user 密码是在启动的控制台中,会多出一条这样的信息,这样登陆后就能够访问restful接口了
这时可能会产生几个疑问
1、为什么只引入了一个spring security的starter,所有的请求就需要认证了呢?
2、在项目中没有设置登录页面,是怎么来的?
3、用户名和密码是怎么通过认证的,此时还没有设置数据源?
原理
spring security官方的架构解释:Architecture :: Spring Securityhttps://docs.spring.io/spring-security/reference/servlet/architecture.htmlSpringSecurity提供了30多个过滤器,默认情况下,springboot在对springsecurity进行自动化配置时,会创建一个名为SpringSecurityFilterChain的过滤器,并注入到spring容器中,这个过滤器将负责所有的安全管理,包括用户认证、授权等等。具体可以参考WebSecurityConfiguration的源码。
Below is a comprehensive list of Spring Security Filter ordering:
• ChannelProcessingFilter
• WebAsyncManagerIntegrationFilter
• SecurityContextPersistenceFilter
• HeaderWriterFilter
• CorsFilter
• CsrfFilter
• LogoutFilter
• OAuth2AuthorizationRequestRedirectFilter
• Saml2WebSsoAuthenticationRequestFilter
• X509AuthenticationFilter
• AbstractPreAuthenticatedProcessingFilter
• CasAuthenticationFilter
• OAuth2LoginAuthenticationFilter
• Saml2WebSsoAuthenticationFilter
• UsernamePasswordAuthenticationFilter
• OpenIDAuthenticationFilter
• DefaultLoginPageGeneratingFilter
• DefaultLogoutPageGeneratingFilter
• ConcurrentSessionFilter
• DigestAuthenticationFilter
• BearerTokenAuthenticationFilter
• BasicAuthenticationFilter
• RequestCacheAwareFilter
• SecurityContextHolderAwareRequestFilter
• JaasApiIntegrationFilter
• RememberMeAuthenticationFilter
• AnonymousAuthenticationFilter
• OAuth2AuthorizationCodeGrantFilter
• SessionManagementFilter
• ExceptionTranslationFilter
• FilterSecurityInterceptor
• SwitchUserFilter
通过源码debug就可以看到默认加载的过滤器,在idea找到springSecurityFilterChain方法,并下载源码,在这个方法中加入断点,重启项目时会直接进入断点,可以看到默认加载的15个过滤器,并且顺序跟官网罗列的顺序一致。
spring security自动配置
通过官网可以看到,当在springboot中引入springsecurity的依赖时,会有一个自动配置,这个bean的名字是springSecurityFilterChain
自动配置的类的名字是SpringBootWebSecurityConfiguration
方法中代码的含义就是,对所有的请求开启认证,可以是通过表单和httpbasic认证。
自动配置的生效条件:
1 当web容器是servlet容器时会自动加载
2 注解@ConditionalOnDefaultWebSecurity中的两个条件
class DefaultWebSecurityCondition extends AllNestedConditions {
DefaultWebSecurityCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
//在类路径中有这两个类时
@ConditionalOnClass({ SecurityFilterChain.class, HttpSecurity.class })
static class Classes {
}
// 在spring容器中没有这两个bean时
@ConditionalOnMissingBean({ WebSecurityConfigurerAdapter.class, SecurityFilterChain.class })
static class Beans {
}
这就是为什么只引入了spring security就会有请求认证验证的原因。
其中WebSecurityConfigurerAdapter是一个抽象类,在之后对springsecurity配置的扩展中需要继承这个类,如果对过滤器进行扩展需要继承SecurityFilterChain来进行扩展。
为什么会自动跳转到登录页面呢,主要是通过DefaultLoginPageGeneratingFilter实现的,流程如下
第三个问题,用户名和密码是怎么通过认证的,数据源验证是在哪里实现的?
1 查看SpringBootWebSecurityConfiguration#defaultSecurityFilterChain方法表单登录
2 处理登录为FormLoginConfigurer类中调用UsernamePasswordAuthenticationFilter这个类实例
3 查看类中UsernamePasswordAuthenticationFilter#attempAuthentication方法得知实际调用AuthenticationManager中authenticate方法
4 调用ProviderManager类中方法authenticate
5 调用了ProviderManager实现类中AbstractUserDetailsAuthenticationProvider类中方法
6 最终调用实现类DaoAuthenticationProvider类中方法比较,到这里就可以知道实现是基于InMemoryUserDetailsManager这个类,也就是内存的实现
在SecurityProperties中可以看到@ConfigurationProperties注解,因此我们可以通过在yml中设置spring.security下的属性来自定义用户名和密码
自定义资源规则
一般在系统中访问资源会存在两种情况,一种是不需要权限能够直接访问的公共资源,另一种是需要认证和授权的受限资源,如下图所示
通过springsecurity如何进行设置呢?
springsecurity默认的配置是要验证所有的请求资源,我们需要去覆盖SpringBootWebSecurityConfiguration类中defaultSecurityFilterChain的默认设置,通过上面的总结可以知道,在什么情况下默认配置会生效
// 在spring容器中没有这两个bean时
@ConditionalOnMissingBean({ WebSecurityConfigurerAdapter.class, SecurityFilterChain.class })
static class Beans {
}
因此我们只需让此条件不满足既可以覆盖默认的资源验证规则。
package com.shaoch.security.springsecurityhelloworld.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* @author shaoch
* @date 2022/2/10 10:37 AM
*/
@Configuration
public class WebResourceSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// super.configure(http);
http.authorizeRequests()
.mvcMatchers("/login")
.permitAll() //放行login资源
.mvcMatchers("/index") //限制index资源
.authenticated().and().formLogin(); // 开启请求权限的管理
}
}