Spring Security Study Notes (13) The underlying principle of authentication and authorization

reference video

When I first learned the overall structure of ss in the previous article, there was such a picture.
insert image description here
When a request comes, it will pass through first FilterSecurityInterceptor, and doFilterthe following invokemethod is called in

public void invoke(FilterInvocation fi) throws IOException, ServletException {
    
    
		if ((fi.getRequest() != null)
				&& (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
				&& observeOncePerRequest) {
    
    
			// filter already applied to this request and user wants us to observe
			// once-per-request handling, so don't re-do security checking
			fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
		}
		else {
    
    
			// first time this request being called, so perform security checking
			if (fi.getRequest() != null && observeOncePerRequest) {
    
    
				fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
			}

//下面这个就会 调用父类中的方法,这就相当于是一个前置的校验,判断当前请求的方法需不需要授权,需不需要认证。
			InterceptorStatusToken token = super.beforeInvocation(fi);

			try {
    
    
				fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
			}
			finally {
    
    
				super.finallyInvocation(token);
			}

			super.afterInvocation(token, null);
		}
	}

InterceptorStatusToken token = super.beforeInvocation(fi);

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);
		}

		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
		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);
		}
	}

Let's analyze SecurityMetadataSourcewhat it is, and what this.obtainSecurityMetadataSource()is returned is one FilterInvocationSecurityMetadataSource, that is to say, this object will obtain which permissions each interface needs, that is to say, if we implement such an interface ourselves, we can customize the access permissions of the interface, if this The custom one is queried from the database, so you don’t need to write the
insert image description here
specific implementation in the code like the following every time

  • Custom MetaSource
package com.dongmu.config;

import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import java.util.Collection;

@Component
public class CoustomSecurityMetaSource implements FilterInvocationSecurityMetadataSource {
    
    


    AntPathMatcher antPathMatcher = new AntPathMatcher();


    /**
     * 自定义动态资源权限元数据信息
     * @param object
     * @return
     * @throws IllegalArgumentException
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
    
    

        /**
         * 这里面进行判断对应的资源所需要的权限,这里仅作说明,不进行具体的操作,有关数据库的设计,需要自行设计。
         */

        String requestURI = ((FilterInvocation) object).getRequest().getRequestURI();

        //这里面根据antPathMatcher匹配从数据库查询出来的对应的url对应数据库所需要的权限
//        然后写到下面
        return SecurityConfig.createList("");
//        return null;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
    
    
        return null;
    }

    @Override
    public boolean supports(Class<?> clazz) {
    
    
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}

Register in the configuration

@Autowired
    CoustomSecurityMetaSource coustomSecurityMetaSource;
    
    @Override
    @DependsOn("myAuthenticationHandler")
    protected void configure(HttpSecurity http) throws Exception {
    
    

        ApplicationContext applicationContext = http.getSharedObject(ApplicationContext.class);


        http.apply(new UrlAuthorizationConfigurer<>(applicationContext)).
                withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
    
    
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
    
    
                        object.setSecurityMetadataSource(coustomSecurityMetaSource);
						//是否拒绝公共资源的访问
                        object.setRejectPublicInvocations(false);
                        return object;
                    }
                });

Guess you like

Origin blog.csdn.net/qq_45401910/article/details/127242398