package com.artfess.base.security; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import com.artfess.base.cache.annotation.CachePut; import com.artfess.base.constants.CacheKeyConst; import com.artfess.base.jwt.JwtTokenHandler; import com.artfess.base.util.AuthenticationUtil; import com.artfess.base.util.BeanUtils; import com.artfess.base.util.ContextThread; import com.artfess.base.util.StringUtil; import org.springframework.http.HttpMethod; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.SecurityConfig; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.FilterInvocation; import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.stereotype.Service; @Service public class HtInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource, ContextThread { // security认证对象的线程变量 Authentication private static ThreadLocal>> mapThreadLocal = new ThreadLocal>>(); @Resource private MethodAuthService methodAuthService; @Resource JwtTokenHandler jwtTokenHandler; /** * 加载权限表中所有权限 * * 同一个方法可能多个角色都具有访问权限 * */ public void loadResourceDefine(){ HashMap> map = getMapThreadLocal(); Collection array; ConfigAttribute cfg; List> methodAuth = methodAuthService.getMethodAuth(); if (BeanUtils.isEmpty(methodAuth)) { return; } for(HashMap mapAuth : methodAuth) { array = new ArrayList(); String roleAlias = mapAuth.get("roleAlias"); String key = mapAuth.get("methodRequestUrl"); if(StringUtil.isEmpty(roleAlias) || StringUtil.isEmpty(key) ) { continue; } cfg = new SecurityConfig(roleAlias); if(map.containsKey(key)){ array = map.get(key); } array.add(cfg); map.put(key, array); } } @CachePut(value = CacheKeyConst.EIP_SYS_DATA_PERMISSION, key="#key") protected String putDataPermissionInCache(String key, String data) { return data; } /** * 此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。 * 返回空 , 则不用经过 decide 方法 判断权限, 直接具有访问权限了 */ @Override public Collection getAttributes(Object object) throws IllegalArgumentException { AntPathRequestMatcher matcher; String resUrl; Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); // 匿名访问不需要获取权限信息 if(AuthenticationUtil.isAnonymous(authentication)) return null; // object 中包含用户请求的request 信息 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest(); // 判断请求是否从feign过来的 String requestHeader = request.getHeader("Proxy-Authorization"); if (StringUtil.isNotEmpty(requestHeader) && requestHeader.startsWith("Bearer ") && jwtTokenHandler.validateFeignToken(requestHeader.substring(7))) { return null; } if(object instanceof FilterInvocation){ FilterInvocation filterInvocation = (FilterInvocation) object; String method = filterInvocation.getRequest().getMethod(); resUrl = filterInvocation.getRequestUrl(); // options 返回null if(HttpMethod.OPTIONS.matches(method)){ return null; } } loadResourceDefine(); // 从线程中获取保证线程安全 HashMap> map = getMapThreadLocal(); for(Iterator iter = map.keySet().iterator(); iter.hasNext(); ) { resUrl = iter.next(); matcher = new AntPathRequestMatcher(resUrl); if(matcher.matches(request)) { return map.get(resUrl); } } return null; } @Override public Collection getAllConfigAttributes() { return Collections.emptyList(); } @Override public boolean supports(Class clazz) { return true; } private static HashMap> getMapThreadLocal(){ HashMap> hashMap = mapThreadLocal.get(); if(BeanUtils.isEmpty(hashMap)){ hashMap = new HashMap>(); mapThreadLocal.set(hashMap); } return hashMap; } /** * 清空线程变量中的权限数据 */ public static void clearMapThreadLocal() { HashMap> hashMap = mapThreadLocal.get(); if(hashMap!=null) { hashMap.clear(); } } @Override public void cleanAll() { clearMapThreadLocal(); } }