不论是自己写还是使用某些权限框架,他们的很多处理逻辑都是相似的,下图展示了对资源访问的常规处理逻辑
我们跟着下面的问题来学习如何使用Spring Security框架
- 用户访问资源时,某些资源不需要登录都可以访问,那么如何配置?
- 如何判断用户是否处于已登录状态?
- 没有登录时,可以有哪些处理方式?
- 如何从请求中解析出需要的登录信息?
- 如何获取外部存储的用户信息?
- 在哪里如何做登录认证?
- 如果登录失败了,都可以怎么进行处理?
- 如果登录成功了,都可以怎么进行处理?
- 有哪些途径可以对权限进行校验?
- 如果权限不足,都可以进行怎么样的处理?
在解答以上问题前,我们先了解下如何注册Filter和Servlet
1、Java web中注册Filter和Servlet
方法1:web.xml
在web.xml文件中配置Filter和Servlet可以将自定义的Filter和Servlet添加到调用链里面
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<!-- 注册Filter -->
<filter>
<filter-name>filter-name</filter-name>
<filter-class>com.demo.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>filter-name</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 注册Servlet-->
<servlet>
<servlet-name>servlet-name</servlet-name>
<servlet-class>com.demo.MyServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>servlet-name</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
方法2:注解注册
@WebFilter(
filterName = "my-fileter",
urlPatterns = "/*"
)
public class MyFilter implements Filter {
}
@WebServlet(
name = "my-servlet",
urlPatterns = "/*"
)
public class MyServlet extends HttpServlet {
}
方法2:ServletContext注册
servletContext.addServlet("servlet-name",servlet) ;
servletContext.addFilter("filter-name",filter) ;
2、Spring web中注册Filter
在Java web中我们有多种方式来注册Filter,但是我们在使用Spring框架的时候,如果还是通过这种方式来使用,似乎就不能很好的利用IOC的特性了,所以在spring web中设计了代理类:DelegatingFilterProxy
DelegatingFilterProxy自身就是一个Filter类型,在其内部通过从Spring 容器中获取Filter类型的Bean,并完成对其的委派实例的执行;
在使用的时候,值需要将DelegatingFilterProxy注册到Servlet容器中即可,例如:
<filter>
<filter-name>filterProxy</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>filterProxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
3、Springboot中注册Filter和Servlet
在讲具体的注册方法时,我们先看看Springboot是如何初始化Servlet容器的。
Servlet Contaier启动
- 从META-INF/spring.factories中加载所有BootstrapRegistryInitializer类型并保存
- 从META-INF/spring.factories中加载所有ApplicationContextInitializer类型并保存
- 从META-INF/spring.factories中加载所有ApplicationListener类型并保存
- 创建DefaultBootstrapContext实例,并作为参数调用所有保存的BootstrapRegistryInitializer实例
- 创建AnnotationConfigServletWebServerApplicationContext实例对象
- 使用applicationContext作为参数调用保存的每个ApplicationContextInitializer实例对象
- 从IOC中获取ServletWebServerFactory类似实例对象
- 从BeanFactory中查询ServletContextInitializer类型的实例,主要类型有
- ServletRegistrationBean类型实例
- FilterRegistrationBean类型实例
- DelegatingFilterProxyRegistrationBean类型实例
- ServletListenerRegistrationBean类型实例
- 其他ServletContextInitializer实例
- 使用ServletContextInitializer类型的集合对象作为参数,调用ServletWebServerFactory类型实例的getWebServer方法来创建web服务器,在这个过程中ServletContextInitializer实例将会被回调
- 自此Servlet容器的启动过程完成,接下来可以接收外部请求了
从上面的流程可见,在Tomcat启动的过程中,只要能够将ServletContainerInitializer类型的实例传递给Servlet容器,那么在容器的启动过程中会将ServletContext作为参数进行回调,这样在回调的时候,通过ServletContext的接口就可以动态的添加Servlet了。
这里的DispatcherServletRegistrationBean和DelegatingFilterProxyRegistrationBean需要注意下,这两个都是springboot提供的
- DispatcherServletRegistrationBean:该类型的实例对象在DispatcherServletAutoConfiguration配置类中被创建,其目的就是自动注册DispatcherServlet实例,从而完成SpringMVC框架的集成
- DelegatingFilterProxyRegistrationBean:该类型的实例对象在SecurityFilterAutoConfiguration配置类中被创建,并使用“springSecurityFilterChain”作为参数进行实例化,目的是集成Spring Security框架。 其内部从IOC中找出名为springSecurityFilterChain的Bean,并通过DelegatingFilterProxy进行封装返回(也就是最终添加到Servlet容器中的是DelegatingFilterProxy实例对象)。
方法1:使用@ServletComponentScan
创建Filter或Servlet等组件的实例,并添加@WebServlet、@WebFilter、@WebListener注解,然后在@Configuration配置类上添加@ServletComponentScan来扫描Servlet组件进行注册。
@Configuration
@ServletComponentScan(basePackages = {"com.demo"})
public class Configuration {
}
方法2:使用RegistrationBean子类进行注册
@Bean
public ServletRegistrationBean regServlet() {
ServletRegistrationBean myServlet= new ServletRegistrationBean();
myServlet.addUrlMappings("/servlet");
myServlet.setServlet(new MyServlet());
return myServlet;
}
@Bean
public FilterRegistrationBean regFilter() {
FilterRegistrationBean myFilter = new FilterRegistrationBean();
myFilter.addUrlPatterns("/*");
myFilter.setFilter(new MyFilter ());
return myFilter ;
}
@Bean
public ServletListenerRegistrationBean<MySessionListener> regServletListener() {
ServletListenerRegistrationBean<MySessionListener> mySessionListener= new ServletListenerRegistrationBean<MySessionListener>();
mySessionListener.setListener(new MySessionListener());
return mySessionListener;
}
//注意:这是SpringSecurity的集成方式
@Bean
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration() {
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
"springSecurityFilterChain");
registration.setOrder(100));
return registration;
}
4、SpringSecurity的运行原理
上面讲述了如何将Filter等实例对象注册到Servlet容器中,下面看下Springboot中几个Filter类型
在SpringSecurity中会创建名为springSecurityFilterChain
的FilterChainProxy实例对象,该对象中包含了一个或多个SecurityFilterChain实例对象,每个SecurityFilterChain实例对象包含了一个或多个用于执行权限控制的Filter实例对象。
而springSecurityFilterChain
则通过DelegatingFilterProxyRegistrationBean被自动的添加到Servlet容器中,在上面的内容中已经有说明。
Spring Security的逻辑架构图
知晓以上的基础内容后,我们现在可以来回答最开始提出的一些问题了,具体如何,请看下回分解。