概述
SecurityContextHolderAwareRequestFilter
對請求
HttpServletRequest
采用
Wrapper/Decorator
模式包裝成一個可以通路
SecurityContextHolder
中安全上下文的
SecurityContextHolderAwareRequestWrapper
。這樣接口
HttpServletRequest
上定義的
getUserPrincipal
這種安全相關的方法才能通路到相應的安全資訊。
針對和
Servlet 2.5
,該過濾器使用了不一樣的工廠,但最終都是使用
Servlet 3
封裝請求使其具備通路
SecurityContextHolderAwareRequestWrapper
安全上下文的能力。
SecurityContextHolder
源代碼解析
package org.springframework.security.web.servletapi;
import java.io.IOException;
import java.util.List;
import javax.servlet.AsyncContext;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.web.filter.GenericFilterBean;
public class SecurityContextHolderAwareRequestFilter extends GenericFilterBean {
// ~ Instance fields
// ====================================================================================
// 預設角色名稱的字首
private String rolePrefix = "ROLE_";
// 用于封裝HttpServletRequest的工廠類,最終目的是封裝HttpServletRequest
// 使之具有通路SecurityContextHolder中安全上下文的能力。
// 針對 Servlet 2.5 和 Servlet 3 使用的不同實作類。
private HttpServletRequestFactory requestFactory;
private AuthenticationEntryPoint authenticationEntryPoint;
private AuthenticationManager authenticationManager;
private List<LogoutHandler> logoutHandlers;
// 判斷認證對象Authentication是何種類型:是否匿名Authentication,
// 是否 Remember Me Authentication。
// 預設使用實作AuthenticationTrustResolverImpl,
// 根據對象Authentication所使用的實作類是AnonymousAuthenticationToken
// 還是RememberMeAuthenticationToken達到上述目的
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
// ~ Methods
// ====================================================================================
// 指定角色字首,
public void setRolePrefix(String rolePrefix) {
Assert.notNull(rolePrefix, "Role prefix must not be null");
this.rolePrefix = rolePrefix;
// 角色字首變更時更新requestFactory工廠
updateFactory();
}
/**
*
* Sets the AuthenticationEntryPoint used when integrating
* HttpServletRequest with Servlet 3 APIs. Specifically, it will be used when
* HttpServletRequest#authenticate(HttpServletResponse) is called and the user
* is not authenticated.
*
*
* If the value is null (default), then the default container behavior will be be
* retained when invoking HttpServletRequest#authenticate(HttpServletResponse)
* .
*
*
* @param authenticationEntryPoint the AuthenticationEntryPoint to use when
* invoking HttpServletRequest#authenticate(HttpServletResponse) if the user
* is not authenticated.
*
* @throws IllegalStateException if the Servlet 3 APIs are not found on the classpath
*/
public void setAuthenticationEntryPoint(
AuthenticationEntryPoint authenticationEntryPoint) {
this.authenticationEntryPoint = authenticationEntryPoint;
}
/**
*
* Sets the AuthenticationManager used when integrating
* HttpServletRequest with Servlet 3 APIs. Specifically, it will be used when
* HttpServletRequest#login(String, String) is invoked to determine if the
* user is authenticated.
*
*
* If the value is null (default), then the default container behavior will be
* retained when invoking HttpServletRequest#login(String, String).
*
*
* @param authenticationManager the AuthenticationManager to use when invoking
* HttpServletRequest#login(String, String)
*
* @throws IllegalStateException if the Servlet 3 APIs are not found on the classpath
*/
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
/**
*
* Sets the LogoutHandlers used when integrating with
* HttpServletRequest with Servlet 3 APIs. Specifically it will be used when
* HttpServletRequest#logout() is invoked in order to log the user out. So
* long as the LogoutHandlers do not commit the HttpServletResponse
* (expected), then the user is in charge of handling the response.
*
*
* If the value is null (default), the default container behavior will be retained
* when invoking HttpServletRequest#logout().
*
*
* @param logoutHandlers the Lis<LogoutHandler> when invoking
* HttpServletRequest#logout().
*
* @throws IllegalStateException if the Servlet 3 APIs are not found on the classpath
*/
public void setLogoutHandlers(List<LogoutHandler> logoutHandlers) {
this.logoutHandlers = logoutHandlers;
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
chain.doFilter(this.requestFactory.create((HttpServletRequest) req,
(HttpServletResponse) res), res);
}
@Override
public void afterPropertiesSet() throws ServletException {
// 本Filter因為繼承自GenericFilterBean,進而隐含地實作了接口InitializingBean,
// 是以會有此方法。而此方法會在該Filter bean被初始化時調用。此時會确定具體使用的
// requestFactory 執行個體
super.afterPropertiesSet();
updateFactory();
}
private void updateFactory() {
// 更新封裝HttpServletRequest的工廠執行個體requestFactory,
// 根據目前使用的Servlet版本的不同使用不同的工廠類
String rolePrefix = this.rolePrefix;
this.requestFactory = isServlet3() ? createServlet3Factory(rolePrefix)
: new HttpServlet25RequestFactory(this.trustResolver, rolePrefix);
}
/**
* Sets the AuthenticationTrustResolver to be used. The default is
* AuthenticationTrustResolverImpl.
*
* @param trustResolver the AuthenticationTrustResolver to use. Cannot be
* null.
*/
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
Assert.notNull(trustResolver, "trustResolver cannot be null");
this.trustResolver = trustResolver;
// trustResolver 變更時更新requestFactory工廠
updateFactory();
}
private HttpServletRequestFactory createServlet3Factory(String rolePrefix) {
HttpServlet3RequestFactory factory = new HttpServlet3RequestFactory(rolePrefix);
factory.setTrustResolver(this.trustResolver);
factory.setAuthenticationEntryPoint(this.authenticationEntryPoint);
factory.setAuthenticationManager(this.authenticationManager);
factory.setLogoutHandlers(this.logoutHandlers);
return factory;
}
/**
* Returns true if the Servlet 3 APIs are detected.
* @return
*/
private boolean isServlet3() {
return ClassUtils.hasMethod(ServletRequest.class, "startAsync");
}
}
其他文章
Spring Security Web 5.1.2 源碼解析 – 安全相關Filter清單