AbstractSecurityInterceptor 类结构图
![](https://img.haomeiwen.com/i1366458/7e896c1d351a85f6.png)
![](https://img.haomeiwen.com/i1366458/5161f54e86a93653.png)
它的三个实现类:
- FilterSecurityInterceptor 主要用来web请求拦截
- MethodSecurityInterceptor 用来方法级别的拦截
- AspectJSecurityInterceptor 用于aop方式的拦截
public abstract class AbstractSecurityInterceptor implements InitializingBean,
ApplicationEventPublisherAware, MessageSourceAware {
// ~ Static fields/initializers
// =====================================================================================
protected final Log logger = LogFactory.getLog(getClass());
// ~ Instance fields
// ================================================================================================
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private ApplicationEventPublisher eventPublisher;
private AccessDecisionManager accessDecisionManager;
private AfterInvocationManager afterInvocationManager;
private AuthenticationManager authenticationManager = new NoOpAuthenticationManager();
private RunAsManager runAsManager = new NullRunAsManager();
private boolean alwaysReauthenticate = false;
private boolean rejectPublicInvocations = false;
private boolean validateConfigAttributes = true;
private boolean publishAuthorizationSuccess = false;
}
它的属性值:
- AuthenticationManager 用来认证
- AccessDecisionManager 用来授权
- SecurityMetadataSource 用来获取与 security object 相关的 ConfigAttribute
- AfterInvocationManager 用来 review 从 secure object invocation 返回的Object
能够修改该 Object 或者抛出 AccessDeniedException 异常。通常用来确保允许 principal 访问服务层返回的 domain object instance
Reviews the Object returned from a secure object invocation,
being able to modify the Object or throw an AccessDeniedException.
Typically used to ensure the principal is permitted to access the domain object instance returned
by a service layer bean.
Can also be used to mutate the domain object instance so the principal is only able to access
authorised bean properties or Collection elements.
- RunAsManager 为当前的 secure object invocation 创建一个新的临时 Authentication 对象。
AbstractSecurityInterceptor将仅在安全对象回调的持续时间内替换SecurityContext中保存的Authentication对象
FilterSecurityInterceptor 源码
public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
...
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
this.invoke(fi);
}
public void invoke(FilterInvocation fi) throws IOException, ServletException {
// 如果应用过该过滤器直接跳过执行下一个过滤器
if (fi.getRequest() != null && fi.getRequest().getAttribute("__spring_security_filterSecurityInterceptor_filterApplied") != null && this.observeOncePerRequest) {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} else {
if (fi.getRequest() != null) {
fi.getRequest().setAttribute("__spring_security_filterSecurityInterceptor_filterApplied", Boolean.TRUE);
}
// 1. 调用之前
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
// 3. 最后调用
super.finallyInvocation(token);
}
// 2. 调用之后
super.afterInvocation(token, (Object)null);
}
}
}
beforeInvocation
protected InterceptorStatusToken beforeInvocation(Object object) {
Assert.notNull(object, "Object was null");
final boolean debug = logger.isDebugEnabled();
// 检查该过滤器是否能处理指定类型的对象
// 例如:FilterSecurityInterceptor 能够处理 FilterInvocation 类型的对象
// MethodSecurityInterceptor 能够处理 MethodInvocation 类型的对象
if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
throw new IllegalArgumentException(
"Security invocation attempted for object "
+ object.getClass().getName()
+ " but AbstractSecurityInterceptor only configured to support secure objects of type: "
+ getSecureObjectClass());
}
// 通过 security metadata 获取与 secure object 相关的 ConfigAttribute 集合
Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
.getAttributes(object);
if (attributes == null || attributes.isEmpty()) {
if (rejectPublicInvocations) {
throw new IllegalArgumentException(
"Secure object invocation "
+ object
+ " was denied as public invocations are not allowed via this interceptor. "
+ "This indicates a configuration error because the "
+ "rejectPublicInvocations property is set to 'true'");
}
if (debug) {
logger.debug("Public object - authentication not attempted");
}
publishEvent(new PublicInvocationEvent(object));
return null; // no further work post-invocation
}
if (debug) {
logger.debug("Secure object: " + object + "; Attributes: " + attributes);
}
if (SecurityContextHolder.getContext().getAuthentication() == null) {
credentialsNotFound(messages.getMessage(
"AbstractSecurityInterceptor.authenticationNotFound",
"An Authentication object was not found in the SecurityContext"),
object, attributes);
}
Authentication authenticated = authenticateIfRequired();
// Attempt authorization
// 通过 AccessDecisionManager 决定是否授权
try {
this.accessDecisionManager.decide(authenticated, object, attributes);
}
catch (AccessDeniedException accessDeniedException) {
publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
accessDeniedException));
throw accessDeniedException;
}
if (debug) {
logger.debug("Authorization successful");
}
if (publishAuthorizationSuccess) {
publishEvent(new AuthorizedEvent(object, attributes, authenticated));
}
// Attempt to run as a different user
// 使用 run-as 功能进行,模仿另一个人获取安全权限(ROLE_RUN_AS_XXX)
Authentication runAs = this.runAsManager.buildRunAs(authenticated, object,
attributes);
if (runAs == null) {
if (debug) {
logger.debug("RunAsManager did not change Authentication object");
}
// no further work post-invocation
return new InterceptorStatusToken(SecurityContextHolder.getContext(), false,
attributes, object);
}
else {
if (debug) {
logger.debug("Switching to RunAs Authentication: " + runAs);
}
SecurityContext origCtx = SecurityContextHolder.getContext();
SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
SecurityContextHolder.getContext().setAuthentication(runAs);
// need to revert to token.Authenticated post-invocation
return new InterceptorStatusToken(origCtx, true, attributes, object);
}
// 如果方法执行到这里,并且没有异常抛出,证明
// the invocation through to the resource. Authorization has been granted.
}
afterInvocation
protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) {
if (token == null) {
// public object
return returnedObject;
}
finallyInvocation(token); // continue to clean in this method for passivity
// 如果有 afterInvocationManager 配置它将负责处理返回值或者抛出异常
if (afterInvocationManager != null) {
// Attempt after invocation handling
try {
returnedObject = afterInvocationManager.decide(token.getSecurityContext()
.getAuthentication(), token.getSecureObject(), token
.getAttributes(), returnedObject);
}
catch (AccessDeniedException accessDeniedException) {
AuthorizationFailureEvent event = new AuthorizationFailureEvent(
token.getSecureObject(), token.getAttributes(), token
.getSecurityContext().getAuthentication(),
accessDeniedException);
publishEvent(event);
throw accessDeniedException;
}
}
// Here is the full authorization cycled finished.
// The response is returned to the caller.
return returnedObject;
}
authenticateIfRequired
private Authentication authenticateIfRequired() {
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
if (authentication.isAuthenticated() && !alwaysReauthenticate) {
if (logger.isDebugEnabled()) {
logger.debug("Previously Authenticated: " + authentication);
}
return authentication;
}
authentication = authenticationManager.authenticate(authentication);
// We don't authenticated.setAuthentication(true), because each provider should do
// that
if (logger.isDebugEnabled()) {
logger.debug("Successfully Authenticated: " + authentication);
}
SecurityContextHolder.getContext().setAuthentication(authentication);
return authentication;
}
![](https://img.haomeiwen.com/i1366458/0f946917f2270132.png)
网友评论