Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它是保护基于Spring的应用程序的事实标准。
Spring Security是一个专注于为Java应用程序提供身份验证和授权的框架。与所有Spring项目一样,Spring Security的真正威力在于它可以容易地扩展以满足定制需求。
创建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
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.6.3
com.shaoch.security
spring-security-helloworld
0.0.1-SNAPSHOT
spring-security-helloworld
spring-security-helloworld
1.8
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-security
org.springframework.boot
spring-boot-maven-plugin
这时只是引入了一个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个过滤器,并且顺序跟官网罗列的顺序一致。
通过官网可以看到,当在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(); // 开启请求权限的管理
}
}