springSecurity框架下自定义校验流程

由于业务的需要,重写springSecurity下核心管理器

FilterInvocationSecurityMetadataSource和AccessDecisionManager

1,业务需求

由于菜单表中的请求地址有可能重复,必须通过请求类型加以区分,原有的springSecurity无法满足,所以重写springSecurity中鉴权的核心管理器FilterInvocationSecurityMetadataSource(查询当前接口所需权限),AccessDecisionManager(判断当前用户是否拥有接口所需权限)

2,核心代码

查询当前接口所需权限

@Component
public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

	@Resource
	private MenuFacade menuFacade;

	@Override
	public Collection getAttributes(Object object) throws IllegalArgumentException {

		final HttpServletRequest request = ((FilterInvocation) object).getRequest();
		if(request.getRequestURI().substring(11).equals("/error")){
			throw new AccessDeniedException("未登录或登录过期");
		}
			String s = request.getRequestURI().substring(11) + request.getMethod();
			List authorityUriList = menuFacade.getAuthorityUriList();
			//获取所有uri
			List collect = authorityUriList.stream().map(MenuCodeUriResDTO::getRequestUri).collect(Collectors.toList());
			//当前接口于菜单表中不存在,不做权限控制
			if(!collect.contains( request.getRequestURI().substring(11))){
				return SecurityConfig.createList(SysAuthConstants.PASS_PERMISSION);
			}
			for(MenuCodeUriResDTO uriReq:authorityUriList){
				//当前接口所需权限返回
				if(uriReq.getRequestUriReqType().equals(s)){
					return SecurityConfig.createList(uriReq.getAuthorityCode());
				}
			}
		//返回默认权限,返回null自定义UrlAccessManage接口权限检验类不生效
		return SecurityConfig.createList(SysAuthConstants.DEFAULT_PERMISSION);
	}

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

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

判断当前用户/角色是否拥有该权限

@Component
public class UrlAccessManage implements AccessDecisionManager {
	@Override
	public void decide(Authentication authentication, Object object, Collection configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
		//获取当前接口所需的权限集合
		Iterator iterator = configAttributes.iterator();
		while (iterator.hasNext()) {
			ConfigAttribute ca = iterator.next();
			//当前请求需要的权限
			String needAuth = ca.getAttribute();
			//等于默认放行权限,直接通过
			if(needAuth.equals(SysAuthConstants.PASS_PERMISSION)){
				return;
			}

			//获取上下文中该用户角色所拥有的所有权限
			Collection authorities = authentication.getAuthorities();
			for (GrantedAuthority authority : authorities) {
				//该用户角色拥有接口所需权限,通过
				if (authority.getAuthority().equals(needAuth)) {
					return;
				}
			}
		}
		throw new AccessDeniedException("当前用户权限不足");
		//UserExceptionEnum.USER_PERMISSION_DENIED.assertFalse(true);
	}

	@Override
	public boolean supports(ConfigAttribute attribute) {
		return true;
	}

	@Override
	public boolean supports(Class clazz) {
		return true;
	}
}

将自定义类注入securityConfig中,使其产生作用


	@Override
	protected void configure(HttpSecurity httpSecurity) throws Exception {
		ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = httpSecurity
				.authorizeRequests()
				.withObjectPostProcessor(new ObjectPostProcessor() {
					@Override
					public  O postProcess(O o) {
						o.setSecurityMetadataSource(mySecurityMetadataSource);
						o.setAccessDecisionManager(urlAccessManage);
						return o;
					}
				});
		// 白名单
		for (String url : ignoreUrlsConfig()) {
			registry.antMatchers(url).permitAll();
		}
		// 允许跨域请求的OPTIONS请求
		registry.antMatchers(HttpMethod.OPTIONS, "/**")
				.permitAll();

至此,该功能基本重写完成

3,注意事项

重写这部分后,springSecurity自带的校验失效,可能导致对于token,接口权限拦截的异常全局拦截出现问题,可以自行定义异常信息,此处不做详解

你可能感兴趣的:(java,spring)