文章目录
AbstractSecurityInterceptor
Declared
package org.springframework.security.access.intercept;
public abstract class AbstractSecurityInterceptor
implements InitializingBean,ApplicationEventPublisherAware, MessageSourceAware
Class Jdoc
实现安全对象的安全拦截的抽象类。
AbstractSecurityInterceptor将确保安全拦截器能正确启动配置。它还将实现对安全对象调用的正确处理,即:
- 从SecurityContextHolder获取Authentication对象。
- 通过在SecurityMetadataSource中查找安全对象请求来确定请求是否与安全调用或公共调用相关。
- 对于安全的调用(有一个关于安全对象调用ConfigAttributes列表):
- 如果Authentication.isAuthenticated()返回false,或者alwaysReauthenticate属性是true,则通过配置的AuthenticationManager实现对请求做身份认证。通过身份认证时,将SecurityContextHolder中的Authentication对象替换为返回的值。
- 鉴权依赖于配置的AccessDecisionManager。
- 通过配置的RunAsManager执行任何run-as替换。
- 将控制权传递回具体的子类,该子类将实际继续执行对象。将返回一个InterceptorStatusToken,以便在子类完成对象的执行之后,它的finally子句可以确保重新调用AbstractSecurityInterceptor并使用finallyInvocation(InterceptorStatusToken)正确地清理。
- 具体子类将通过afterInvocation(InterceptorStatusToken, Object) 从新调用AbstractSecurityInterceptor。
- 如果RunAsManager替换了Authentication对象,则在调用AuthenticationManager后返回存在于SecurityContextHolder中的对象。
- 如果定义了AfterInvocationManager,则调用InvocationManager并允许它替换要返回给调用方的对象。
- 对于公共调用(没有ConfigAttributes的安全对象调用):
- 如上所述,在安全对象被处理后,返回InterceptorStatusToken,然后重新提交给AbstractSecurityInterceptor。当afterInvocation(InterceptorStatusToken, Object)被调用时,AbstractSecurityInterceptor不会做任何事情。
- 控制权被再次返给具体的子类,随对象一起返回给调用方,然后,子类将该结果或异常返回给原始调用方。
beforeInvocation
Declared
protected InterceptorStatusToken beforeInvocation(Object object)
Method Code
protected InterceptorStatusToken beforeInvocation(Object object) {
Assert.notNull(object, "Object was null");
final boolean debug = logger.isDebugEnabled();
//判断当前过滤器能否支持待处理的安全对象的类型?
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());
}
// 在SecurityMetadataSource中查找权限配置。
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);
}
// 判断SecurityContext中有无Authentication对象,即是否已经有经过认证的账户信息(匿名访问会生成匿名访问账户)
if (SecurityContextHolder.getContext().getAuthentication() == null) {
credentialsNotFound(messages.getMessage(
"AbstractSecurityInterceptor.authenticationNotFound",
"An Authentication object was not found in the SecurityContext"),
object, attributes);
}
// 如果Authentication.isauthenticated()返回false或属性alwaysReauthenticate已设置为true,则检查当前身份验证令牌并将其传递给AuthenticationManager。
Authentication authenticated = authenticateIfRequired();
// Attempt authorization
// 尝试鉴权
try {
// 通过AccessDecisionManager鉴权
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
// 尝试替换为另一个用户。
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);
}
}
finallyInvocation
Declared
protected void finallyInvocation(InterceptorStatusToken token)
Method Jdoc
安全对象调用完成后,清理AbstractSecurityInterceptor的工作。无论安全对象调用是否成功返回(即它应该在finally块中完成),都应该在安全对象调用之后和调用后调用之前调用此方法。
Method Code
protected void finallyInvocation(InterceptorStatusToken token) {
if (token != null && token.isContextHolderRefreshRequired()) {
if (logger.isDebugEnabled()) {
logger.debug("Reverting to original Authentication: "
+ token.getSecurityContext().getAuthentication());
}
SecurityContextHolder.setContext(token.getSecurityContext());
}
}
afterInvocation
Declared
protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject)
Method Jdoc
完成安全对象调用之后,完成AbstractSecurityInterceptor的工作。
Method Code
protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) {
if (token == null) {
// public object
return returnedObject;
}
finallyInvocation(token); // continue to clean in this method for passivity
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;
}
}
return returnedObject;
}
getSecureObjectClass
Declared
public abstract Class<?> getSecureObjectClass();
Method Jdoc
指示子类将呈现给抽象父类以进行处理的安全对象的类型。这用于确保连接到AbstractSecurityInterceptor的协作者都支持指定的安全对象类。
authenticateIfRequired
Declared
private Authentication authenticateIfRequired()
Method Jdoc
如果Authentication.isauthenticated()返回false或属性alwaysReauthenticate已设置为true,则检查当前身份验证令牌并将其传递给AuthenticationManager。
Method Code
private Authentication authenticateIfRequired() {
// 从SecurityContext中取得Authentication对象
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
// 如果Authentication.isauthenticated()返回true且属性alwaysReauthenticate已设置为false,则直接返回SecurityContext中的Authentication对象
if (authentication.isAuthenticated() && !alwaysReauthenticate) {
if (logger.isDebugEnabled()) {
logger.debug("Previously Authenticated: " + authentication);
}
return authentication;
}
// 通过配置的AuthenticationManager实现对请求做身份认证。
authentication = authenticationManager.authenticate(authentication);
// We don't authenticated.setAuthentication(true), because each provider should do
// that
if (logger.isDebugEnabled()) {
logger.debug("Successfully Authenticated: " + authentication);
}
// 将经过认证的Authentication写回SecurityContext
SecurityContextHolder.getContext().setAuthentication(authentication);
return authentication;
}