Java中AuthorizationFilter過濾器的功能
AuthorizationFilter過濾器的作用
在 Spring Security 框架中,AuthorizationFilter 是負(fù)責(zé)執(zhí)行授權(quán)(Authorization) 決策的核心過濾器。根據(jù)當(dāng)前的 Authentication(認(rèn)證信息)和請求上下文,判斷當(dāng)前用戶是否有權(quán)限訪問目標(biāo)資源。
先看段源碼
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 {
//授權(quán)決策
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 本身不直接做授權(quán)判斷,而是委托給一個 AuthorizationManager 接口.
AuthorizationManager
從上面的代碼中可以看出,授權(quán)的核心在于調(diào)用authorizationManager的check方法,去決定當(dāng)前的authentication是否有權(quán)限訪問request
//授權(quán)決策 AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, request);
@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方法,為什么說叫授權(quán)決策,因?yàn)閏heck方法返回的是AuthorizationDecision,這是一種授權(quán)結(jié)果 里面有個字段granted,用來決定是否有權(quán)限繼續(xù)訪問當(dāng)前資源。
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
如果你去調(diào)試你會發(fā)現(xiàn)AuthorizationFilter委托的AuthorizationManager的實(shí)現(xiàn)類是 RequestMatcherDelegatingAuthorizationManager,至于為什么是這個實(shí)現(xiàn)類,我們后續(xù)會出一個文章專門來講解,大體還是HttpSecurity,因?yàn)樗褪怯脕砼渲眠^濾器鏈的,底層就是配置過濾器的。這里不會多講。拆解一下這個類的名字
- RequestMatcher:請求匹配器,用于匹配不同的 HTTP 請求(如 /admin/**, /public/**)
- Delegating:代表“委派”,即它自己不做判斷,而是把任務(wù)委派給其他具體的 AuthorizationManager
- AuthorizationManager:授權(quán)管理接口,負(fù)責(zé)做出“允許”或“拒絕”的決策
RequestMatcherDelegatingAuthorizationManager 內(nèi)部維護(hù)了一個列表mappings:
public final class RequestMatcherDelegatingAuthorizationManager implements AuthorizationManager<HttpServletRequest> {
private static final AuthorizationDecision DENY = new AuthorizationDecision(false);
private final List<RequestMatcherEntry<AuthorizationManager<RequestAuthorizationContext>>> mappings;
/**
* A rich object for associating a {@link RequestMatcher} to another object.
* 從注釋中可以看出RequestMatcherEntry就是用來將RequestMatcher和其他對象建立關(guān)聯(lián)的
* @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代表著一種映射關(guān)系,里面包含了
- 一個 RequestMatcher(比如 AntPathRequestMatcher("/admin/**", "GET"))
- 一個對應(yīng)的 AuthorizationManager(比如負(fù)責(zé)判斷 hasRole('ADMIN') 的管理器)
接下來我們看下RequestMatcherDelegatingAuthorizationManager的check流程
/**
* 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的核心流程是:當(dāng)請求到來時(shí),它會:
- 遍歷這個列表,取出里面的每個RequestMatcher,然后去判斷當(dāng)前
RequestMatcher是否和當(dāng)前請求request匹配,找到匹配后,找到和RequestMatcher匹配的AuthorizationManager - 將授權(quán)任務(wù)委托給該條目對應(yīng)的
AuthorizationManager。 - 如果沒有任何匹配,默認(rèn)拒絕(或使用默認(rèn)策略)。
mappings列表中注冊的RequestMatcher和AuthorizationManager關(guān)系是如何綁定的
后續(xù)我會專門出一期文章從源碼角度講解RequestMatcherDelegatingAuthorizationManager是如何完成創(chuàng)建以及初始化mappings的,這里我們會如何使用就好啦。我們經(jīng)常在配置過濾器鏈的時(shí)候有如下配置
@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映射關(guān)系,也就是當(dāng)訪問/admin/**時(shí),底層會創(chuàng)建一個AuthorityAuthorizationManager來處理授權(quán)決策(這塊后面出一篇文章細(xì)講),.hasRole("ADMIN")底層會創(chuàng)建一個AuthorizationManager來處理授權(quán)請求。
授權(quán)結(jié)果
從 AuthorizationFilter的源碼中我們知道,如果授權(quán)沒通過的話就會拋出一個AccessDeniedException異常,這個異常通常會被ExceptionTranslationFilter過濾器捕獲 然后進(jìn)行處理,這個我們后續(xù)再將
if (decision != null && !decision.isGranted()) { throw new AccessDeniedException("Access Denied"); }
總結(jié)
AuthorizationFilter過濾器是用來實(shí)現(xiàn)授權(quán)決策的,但是它會委托給 RequestMatcherDelegatingAuthorizationManager來實(shí)現(xiàn)具體的授權(quán)決策,結(jié)合當(dāng)前請求和authentication來決定是否有權(quán)限。但是這里我也留下了幾處坑,沒有填,后續(xù)我會寫文章繼續(xù)填坑
- AuthorizationFilter里面的RequestMatcherDelegatingAuthorizationManager是什么時(shí)候配置的
- RequestMatcherDelegatingAuthorizationManager需要維護(hù)一個mappings,用來映射requestMatcher和authorizationManager
- 用戶配置調(diào)用的
requestMatchers("/admin/**").hasRole("ADMIN")等配置,最后怎么生效的,底層原理是什么 4.授權(quán)沒通過拋出的異常被 ExceptionTranslationFilter捕獲后如何自定義處理。
到此這篇關(guān)于Java中AuthorizationFilter過濾器的功能的文章就介紹到這了,更多相關(guān)Java AuthorizationFilter過濾器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring boot與redis 實(shí)現(xiàn)session共享教程
這篇文章主要介紹了spring boot與redis 實(shí)現(xiàn)session共享教程,非常不錯,具有參考借鑒價(jià)值,需要的朋友可以參考下2017-04-04
Java中的== 和equals()方法詳解與實(shí)例
本篇文章介紹了,在java中"==" 與equals方法的使用及其實(shí)例,需要的朋友可以參考下2017-04-04
java反射機(jī)制的一些學(xué)習(xí)心得小結(jié)
這篇文章主要給大家介紹了關(guān)于java反射機(jī)制的一些學(xué)習(xí)心得,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02
基于Java判斷網(wǎng)絡(luò)是否正常代碼實(shí)例
這篇文章主要介紹了基于Java判斷網(wǎng)絡(luò)是否正常代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
Mybatis-Plus適配聯(lián)合主鍵實(shí)現(xiàn)方式
本文介紹了如何使用Mybatis-Plus(MP)和Mybatis-Plus-Plus(MPP)工具來處理聯(lián)合主鍵的問題,并詳細(xì)描述了整合過程、遇到的問題及解決方式2025-11-11

