spring security + Oauth2.0
一.spring security
问:
- spring security是如何注入spring容器?
- filter是如何加入tomcat?
- spring security是如何起作用?
1.自动注入
springboot在启动时会扫描所有jar包下的spring.factories,并且利用工具类转换成Map<String,List>对象,其中org.springframework.boot.autoconfigure.EnableAutoConfiguration就是自动注入的关键。
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityRequestMatcherProviderAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration
首先我们来看看这个类SecurityAutoConfiguration.java
@Configuration
//条件,如果有这个类则注入
@ConditionalOnClass({
DefaultAuthenticationEventPublisher.class})
//自动注入参数
@EnableConfigurationProperties({
SecurityProperties.class})
//导入三个配置类
@Import({
SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class, SecurityDataConfiguration.class})
public class SecurityAutoConfiguration {
public SecurityAutoConfiguration() {
}
@Bean
//容器中不存在这个类
@ConditionalOnMissingBean({
AuthenticationEventPublisher.class})
public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
return new DefaultAuthenticationEventPublisher(publisher);
}
}
然后我们再看看三个配置类干了些什么
/**
*SpringBootWebSecurityConfiguration
*/
@Configuration
//存在这个class
@ConditionalOnClass({
WebSecurityConfigurerAdapter.class})
//容器中没有这个类及其子类
@ConditionalOnMissingBean({
WebSecurityConfigurerAdapter.class})
//条件是web环境
@ConditionalOnWebApplication(
type = Type.SERVLET
)
public class SpringBootWebSecurityConfiguration {
public SpringBootWebSecurityConfiguration() {
}
@Configuration
@Order(2147483642)
//如果以上条件成立生成默认WebSecurityConfigurerAdapter,这也是我们不做任何配置security能生效的原因
static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {
DefaultConfigurerAdapter() {
}
}
}
----------------------------------------------------------------------------------------
/**
*WebSecurityEnablerConfiguration
*/
@Configuration
@ConditionalOnBean({
WebSecurityConfigurerAdapter.class})
@ConditionalOnMissingBean(
name = {
"springSecurityFilterChain"}
)
@ConditionalOnWebApplication(
type = Type.SERVLET
)
//1.引入三个类(WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class)
//2.@EnableGlobalAuthentication注解(引入AuthenticationConfiguration.class)
@EnableWebSecurity
public class WebSecurityEnablerConfiguration {
public WebSecurityEnablerConfiguration() {
}
}
引入的类的作用:
一.==WebSecurityConfiguration.class==
1.注入springSecurityFilterChain,并把WebSecurityConfigurerAdapter添加到webSecurity
2.注入SecurityExpressionHandler
3.注入DelegatingApplicationListener
二.==SpringWebMvcImportSelector.class==
如果有dispatcherservlet.class返回WebMvcSecurityConfiguration全类名
三.==OAuth2ImportSelector.class==
如果存在OAuth2ClientConfiguration.class返回OAuth2ClientConfiguration全类名
四.注解@EnableGlobalAuthentication
导入AuthenticationConfiguration.class
1.导入ObjectPostProcessorConfiguration.class
注入ObjectPostProcessor<Object>
2.注入AuthenticationManagerBuilder
3.注入GlobalAuthenticationConfigurerAdapter
---------------------------------------------------------------------------------------
/**
*SecurityDataConfiguration
*/
@Configuration
@ConditionalOnClass({
SecurityEvaluationContextExtension.class})
public class SecurityDataConfiguration {
public SecurityDataConfiguration() {
}
@Bean
@ConditionalOnMissingBean
//安全表达式扩展
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
return new SecurityEvaluationContextExtension();
}
}
接下来看看SecurityRequestMatcherProviderAutoConfiguration
@Configuration
@ConditionalOnClass({
RequestMatcher.class})
@ConditionalOnWebApplication(
type = Type.SERVLET
)
public class SecurityRequestMatcherProviderAutoConfiguration {
public SecurityRequestMatcherProviderAutoConfiguration() {
}
@Configuration
@ConditionalOnClass({
DispatcherServlet.class})
@ConditionalOnBean({
HandlerMappingIntrospector.class})
public static class MvcRequestMatcherConfiguration {
public MvcRequestMatcherConfiguration() {
}
@Bean
@ConditionalOnClass({
DispatcherServlet.class})
//注入MvcRequestMatcherProvider
public RequestMatcherProvider requestMatcherProvider(HandlerMappingIntrospector introspector) {
return new MvcRequestMatcherProvider(introspector);
}
}
}
下面到了SecurityFilterAutoConfiguration
@AutoConfigureAfter
public class SecurityFilterAutoConfiguration {
private static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain";
@Bean
@ConditionalOnBean(
name = {
"springSecurityFilterChain"}
)
//这个类实现了RegistrationBean,会在初始化时调用钩子函数onStartup(ServletContext servletContext),这个方法会最终调用父类AbstractFilterRegistrationBean的addRegistration(String description, ServletContext servletContext) 将DelegatingFilterProxy(filter的实现类)添加到ServletContext
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(SecurityProperties securityProperties) {
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean("springSecurityFilterChain", new ServletRegistrationBean[0]);
registration.setOrder(securityProperties.getFilter().getOrder());
registration.setDispatcherTypes(this.getDispatcherTypes(securityProperties));
return registration;
}
private EnumSet<DispatcherType> getDispatcherTypes(SecurityProperties securityProperties) {
return securityProperties.getFilter().getDispatcherTypes() == null ? null : (EnumSet)securityProperties.getFilter().getDispatcherTypes().stream().map((type) -> {
return DispatcherType.valueOf(type.name());
}).collect(Collectors.collectingAndThen(Collectors.toSet(), EnumSet::copyOf));
}
}
tomcat启动时,TomcatStarter会遍历ServletContextInitializer[] initializers;调用所有实现ServletContextInitializer的onStartup(),而此刻会将DelegatingFilterProxy加入到ServletContext的实现类ApplicationContext的StandardContext(TomcatEmbeddedContext(为StandardContext实现类))中
protected Dynamic addRegistration(String description, ServletContext servletContext) {
Filter filter = this.getFilter();
return servletContext.addFilter(this.getOrDeduceName(filter), filter);
}
此时,初始化已经完毕,springSecurityFilterChain已经加入到tomcat。
tomcat结构图
<Server>顶层类元素:一个配置文件中只能有一个<Server>元素,可包含多个Service。
<Service>顶层类元素:本身不是容器,可包含一个Engine,多个Connector。
<Connector/>连接器类元素:代表通信接口。
<Engine>容器类元素:为特定的Service组件处理所有客户请求,可包含多个Host。engine为顶层Container
<Host>容器类元素:为特定的虚拟主机处理所有客户请求,可包含多个Context。
<Context>容器类元素:为特定的Web应用处理所有客户请求。代表一个应用,包含多个Wrapper(封装了servlet)
</Context>
</Host>
</Engine>
</Service>
</Server>
2.当请求到达tomcat
首先请求会到达Acceptor类,Acceptor 继承了 AbstractEndpoint.Acceptor ,间接实现了Runnable接口,tomcat在运行时将Acceptor运行在一个后台线程内,单独监听socket请求,
protected class Acceptor extends AbstractEndpoint.Acceptor {
@Override
public void run() {
// Loop until we receive a shutdown command
while (running) {
//if we have reached max connections, wait
countUpOrAwaitConnection();
Socket socket = null;
//阻塞住 监听新的socket请求
socket = serverSocketFactory.acceptSocket(serverSocket);
// Configure the socket
if (running && !paused && setSocketOptions(socket)) {
// Hand this socket off to an appropriate processor 首先使用processSocket()简单处理socket
if (!processSocket(socket)) {
countDownConnection();
// Close socket right away
closeSocket(socket);
}
} else {
countDownConnection();
// Close socket right away
closeSocket(socket);
}
}
}
}
//接着进入processSocket(Socket socket)方法,processSocket方法主要工作为将socket请求信息进行封装,然后将一个实现了Runnable接口的并包含socket信息的SocketProcessor对象交给线程池,进行执行,然后Acceptor线程从该方法返回,重新监听端口上的socket请求。
protected boolean processSocket(Socket socket) {
// Process the request from this socket
SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket);
wrapper.setKeepAliveLeft(getMaxKeepAliveRequests