spring security custom access control process

Insert picture description here
First look at the above picture.
After the authentication is successful, we put the Authentication object in the securityContex, the user name and password are also put in an Authorities, the authority list is as follows: we are based on rbac so we put an admin role in, in fact, the SimpleGrantedAuthority class The agreement only allows you to put in the role, of course, there is no prescribed format, you can also put the role id such as 123;

		SimpleGrantedAuthority admin = new SimpleGrantedAuthority("admin");
        Set<SimpleGrantedAuthority> authorities=new HashSet<>();
        authorities.add(admin);
        userDetails.getAuthorities().addAll(authorities);

From this [ Analysis of Filter Access Control Process ] article, we know
that there are two lines of code in the AbstractSecurityInterceptor.beforeInvocation method.
①It means extracting the access rights required to access the current request link from the object passed in by the request.
②It means obtaining the access in 1 step. The authority is compared with the information of the currently authenticated user to determine whether it can be accessed

① Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
				.getAttributes(object);
				//。。。省略this.accessDecisionManager.decide(authenticated, object, attributes);

Then we look at the second class diagram

Insert picture description here

First look at the DefaultFilterInvocationSecurityMetadataSource.getAttributes method of this default implementation, which is called in step ① above

public Collection<ConfigAttribute> getAttributes(Object object) {
    
    
		final HttpServletRequest request = ((FilterInvocation) object).getRequest();
		for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : requestMap
				.entrySet()) {
    
    
			if (entry.getKey().matches(request)) {
    
    
				return entry.getValue();
			}
		}
		return null;
	}

requestMap is initialized in the subclass, but the subclass is rewritten in the ExpressionUrlAuthorizationConfigurer class and finally created by REGISTRY

LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = REGISTRY
				.createRequestMap();

And REGISTRY is configured in the ExpressionUrlAuthorizationConfigurer class

private final ExpressionInterceptUrlRegistry REGISTRY;

The content created above is extracted from the hard-coded content in the code in the following ways.

http.authorizeRequests().antMatchers("/test/**").hasRole("admin")
http.authorizeRequests().mvcMatcher("/test/**").hasAuthority("xxx")
@Secured("hasRole('admin')")

Obviously, the default securityMetadataSource cannot meet our current needs. Because we need to get it from the database. So we need to customize a FilterInvocationSecutrityMetadataSource implementation

public class UrlFilterInvocationSecurityMetadataSource implements
    FilterInvocationSecurityMetadataSource {
    
    
    private ResourcesProvider resourcesProvider;
    AntPathMatcher antPathMatcher = new AntPathMatcher();
    public UrlFilterInvocationSecurityMetadataSource(ResourcesProvider resourcesProvider){
    
    
        this.resourcesProvider=resourcesProvider;
    }
    
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object)
        throws IllegalArgumentException {
    
    
        //获取请求地址
        String requestUrl = ((FilterInvocation) object).getRequestUrl();
        List<Menu> allMenu = resourcesProvider.getAllMenu();
        for (Menu menu : allMenu) {
    
    
            if (antPathMatcher.match(menu.getPath(), requestUrl)&&menu.getRoles().size()>0) {
    
    
                List<Role> roles = menu.getRoles();
                int size = roles.size();
                String[] values = new String[size];
                for (int i = 0; i < size; i++) {
    
    
                    values[i] = roles.get(i).getRoleName();
                }
                //匹配上则获取role返回
                return SecurityConfig.createList(values);
            }
        }
        //没有匹配上的资源,就创建一个匿名对象即可
        return SecurityConfig.createList("ROLE_ANONYMOUS");
    }
    //没用到放空即可
    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
    
    
        return null;
    }
    //这里也可以直接放回true
    @Override
    public boolean supports(Class<?> clazz) {
    
    
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}

Then we can also customize how to compare the steps in the implementation of the above-mentioned ②


public class UrlAccessDecisionManager implements AccessDecisionManager {
    
    
    
    @Override
    public void decide(Authentication authentication, Object object,
        Collection<ConfigAttribute> configAttributes)
        throws AccessDeniedException, InsufficientAuthenticationException {
    
    
        
        // TODO: 2020/11/16 这里写访问控制流程
        Iterator<ConfigAttribute> iterator = configAttributes.iterator();
        while (iterator.hasNext()) {
    
    
            ConfigAttribute ca = iterator.next();
            //当前请求需要的权限
            String needRole = ca.getAttribute();
            //当前用户所具有的权限
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            for (GrantedAuthority authority : authorities) {
    
    
                if (authority.getAuthority().equals(needRole)) {
    
    
                    return;
                }
            }
        }
        throw new AccessDeniedException("权限不足!");
    }
    
    @Override
    public boolean supports(ConfigAttribute attribute) {
    
    
        return true;
    }
    
    @Override
    public boolean supports(Class<?> clazz) {
    
    
        return true;
    }
}

How to take effect after the expansion is completed? Remember that the previous article introduced [ ObjectPostProcessor parsing ]
that can modify the object at the time of instantiation and configure it in http

http.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
    
    
                @Override
                public <O extends FilterSecurityInterceptor> O postProcess(O o) {
    
    
                    o.setSecurityMetadataSource(urlFilterInvocationSecurityMetadataSource);
                    o.setAccessDecisionManager(urlAccessDecisionManager);
                    return o;
                }
            })

Guess you like

Origin blog.csdn.net/a807719447/article/details/110222634