在SpringSecurity中,会使用默认的FilterSecurityInterceptor来进行权限校验
在FilterSecurityInterceptor中会从SecurityContextHolder获取其中的Authentication,然后获取其中的权限信息,判断当前用户是否拥有访问当前资源所需的权限。
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 && this.observeOncePerRequest) {
fi.getRequest().setAttribute("__spring_security_filterSecurityInterceptor_filterApplied", Boolean.TRUE);
}
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.finallyInvocation(token);
}
super.afterInvocation(token, (Object)null);
}
}
protected InterceptorStatusToken beforeInvocation(Object object) {
Assert.notNull(object, "Object was null");
boolean debug = this.logger.isDebugEnabled();
if (!this.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: " + this.getSecureObjectClass());
} else {
//匹配路径
Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
if (attributes != null && !attributes.isEmpty()) {
if (debug) {
this.logger.debug("Secure object: " + object + "; Attributes: " + attributes);
}
if (SecurityContextHolder.getContext().getAuthentication() == null) {
this.credentialsNotFound(this.messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound", "An Authentication object was not found in the SecurityContext"), object, attributes);
}
Authentication authenticated = this.authenticateIfRequired();
try {
//通过传递的参数来决定用户是否有访问对应受保护资源的权限
this.accessDecisionManager.decide(authenticated, object, attributes);
} catch (AccessDeniedException var7) {
this.publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, var7));
throw var7;
}
if (debug) {
this.logger.debug("Authorization successful");
}
if (this.publishAuthorizationSuccess) {
this.publishEvent(new AuthorizedEvent(object, attributes, authenticated));
}
Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);
if (runAs == null) {
if (debug) {
this.logger.debug("RunAsManager did not change Authentication object");
}
return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);
} else {
if (debug) {
this.logger.debug("Switching to RunAs Authentication: " + runAs);
}
SecurityContext origCtx = SecurityContextHolder.getContext();
SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
SecurityContextHolder.getContext().setAuthentication(runAs);
return new InterceptorStatusToken(origCtx, true, attributes, object);
}
} else if (this.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'");
} else {
if (debug) {
this.logger.debug("Public object - authentication not attempted");
}
this.publishEvent(new PublicInvocationEvent(object));
return null;
}
}
}
获取放行路径,匹配当前路径命中,这里的路径是HttpSecurity配置的放行路径;
权限控制相关API
Spring Security 匹配了URL 后调用了permitAll()表示无需授权,随意访问。在Spring Security 中提供了多种内置控制。
权限控制方法
permitAll() permitAll()表示所匹配的URL任何人都允许访问。
authenticated() authenticated()表示所匹配的URL需要被认证才能访问。
anonymous() anonymous()表示可以匿名访问匹配的URL。
denyAll() denyAll()表示所匹配的URL 都不允许被访问。
rememberMe() 被“remember me”的用户允许访问。
fullyAuthenticated() 用户通过正常登录认证的而不是被remember me 的,才可以访问
匹配的路径也是存在规则的,如下是我的配置
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement().disable()
.authorizeRequests()
.antMatchers(
noTokenAccessUrl.toArray(new String[0])
).permitAll()
.antMatchers("/**").authenticated()
.and().headers().cacheControl();
}
访问接口区别
放行接口:
需要验证的接口:
this.accessDecisionManager.decide(authenticated, object, attributes);
通过传递的参数来决定用户是否有访问对应受保护资源的权限
这里着重说明一下decide的参数:
authentication:要访问资源的访问者的身份
object:要访问的受保护资源,web请求对应FilterInvocation
configAttributes:是受保护资源的访问策略(要访问当前资源所需要的权限),通过SecurityMetadataSource获取。 decide接口就是用来鉴定当前用户是否有访问对应受保护资源的权限。
decide接口就是用来鉴定当前用户是否有访问对应受保护资源的权限。
public class AffirmativeBased extends AbstractAccessDecisionManager {
public AffirmativeBased(List<AccessDecisionVoter<?>> decisionVoters) {
super(decisionVoters);
}
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
int deny = 0;
Iterator var5 = this.getDecisionVoters().iterator();
while(var5.hasNext()) {
AccessDecisionVoter voter = (AccessDecisionVoter)var5.next();
//投票 1授予访问权限 0访问 弃权 -1拒绝访问
int result = voter.vote(authentication, object, configAttributes);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Voter: " + voter + ", returned: " + result);
}
switch(result) {
case -1:
++deny;
break;
case 1:
return;
}
}
//拒绝数大于0
if (deny > 0) {
throw new AccessDeniedException(this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
} else {
//全是弃权的
this.checkAllowIfAllAbstainDecisions();
}
}
}
投票器
WebExpressionVoter
最常用的,也是Spring Security 框架默认 FilterSecuritylnterceptor 实例中AccessDecisionManager 默认的投票器 WebExpressionVoter。其实,就是对使用http.authorizeRequests0基于Spring-EL进行控制权限的的授权决策类
public class WebExpressionVoter implements AccessDecisionVoter<FilterInvocation> {
private SecurityExpressionHandler<FilterInvocation> expressionHandler = new DefaultWebSecurityExpressionHandler();
public WebExpressionVoter() {
}
public int vote(Authentication authentication, FilterInvocation fi, Collection<ConfigAttribute> attributes) {
assert authentication != null;
assert fi != null;
assert attributes != null;
WebExpressionConfigAttribute weca = this.findConfigAttribute(attributes);
if (weca == null) {
return 0;
} else {
//创建评估环境 将authentication信息封装
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, fi);
ctx = weca.postProcess(ctx, fi);
return ExpressionUtils.evaluateAsBoolean(weca.getAuthorizeExpression(), ctx) ? 1 : -1;
}
}
}