优化权限解析

This commit is contained in:
zhou-hao
2017-11-30 00:31:16 +08:00
parent e6434db744
commit 735710b054
18 changed files with 179 additions and 172 deletions

View File

@@ -1,29 +1,52 @@
package org.hswebframework.web.authorization.basic.aop;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.hswebframework.web.AopUtils;
import org.hswebframework.web.authorization.Authentication;
import org.hswebframework.web.authorization.annotation.Authorize;
import org.hswebframework.web.authorization.basic.define.EmptyAuthorizeDefinition;
import org.hswebframework.web.authorization.basic.handler.AuthorizingHandler;
import org.hswebframework.web.authorization.define.AuthorizeDefinition;
import org.hswebframework.web.authorization.define.AuthorizeDefinitionInitializedEvent;
import org.hswebframework.web.authorization.define.AuthorizingContext;
import org.hswebframework.web.authorization.define.Phased;
import org.hswebframework.web.authorization.exception.UnAuthorizedException;
import org.hswebframework.web.boost.aop.context.MethodInterceptorContext;
import org.hswebframework.web.boost.aop.context.MethodInterceptorHolder;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RestController;
import java.lang.reflect.Method;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author zhouhao
* @see AuthorizeDefinitionInitializedEvent
*/
public class AopAuthorizingController extends StaticMethodMatcherPointcutAdvisor {
@Slf4j
public class AopAuthorizingController extends StaticMethodMatcherPointcutAdvisor implements CommandLineRunner {
private static final long serialVersionUID = 1154190623020670672L;
@Autowired
private ApplicationEventPublisher eventPublisher;
private DefaultAopMethodAuthorizeDefinitionParser defaultParser = new DefaultAopMethodAuthorizeDefinitionParser();
private boolean autoParse = false;
public void setAutoParse(boolean autoParse) {
this.autoParse = autoParse;
}
public AopAuthorizingController(AuthorizingHandler authorizingHandler, AopMethodAuthorizeDefinitionParser aopMethodAuthorizeDefinitionParser) {
super((MethodInterceptor) methodInvocation -> {
@@ -31,12 +54,13 @@ public class AopAuthorizingController extends StaticMethodMatcherPointcutAdvisor
MethodInterceptorContext paramContext = holder.createParamContext();
AuthorizeDefinition definition = aopMethodAuthorizeDefinitionParser.parse(paramContext);
AuthorizeDefinition definition = aopMethodAuthorizeDefinitionParser.parse(methodInvocation.getThis().getClass(), methodInvocation.getMethod());
Object result = true;
boolean isControl = false;
if (null != definition) {
Authentication authentication = Authentication.current().orElseThrow(UnAuthorizedException::new);
if (!definition.isEmpty()) {
AuthorizingContext context = new AuthorizingContext();
context.setAuthentication(authentication);
context.setDefinition(definition);
@@ -84,16 +108,33 @@ public class AopAuthorizingController extends StaticMethodMatcherPointcutAdvisor
if (!isControl) {
result = methodInvocation.proceed();
}
return result;
});
}
@Override
public boolean matches(Method method, Class<?> aClass) {
//对controller进行控制
return AopUtils.findAnnotation(aClass, Controller.class) != null
boolean support = AopUtils.findAnnotation(aClass, Controller.class) != null
|| AopUtils.findAnnotation(aClass, RestController.class) != null
|| AopUtils.findAnnotation(aClass, method, Authorize.class) != null;
if (support && autoParse) {
defaultParser.parse(aClass, method);
}
return support;
}
@Override
public void run(String... args) throws Exception {
if (autoParse) {
List<AuthorizeDefinition> definitions = defaultParser.getAllParsed()
.stream().filter(def -> !def.isEmpty()).collect(Collectors.toList());
log.info("publish AuthorizeDefinitionInitializedEvent,definition size:{}", definitions.size());
eventPublisher.publishEvent(new AuthorizeDefinitionInitializedEvent(definitions));
defaultParser.destroy();
}
}
}

View File

@@ -3,11 +3,13 @@ package org.hswebframework.web.authorization.basic.aop;
import org.hswebframework.web.authorization.define.AuthorizeDefinition;
import org.hswebframework.web.boost.aop.context.MethodInterceptorContext;
import java.lang.reflect.Method;
/**
* 自定义权限控制定义,在拦截到方法后,优先使用此接口来获取权限控制方式
* @see AuthorizeDefinition
* @author zhouhao
*/
public interface AopMethodAuthorizeDefinitionCustomizerParser {
AuthorizeDefinition parse(MethodInterceptorContext context);
AuthorizeDefinition parse(Class target, Method method,MethodInterceptorContext context);
}

View File

@@ -3,6 +3,9 @@ package org.hswebframework.web.authorization.basic.aop;
import org.hswebframework.web.authorization.define.AuthorizeDefinition;
import org.hswebframework.web.boost.aop.context.MethodInterceptorContext;
import java.lang.reflect.Method;
import java.util.List;
/**
* 权限控制定义解析器,用于解析被拦截的请求是否需要进行权限控制,以及权限控制的方式
*
@@ -14,8 +17,15 @@ public interface AopMethodAuthorizeDefinitionParser {
/**
* 解析权限控制定义
*
* @param paramContext 被拦截的方法上下文
* @param target class
* @param method method
* @return 权限控制定义, 如果不进行权限控制则返回{@code null}
*/
AuthorizeDefinition parse(MethodInterceptorContext paramContext);
AuthorizeDefinition parse(Class target, Method method, MethodInterceptorContext context);
default AuthorizeDefinition parse(Class target, Method method) {
return parse(target, method, null);
}
List<AuthorizeDefinition> getAllParsed();
}

View File

@@ -1,5 +1,6 @@
package org.hswebframework.web.authorization.basic.aop;
import lombok.extern.slf4j.Slf4j;
import org.hswebframework.web.AopUtils;
import org.hswebframework.web.authorization.annotation.Authorize;
import org.hswebframework.web.authorization.annotation.RequiresDataAccess;
@@ -13,10 +14,7 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
@@ -26,23 +24,32 @@ import java.util.concurrent.ConcurrentHashMap;
* @see AopMethodAuthorizeDefinitionParser
* @see AuthorizeDefinition
*/
@Slf4j
public class DefaultAopMethodAuthorizeDefinitionParser implements AopMethodAuthorizeDefinitionParser {
private Map<CacheKey, AuthorizeDefinition> cache = new ConcurrentHashMap<>();
private List<AopMethodAuthorizeDefinitionCustomizerParser> parserCustomers;
private static Set<String> excludeMethodName = new HashSet<>(Arrays.asList("toString", "clone", "hashCode", "getClass"));
@Autowired(required = false)
public void setParserCustomers(List<AopMethodAuthorizeDefinitionCustomizerParser> parserCustomers) {
this.parserCustomers = parserCustomers;
}
@Override
public List<AuthorizeDefinition> getAllParsed() {
return new ArrayList<>(cache.values());
}
@Override
@SuppressWarnings("all")
public AuthorizeDefinition parse(MethodInterceptorContext paramContext) {
CacheKey key = buildCacheKey(paramContext);
public AuthorizeDefinition parse(Class target, Method method, MethodInterceptorContext context) {
if (excludeMethodName.contains(method.getName())) {
return null;
}
CacheKey key = buildCacheKey(target, method);
AuthorizeDefinition definition = cache.get(key);
if (definition != null && (definition instanceof EmptyAuthorizeDefinition)) {
@@ -51,7 +58,7 @@ public class DefaultAopMethodAuthorizeDefinitionParser implements AopMethodAutho
//使用自定义
if (!CollectionUtils.isEmpty(parserCustomers)) {
definition = parserCustomers.stream()
.map(customer -> customer.parse(paramContext))
.map(customer -> customer.parse(target, method, context))
.filter(Objects::nonNull)
.findAny().orElse(null);
if (definition == null || definition instanceof EmptyAuthorizeDefinition) {
@@ -60,14 +67,14 @@ public class DefaultAopMethodAuthorizeDefinitionParser implements AopMethodAutho
}
Authorize classAuth = AopUtils.findAnnotation(paramContext.getTarget().getClass(), Authorize.class);
Authorize methodAuth = AopUtils.findMethodAnnotation(paramContext.getTarget().getClass(), paramContext.getMethod(), Authorize.class);
Authorize classAuth = AopUtils.findAnnotation(target, Authorize.class);
Authorize methodAuth = AopUtils.findMethodAnnotation(target, method, Authorize.class);
RequiresDataAccess classDataAccess = AopUtils.findAnnotation(paramContext.getTarget().getClass(), RequiresDataAccess.class);
RequiresDataAccess classDataAccess = AopUtils.findAnnotation(target, RequiresDataAccess.class);
RequiresDataAccess methodDataAccess = AopUtils.findMethodAnnotation(paramContext.getTarget().getClass(), paramContext.getMethod(), RequiresDataAccess.class);
RequiresDataAccess methodDataAccess = AopUtils.findMethodAnnotation(target, method, RequiresDataAccess.class);
RequiresExpression expression = AopUtils.findAnnotation(paramContext.getTarget().getClass(), RequiresExpression.class);
RequiresExpression expression = AopUtils.findAnnotation(target, RequiresExpression.class);
if (classAuth == null && methodAuth == null && classDataAccess == null && methodDataAccess == null && expression == null) {
cache.put(key, EmptyAuthorizeDefinition.instance);
@@ -78,7 +85,6 @@ public class DefaultAopMethodAuthorizeDefinitionParser implements AopMethodAutho
cache.put(key, EmptyAuthorizeDefinition.instance);
return null;
}
DefaultBasicAuthorizeDefinition authorizeDefinition = new DefaultBasicAuthorizeDefinition();
if (methodAuth == null || methodAuth.merge()) {
@@ -89,18 +95,43 @@ public class DefaultAopMethodAuthorizeDefinitionParser implements AopMethodAutho
authorizeDefinition.put(expression);
authorizeDefinition.put(methodAuth.dataAccess());
if (methodAuth != null) {
authorizeDefinition.put(methodAuth.dataAccess());
}
authorizeDefinition.put(classDataAccess);
authorizeDefinition.put(methodDataAccess);
if (authorizeDefinition.getPermissionDescription().length == 0) {
if (classAuth != null) {
String[] desc = classAuth.description();
if (desc.length > 0) {
authorizeDefinition.setPermissionDescription(desc);
}
}
}
if (authorizeDefinition.getActionDescription().length == 0) {
if (methodAuth != null) {
if (methodAuth.description().length != 0) {
authorizeDefinition.setActionDescription(methodAuth.description());
}
}
}
log.info("parsed authorizeDefinition {}.{} => {}.{} permission:{} actions:{}",
target.getSimpleName(),
method.getName(),
authorizeDefinition.getPermissionDescription(),
authorizeDefinition.getActionDescription(),
authorizeDefinition.getPermissions(),
authorizeDefinition.getActions());
cache.put(key, authorizeDefinition);
return authorizeDefinition;
}
public CacheKey buildCacheKey(MethodInterceptorContext context) {
return new CacheKey(ClassUtils.getUserClass(context.getTarget()), context.getMethod());
public CacheKey buildCacheKey(Class target, Method method) {
return new CacheKey(ClassUtils.getUserClass(target), method);
}
class CacheKey {
@@ -123,4 +154,8 @@ public class DefaultAopMethodAuthorizeDefinitionParser implements AopMethodAutho
}
}
public void destroy() {
cache.clear();
}
}

View File

@@ -6,6 +6,7 @@ import org.hswebframework.web.authorization.basic.aop.DefaultAopMethodAuthorizeD
import org.hswebframework.web.authorization.basic.handler.AuthorizingHandler;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -24,8 +25,10 @@ public class AopAuthorizeAutoConfiguration {
@Bean
@ConfigurationProperties(prefix = "hsweb.authorize")
public AopAuthorizingController aopAuthorizingController(AuthorizingHandler authorizingHandler,
AopMethodAuthorizeDefinitionParser aopMethodAuthorizeDefinitionParser) {
return new AopAuthorizingController(authorizingHandler, aopMethodAuthorizeDefinitionParser);
return new AopAuthorizingController(authorizingHandler, aopMethodAuthorizeDefinitionParser);
}
}

View File

@@ -25,9 +25,14 @@ import java.util.Set;
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class DefaultBasicAuthorizeDefinition implements AuthorizeDefinition {
private boolean dataAccessControl;
private String[] permissionDescription = {};
private String[] actionDescription = {};
private Set<String> permissions = new HashSet<>();
private Set<String> actions = new HashSet<>();

View File

@@ -1,9 +1,6 @@
package org.hswebframework.web.authorization.basic.define;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.*;
import org.hswebframework.web.authorization.define.DataAccessDefinition;
import org.hswebframework.web.authorization.define.Phased;
@@ -14,6 +11,7 @@ import org.hswebframework.web.authorization.define.Phased;
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class DefaultDataAccessDefinition implements DataAccessDefinition {
private static final long serialVersionUID = 8285566729547666068L;

View File

@@ -38,6 +38,16 @@ public class EmptyAuthorizeDefinition implements AuthorizeDefinition {
throw new UnsupportedOperationException();
}
@Override
public String[] getPermissionDescription() {
throw new UnsupportedOperationException();
}
@Override
public String[] getActionDescription() {
throw new UnsupportedOperationException();
}
@Override
public Set<String> getActions() {
throw new UnsupportedOperationException();

View File

@@ -92,7 +92,7 @@ public class AuthorizeTests {
@Test
public void testParseAuthorizeDefinition() {
AuthorizeDefinition definition = parser.parse(queryById);
AuthorizeDefinition definition = parser.parse(queryById.getTarget().getClass(),queryById.getMethod());
Assert.assertNotNull(definition);
Assert.assertEquals(definition.getPermissions().size(), 1);
@@ -104,7 +104,7 @@ public class AuthorizeTests {
public void testAuthorizingHandler() {
DefaultAuthorizingHandler handler = new DefaultAuthorizingHandler();
AuthorizeDefinition definition = parser.parse(queryById);
AuthorizeDefinition definition = parser.parse(queryById.getTarget().getClass(),queryById.getMethod());
AuthorizingContext authorizingContext = new AuthorizingContext();
authorizingContext.setAuthentication(authentication);
@@ -127,7 +127,7 @@ public class AuthorizeTests {
handler.setDataAccessController(controller);
AuthorizeDefinition definition = parser.parse(dynamicQuery);
AuthorizeDefinition definition = parser.parse(dynamicQuery.getTarget().getClass(),dynamicQuery.getMethod());
//获取到请求参数
QueryParamEntity entity = dynamicQuery.<QueryParamEntity>getParameter("paramEntity").orElseThrow(NullPointerException::new);
@@ -158,7 +158,7 @@ public class AuthorizeTests {
handler.setDataAccessController(controller);
AuthorizeDefinition definition = parser.parse(queryById);
AuthorizeDefinition definition = parser.parse(queryById.getTarget().getClass(),queryById.getMethod());
//响应结果
Object response = queryById.getInvokeResult();