AuthorizationFilter过滤器的作用
在 Spring Security 框架中,AuthorizationFilter
是负责执行授权(Authorization) 决策的核心过滤器。根据当前的 Authentication
(认证信息)和请求上下文,判断当前用户是否有权限访问目标资源。
先看段源码
java
public class AuthorizationFilter extends GenericFilterBean {
...
private final AuthorizationManager<HttpServletRequest> authorizationManager;
/**
* Creates an instance.
* @param authorizationManager the {@link AuthorizationManager} to use
*/
public AuthorizationFilter(AuthorizationManager<HttpServletRequest> authorizationManager) {
Assert.notNull(authorizationManager, "authorizationManager cannot be null");
this.authorizationManager = authorizationManager;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
if (this.observeOncePerRequest && isApplied(request)) {
chain.doFilter(request, response);
return;
}
if (skipDispatch(request)) {
chain.doFilter(request, response);
return;
}
String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
try {
//授权决策
AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, request);
this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, request, decision);
if (decision != null && !decision.isGranted()) {
throw new AccessDeniedException("Access Denied");
}
chain.doFilter(request, response);
}
finally {
request.removeAttribute(alreadyFilteredAttributeName);
}
}
从上面的代码中可以看出:AuthorizationFilter
本身不直接做授权判断,而是委托给一个 AuthorizationManager
接口.
AuthorizationManager
从上面的代码中可以看出,授权的核心在于调用authorizationManager的check方法,去决定当前的authentication是否有权限访问request
kotlin
//授权决策
AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, request);
typescript
@FunctionalInterface
public interface AuthorizationManager<T> {
/**
* Determines if access should be granted for a specific authentication and object.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param object the {@link T} object to check
* @throws AccessDeniedException if access is not granted
*/
default void verify(Supplier<Authentication> authentication, T object) {
AuthorizationDecision decision = check(authentication, object);
if (decision != null && !decision.isGranted()) {
throw new AccessDeniedException("Access Denied");
}
}
/**
* Determines if access is granted for a specific authentication and object.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param object the {@link T} object to check
* @return an {@link AuthorizationDecision} or null if no decision could be made
*/
@Nullable
AuthorizationDecision check(Supplier<Authentication> authentication, T object);
}
AuthorizationManager接口定义了check方法,为什么说叫授权决策,因为check方法返回的是AuthorizationDecision,这是一种授权结果 里面有个字段granted,用来决定是否有权限继续访问当前资源。
typescript
public class AuthorizationDecision implements AuthorizationResult {
private final boolean granted;
public AuthorizationDecision(boolean granted) {
this.granted = granted;
}
@Override
public boolean isGranted() {
return this.granted;
}
@Override
public String toString() {
return getClass().getSimpleName() + " [granted=" + this.granted + "]";
}
}
RequestMatcherDelegatingAuthorizationManager
如果你去调试你会发现AuthorizationFilter委托的AuthorizationManager的实现类是 RequestMatcherDelegatingAuthorizationManager,至于为什么是这个实现类,我们后续会出一个文章专门来讲解,大体还是HttpSecurity,因为它就是用来配置过滤器链的,底层就是配置过滤器的。这里不会多讲。拆解一下这个类的名字
-
RequestMatcher
:请求匹配器,用于匹配不同的 HTTP 请求(如/admin/**
,/public/**
) -
Delegating
:代表"委派",即它自己不做判断,而是把任务委派 给其他具体的AuthorizationManager
-
AuthorizationManager
:授权管理接口,负责做出"允许"或"拒绝"的决策
RequestMatcherDelegatingAuthorizationManager
内部维护了一个列表mappings:
java
public final class RequestMatcherDelegatingAuthorizationManager implements AuthorizationManager<HttpServletRequest> {
private static final AuthorizationDecision DENY = new AuthorizationDecision(false);
private final List<RequestMatcherEntry<AuthorizationManager<RequestAuthorizationContext>>> mappings;
kotlin
/**
* A rich object for associating a {@link RequestMatcher} to another object.
* 从注释中可以看出RequestMatcherEntry就是用来将RequestMatcher和其他对象建立关联的
* @author Marcus Da Coregio
* @since 5.5.5
*/
public class RequestMatcherEntry<T> {
private final RequestMatcher requestMatcher;
private final T entry;
public RequestMatcherEntry(RequestMatcher requestMatcher, T entry) {
this.requestMatcher = requestMatcher;
this.entry = entry;
}
public RequestMatcher getRequestMatcher() {
return this.requestMatcher;
}
public T getEntry() {
return this.entry;
}
}
mappings中的每个条目RequestMatcherEntry代表着一种映射关系,里面包含了
-
一个
RequestMatcher
(比如AntPathRequestMatcher("/admin/**", "GET")
) -
一个对应的
AuthorizationManager
(比如负责判断hasRole('ADMIN')
的管理器)
接下来我们看下RequestMatcherDelegatingAuthorizationManager的check流程
kotlin
/**
* Delegates to a specific {@link AuthorizationManager} based on a
* {@link RequestMatcher} evaluation.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param request the {@link HttpServletRequest} to check
* @return an {@link AuthorizationDecision}. If there is no {@link RequestMatcher}
* matching the request, or the {@link AuthorizationManager} could not decide, then
* null is returned
*/
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, HttpServletRequest request) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.format("Authorizing %s", requestLine(request)));
}
for (RequestMatcherEntry<AuthorizationManager<RequestAuthorizationContext>> mapping : this.mappings) {
RequestMatcher matcher = mapping.getRequestMatcher();
MatchResult matchResult = matcher.matcher(request);
if (matchResult.isMatch()) {
AuthorizationManager<RequestAuthorizationContext> manager = mapping.getEntry();
if (this.logger.isTraceEnabled()) {
this.logger.trace(
LogMessage.format("Checking authorization on %s using %s", requestLine(request), manager));
}
return manager.check(authentication,
new RequestAuthorizationContext(request, matchResult.getVariables()));
}
}
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.of(() -> "Denying request since did not find matching RequestMatcher"));
}
return DENY;
}
从上面的代码我们可以看出check的核心流程是:当请求到来时,它会:
-
遍历这个列表,取出里面的每个RequestMatcher,然后去判断当前
RequestMatcher
是否和当前请求request匹配,找到匹配后,找到和RequestMatcher匹配的AuthorizationManager -
将授权任务委托 给该条目对应的
AuthorizationManager
。 -
如果没有任何匹配,默认拒绝(或使用默认策略)。
mappings列表中注册的RequestMatcher和AuthorizationManager关系是如何绑定的
后续我会专门出一期文章从源码角度讲解RequestMatcherDelegatingAuthorizationManager是如何完成创建以及初始化mappings的,这里我们会如何使用就好啦。我们经常在配置过滤器链的时候有如下配置
less
@Bean
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
throws Exception {
System.out.println("filterChain http: " + System.identityHashCode(http));
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
);
return http.build();
}
比如这句代码.requestMatchers("/admin/**").hasRole("ADMIN")
就是在配置mappings映射关系,也就是当访问/admin/**
时,底层会创建一个AuthorityAuthorizationManager来处理授权决策(这块后面出一篇文章细讲),.hasRole("ADMIN")
底层会创建一个AuthorizationManager来处理授权请求。
授权结果
从 AuthorizationFilter的源码中我们知道,如果授权没通过的话就会抛出一个AccessDeniedException异常,这个异常通常会被ExceptionTranslationFilter过滤器捕获 然后进行处理,这个我们后续再将
csharp
if (decision != null && !decision.isGranted()) { throw new AccessDeniedException("Access Denied"); }
总结
AuthorizationFilter过滤器是用来实现授权决策的,但是它会委托给 RequestMatcherDelegatingAuthorizationManager来实现具体的授权决策,结合当前请求和authentication来决定是否有权限。但是这里我也留下了几处坑,没有填,后续我会写文章继续填坑
- AuthorizationFilter里面的RequestMatcherDelegatingAuthorizationManager是什么时候配置的
- RequestMatcherDelegatingAuthorizationManager需要维护一个mappings,用来映射requestMatcher和authorizationManager
- 用户配置调用的
requestMatchers("/admin/**").hasRole("ADMIN")
等配置,最后怎么生效的,底层原理是什么 4.授权没通过抛出的异常被 ExceptionTranslationFilter捕获后如何自定义处理。