mirror of
https://github.com/hs-web/hsweb-framework.git
synced 2026-06-02 02:43:59 +08:00
优化权限解析
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<>();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user