Spring Security
一、 什么是Spring Security?
根据官方文档的解释,Spring Security是一个可以提供认证,鉴权,和从常见攻击中受到保护的框架。
二、它是怎么工作的
说明
以下内容仅用于Spring Security 6.0 在Servlet Application中的使用。之后出现的Spring Security字眼都是since 6.0 版本的。关于Reactive Application的相关内容请自行移步度娘,或者等本作者心情好了出个新篇章。大概率等不到了
Filter
Filter是javaWeb三大组件之一(Servlet程序、Listener监听器、Filter过滤器)
他的作用是:既可以对请求进行拦截,也可以对响应进行处理。
Spring Security在Servlet Application中发挥作用,就是基于Servlet Filters
DelegatingFilterProxy
Spring Security 提供了一个名为DelegatingFilterProxy的Filter实现类,它允许桥接Servlet容器的生命周期和ApplicationContext。Servlet容器允许注册用它自己标准的Filter实例,但是它不知道Spring 定义的bean。你可以将delegatingFilterProxy注册到FilterChain中,然后将所有工作丢给实现Filter接口的Spring Bean
以上是意思差不多的翻译。接下来用稍微通俗易懂点的语言解释一下
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// Lazily get Filter that was registered as a Spring Bean
// For the example in DelegatingFilterProxy delegate is an instance of Bean Filter0
Filter delegate = getFilterBean(someBeanName);
// delegate work to the Spring Bean
delegate.doFilter(request, response);
}
delegate在C语言中是委托函数保留字,java中好像没有这个概念,但java 有反射啊。getFilterBean(someBeanName),DelegatingFilterProxy将需要使用的过滤器都拿过来,这个过滤器代理这些过滤器实现他们的目的
SecurityFilterChain
接下来我们要实现的就是SecurityFilterChain,在这里经过认证,鉴权,通过的放行,没通过的拒绝访问。
FilterChain由一个个的SecurityFilter组成,他们在项目启动的时候就被打包塞给DelegatingFilterProxy代理执行了
SecurityFilterChain可以有不止1个。但是!Only the first SecurityFilterChain that matches is invoked只有匹配到的第一个SecurityFilterCHain会被调用。所以,我们将SecurityFilterChain注册到bean的时候要注意顺序。如上图模型中,/api/api同时匹配/** 和/api/**。但是,当SecurityFilterChain0被调用后,SecurityFilterChainn是不会被调用的,他只能在旁边看着干瞪眼。
Handling Security Exceptions
ExceptionTranslationFilter其实它什么都没干
public class ExceptionTranslationFilter extends GenericFilterBean implements MessageSourceAware {
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
chain.doFilter(request, response);
}
catch (IOException ex) {
throw ex;
}
catch (Exception ex) {
// Try to extract a SpringSecurityException from the stacktrace
Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);
RuntimeException securityException = (AuthenticationException) this.throwableAnalyzer
.getFirstThrowableOfType(AuthenticationException.class, causeChain);
if (securityException == null) {
securityException = (AccessDeniedException) this.throwableAnalyzer
.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
}
if (securityException == null) {
rethrow(ex);
}
if (response.isCommitted()) {
throw new ServletException("Unable to handle the Spring Security Exception "
+ "because the response is already committed.", ex);
}
handleSpringSecurityException(request, response, chain, securityException);
}
}
}
它只是在遇到异常的时候把它抛出来而已