优化权限,修复controller重写父类导致注解失效的问题

This commit is contained in:
zhouhao
2017-02-24 15:18:21 +08:00
parent 47ebcbc6da
commit d2eba56010
17 changed files with 115 additions and 153 deletions

View File

@@ -184,7 +184,8 @@ public class ShiroAutoconfiguration {
static class MethodInterceptorHolderAdvisor {
@Around(value = "@annotation(org.hswebframework.web.authorization.annotation.RequiresExpression)" +
"||@annotation(org.hswebframework.web.authorization.annotation.RequiresDataAccess)" +
"||@annotation(org.hswebframework.web.authorization.annotation.Authorize)")
"||@annotation(org.hswebframework.web.authorization.annotation.Authorize)" +
"||within(@org.hswebframework.web.authorization.annotation.Authorize *)")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
MethodSignature signature = (MethodSignature) pjp.getSignature();
String methodName = AopUtils.getMethodBody(pjp);
@@ -202,14 +203,14 @@ public class ShiroAutoconfiguration {
@ResponseStatus(HttpStatus.FORBIDDEN)
@ResponseBody
ResponseMessage handleException(AuthorizationException exception) {
return ResponseMessage.error(exception.getMessage(), 403);
return ResponseMessage.error(403, exception.getMessage());
}
@ExceptionHandler(UnauthenticatedException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@ResponseBody
ResponseMessage handleException(UnauthenticatedException exception) {
return ResponseMessage.error(exception.getMessage() == null ? "{access_denied}" : exception.getMessage(), 401);
return ResponseMessage.error(401, exception.getMessage() == null ? "{access_denied}" : exception.getMessage());
}
}

View File

@@ -28,11 +28,11 @@ import org.hswebframework.web.authorization.annotation.RequiresDataAccess;
import org.hswebframework.web.authorization.annotation.RequiresExpression;
import org.hswebframework.web.authorization.annotation.RequiresFieldAccess;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* @author zhouhao
@@ -100,16 +100,28 @@ public class BoostAuthorizationAttributeSourceAdvisor extends StaticMethodMatche
*/
public boolean matches(Method method, Class targetClass) {
Method m = method;
if (isAuthzAnnotationPresent(m)) {
return true;
}
//The 'method' parameter could be from an interface that doesn't have the annotation.
//Check to see if the implementation has it.
if (targetClass != null) {
try {
m = targetClass.getMethod(m.getName(), m.getParameterTypes());
//尝试解决由于被拦截的方法使用了泛型,并且重写了方法,导致无法获取父类方法的问题
Class[] parameter = Arrays
.stream(m.getParameterTypes())
.map(type -> {
if (type.isInterface()) return type;
Class<?>[] interfaces = type.getInterfaces();
if (interfaces.length > 0) return interfaces[0];
Class superclass = type.getSuperclass();
if (null != superclass && superclass != Object.class) {
return superclass;
}
return type;
})
.toArray(Class[]::new);
m = targetClass.getMethod(m.getName(), parameter);
if (isAuthzAnnotationPresent(m)) {
return true;
}
@@ -131,5 +143,4 @@ public class BoostAuthorizationAttributeSourceAdvisor extends StaticMethodMatche
}
return false;
}
}

View File

@@ -6,6 +6,7 @@ import org.hswebframework.web.authorization.access.FieldAccess;
import org.hswebframework.web.authorization.access.FieldAccessController;
import org.hswebframework.web.authorization.access.ParamContext;
import org.hswebframework.web.commons.entity.Entity;
import org.hswebframework.web.commons.entity.RecordCreationEntity;
import org.hswebframework.web.commons.entity.param.QueryParamEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -64,6 +65,11 @@ public class DefaultFieldAccessController implements FieldAccessController {
} catch (Exception e) {
}
}
if (entity instanceof RecordCreationEntity) {
RecordCreationEntity creationEntity = ((RecordCreationEntity) entity);
creationEntity.setCreateTime(null);
creationEntity.setCreatorId(null);
}
} else {
logger.warn("doUpdateAccess skip ,because can not found any entity in param!");
}

View File

@@ -17,6 +17,7 @@
package org.hswebframework.web.controller;
import org.hswebframework.web.authorization.Permission;
import org.hswebframework.web.authorization.annotation.Authorize;
import org.hswebframework.web.controller.message.ResponseMessage;
import org.hswebframework.web.logging.AccessLogger;
@@ -39,13 +40,13 @@ import static org.hswebframework.web.controller.message.ResponseMessage.ok;
* @author zhouhao
* @since 3.0
*/
public interface CreateController<E, PK> {
public interface CreateController<E, PK> {
InsertService<E, PK> getService();
@Authorize(action = "add")
@Authorize(action = Permission.ACTION_ADD)
@PostMapping
@AccessLogger("添加数据")
@AccessLogger("{action_add}")
@ResponseStatus(HttpStatus.CREATED)
default ResponseMessage add(@RequestBody E data) {
return ok(getService().insert(data));

View File

@@ -17,6 +17,7 @@
package org.hswebframework.web.controller;
import org.hswebframework.web.authorization.Permission;
import org.hswebframework.web.authorization.annotation.Authorize;
import org.hswebframework.web.controller.message.ResponseMessage;
import org.hswebframework.web.logging.AccessLogger;
@@ -35,9 +36,9 @@ public interface DeleteController<PK> {
DeleteService<PK> getService();
@Authorize(action = "delete")
@Authorize(action = Permission.ACTION_DELETE)
@DeleteMapping(path = "/{id}")
@AccessLogger("根据主键删除数据")
@AccessLogger("{delete_by_primary_key}")
default ResponseMessage deleteByPrimaryKey(@PathVariable PK id) {
return ok(getService().deleteByPk(id));
}

View File

@@ -17,6 +17,7 @@
package org.hswebframework.web.controller;
import org.hswebframework.web.authorization.Permission;
import org.hswebframework.web.authorization.annotation.Authorize;
import org.hswebframework.web.commons.entity.GenericEntity;
import org.hswebframework.web.controller.message.ResponseMessage;
@@ -37,9 +38,9 @@ public interface GenericEntityUpdateController<E extends GenericEntity<PK>, PK>
UpdateService<E> getService();
@Authorize(action = "update")
@Authorize(action = Permission.ACTION_UPDATE)
@PutMapping(path = "/{id}")
@AccessLogger("根据主键修改数据")
@AccessLogger("{update_by_primary_key}")
default ResponseMessage updateByPrimaryKey(@PathVariable PK id, @RequestBody E data) {
data.setId(id);
return ResponseMessage.ok(getService().updateByPk(data));

View File

@@ -17,6 +17,7 @@
package org.hswebframework.web.controller;
import org.hswebframework.web.authorization.Permission;
import org.hswebframework.web.authorization.annotation.Authorize;
import org.hswebframework.web.commons.entity.Entity;
import org.hswebframework.web.commons.entity.param.QueryParamEntity;
@@ -39,7 +40,7 @@ import static org.hswebframework.web.controller.message.ResponseMessage.ok;
* @see QueryParamEntity
* @see 3.0
*/
public interface QueryController<E, PK, Q extends Entity> {
public interface QueryController<E, PK, Q extends Entity> {
/**
* 获取实现了{@link QueryByEntityService}和{@link QueryService}的服务类
@@ -59,16 +60,16 @@ public interface QueryController<E, PK, Q extends Entity> {
* @param param 参数
* @return 查询结果
*/
@Authorize(action = "read")
@Authorize(action = Permission.ACTION_QUERY)
@GetMapping
@AccessLogger("根据条件查询")
@AccessLogger("{dynamic_query}")
default ResponseMessage list(Q param) {
return ok(getService().selectPager(param));
}
@Authorize(action = "read")
@Authorize(action = Permission.ACTION_GET)
@GetMapping(path = "/{id}")
@AccessLogger("根据主键查询")
@AccessLogger("{get_by_id}")
default ResponseMessage getByPrimaryKey(@PathVariable PK id) {
return ok(getService().selectByPk(id));
}

View File

@@ -17,6 +17,7 @@
package org.hswebframework.web.controller;
import org.hswebframework.web.authorization.Permission;
import org.hswebframework.web.authorization.annotation.Authorize;
import org.hswebframework.web.controller.message.ResponseMessage;
import org.hswebframework.web.logging.AccessLogger;
@@ -30,8 +31,8 @@ import org.springframework.web.bind.annotation.RequestBody;
* @author zhouhao
*/
public interface UpdateController<E, PK> {
@Authorize(action = "update")
@Authorize(action = Permission.ACTION_UPDATE)
@PutMapping(path = "/{id}")
@AccessLogger("根据主键修改数据")
@AccessLogger("{update_by_primary_key}")
ResponseMessage updateByPrimaryKey(@PathVariable PK id, @RequestBody E data);
}

View File

@@ -28,23 +28,46 @@ import java.util.*;
/**
* 响应消息。controller中处理后返回此对象响应请求结果给客户端。
*/
public class ResponseMessage implements Serializable {
public class ResponseMessage extends LinkedHashMap<String, Object> implements Serializable {
private static final long serialVersionUID = 8992436576262574064L;
/**
* 反馈数据
*/
private Object data;
public static ResponseMessage error(String message) {
return error(500, message);
}
/**
* 反馈信息
*/
private String message;
public static ResponseMessage error(int status, String message) {
ResponseMessage msg = new ResponseMessage();
msg.put("message", message);
msg.put("status", status);
return msg.putTimeStamp();
}
/**
* 响应码
*/
private int code;
public static ResponseMessage ok() {
return ok(null);
}
private ResponseMessage putTimeStamp() {
put("timestamp", System.currentTimeMillis());
return this;
}
public static ResponseMessage ok(Object data) {
return new ResponseMessage()
.data(data)
.putTimeStamp()
.status(200);
}
public ResponseMessage and(String key, Object value) {
put(key, value);
return this;
}
public ResponseMessage data(Object data) {
if (data != null)
put("data", data);
return this;
}
/**
* 过滤字段:指定需要序列化的字段
@@ -56,24 +79,9 @@ public class ResponseMessage implements Serializable {
*/
private transient Map<Class<?>, Set<String>> excludes;
private transient boolean onlyData;
private transient String callback;
public Map<String, Object> toMap() {
Map<String, Object> map = new HashMap<>();
if (data != null)
map.put("data", this.getData());
if (message != null)
map.put("message", this.getMessage());
map.put("code", this.getCode());
return map;
}
protected ResponseMessage() {
}
public ResponseMessage include(Class<?> type, String... fields) {
return include(type, Arrays.asList(fields));
}
@@ -125,7 +133,7 @@ public class ResponseMessage implements Serializable {
excludes = new HashMap<>();
if (fields == null || fields.isEmpty()) return this;
Class type;
if (data != null) type = data.getClass();
if (getData() != null) type = getData().getClass();
else return this;
exclude(type, fields);
return this;
@@ -136,7 +144,7 @@ public class ResponseMessage implements Serializable {
includes = new HashMap<>();
if (fields == null || fields.isEmpty()) return this;
Class type;
if (data != null) type = data.getClass();
if (getData() != null) type = getData().getClass();
else return this;
include(type, fields);
return this;
@@ -159,32 +167,20 @@ public class ResponseMessage implements Serializable {
}
public Object getData() {
return data;
return get("data");
}
public ResponseMessage setData(Object data) {
this.data = data;
return this;
}
@Override
public String toString() {
return JSON.toJSONStringWithDateFormat(this, "yyyy-MM-dd HH:mm:ss");
}
public int getCode() {
return code;
}
public ResponseMessage setCode(int code) {
this.code = code;
public ResponseMessage status(int status) {
put("status", status);
return this;
}
public static ResponseMessage fromJson(String json) {
return JSON.parseObject(json, ResponseMessage.class);
}
public Map<Class<?>, Set<String>> getExcludes() {
return excludes;
}
@@ -193,63 +189,4 @@ public class ResponseMessage implements Serializable {
return includes;
}
public ResponseMessage onlyData() {
setOnlyData(true);
return this;
}
public void setOnlyData(boolean onlyData) {
this.onlyData = onlyData;
}
public boolean isOnlyData() {
return onlyData;
}
public ResponseMessage callback(String callback) {
this.callback = callback;
return this;
}
public String getCallback() {
return callback;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public static ResponseMessage ok() {
return ok(null);
}
public static ResponseMessage ok(Object data) {
ResponseMessage message = new ResponseMessage();
message.setCode(200);
message.setData(data);
return message;
}
public static ResponseMessage created(Object data) {
ResponseMessage message = new ResponseMessage();
message.setCode(201);
message.setData(data);
return message;
}
public static ResponseMessage error(String message) {
return error(message, 500);
}
public static ResponseMessage error(String message, int code) {
ResponseMessage response = new ResponseMessage();
response.setCode(code);
response.setMessage(message);
return response;
}
}

View File

@@ -35,8 +35,8 @@ import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.*;
import org.springframework.jdbc.datasource.DataSourceUtils;
import javax.sql.DataSource;

View File

@@ -13,6 +13,7 @@ import org.hswebframework.web.commons.entity.Entity;
import org.hswebframework.web.commons.entity.PagerResult;
import org.hswebframework.web.commons.entity.param.QueryParamEntity;
import org.hswebframework.web.controller.QueryController;
import org.hswebframework.web.controller.authorization.UserController;
import org.hswebframework.web.controller.message.ResponseMessage;
import org.hswebframework.web.entity.authorization.SimpleUserEntity;
import org.hswebframework.web.entity.authorization.UserEntity;
@@ -30,6 +31,7 @@ import java.util.List;
*/
@RestController
@Authorize(permission = "test")
@RequestMapping("/test")
public class TestController implements QueryController<UserEntity, String, QueryParamEntity> {
@GetMapping("/test1")
@@ -61,6 +63,9 @@ public class TestController implements QueryController<UserEntity, String, Query
return ResponseMessage.ok(entity);
}
public static void main(String[] args) throws NoSuchMethodException {
System.out.println(UserController.class.getMethod("list", Entity.class));
}
@Override
public TestService getService() {

View File

@@ -1,6 +1,7 @@
spring:
aop:
auto: true
proxy-target-class: true
datasource:
url : jdbc:h2:mem:examples
username : sa
@@ -10,7 +11,4 @@ spring:
hsweb:
app:
name: hsweb示例
version: 3.0.0
authorize:
filters:
user/**: user:*
version: 3.0.0

View File

@@ -23,6 +23,7 @@ import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface AccessLogger {
String value();

View File

@@ -56,8 +56,8 @@ public class HswebAutoConfiguration {
converter.setFeatures(
SerializerFeature.WriteNullListAsEmpty,
SerializerFeature.WriteNullNumberAsZero,
SerializerFeature.WriteNullBooleanAsFalse,
SerializerFeature.WriteDateUseDateFormat
SerializerFeature.WriteNullBooleanAsFalse
// SerializerFeature.WriteDateUseDateFormat
);
converter.setEntityFactory(entityFactory);
return converter;

View File

@@ -39,14 +39,14 @@ public class RestControllerExceptionTranslator {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
ResponseMessage handleException(ValidationException exception) {
return ResponseMessage.error(exception.getMessage(), 400);
return ResponseMessage.error(400, exception.getMessage());
}
@ExceptionHandler(org.hsweb.ezorm.rdb.exception.ValidationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
ResponseMessage handleException(org.hsweb.ezorm.rdb.exception.ValidationException exception) {
return ResponseMessage.error(JSON.toJSONString(exception.getValidateResult()), 400);
return ResponseMessage.error(400, exception.getMessage()).data(exception.getValidateResult());
}
@ExceptionHandler(BusinessException.class)
@@ -56,21 +56,21 @@ public class RestControllerExceptionTranslator {
if (exception.getCause() != null) {
logger.error("{}:{}", exception.getMessage(), exception.getStatus(), exception.getCause());
}
return ResponseMessage.error(exception.getMessage(), exception.getStatus());
return ResponseMessage.error(exception.getStatus(), exception.getMessage());
}
@ExceptionHandler(AuthorizeException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@ResponseBody
ResponseMessage handleException(AuthorizeException exception) {
return ResponseMessage.error(exception.getMessage(), exception.getStatus());
return ResponseMessage.error(exception.getStatus(), exception.getMessage());
}
@ExceptionHandler(AuthorizeForbiddenException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
@ResponseBody
ResponseMessage handleException(AuthorizeForbiddenException exception) {
return ResponseMessage.error(exception.getMessage(), exception.getStatus());
return ResponseMessage.error(exception.getStatus(), exception.getMessage());
}
@@ -78,7 +78,7 @@ public class RestControllerExceptionTranslator {
@ResponseStatus(HttpStatus.NOT_FOUND)
@ResponseBody
ResponseMessage handleException(NotFoundException exception) {
return ResponseMessage.error(exception.getMessage(), 404);
return ResponseMessage.error(404, exception.getMessage());
}
// @ExceptionHandler(Throwable.class)

View File

@@ -1,6 +1,7 @@
package org.hswebframework.web.starter.convert;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONPObject;
import com.alibaba.fastjson.serializer.PropertyPreFilter;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;
@@ -109,13 +110,7 @@ public class FastJsonHttpMessageConverter extends AbstractHttpMessageConverter<O
String callback = ThreadLocalUtils.getAndRemove("jsonp-callback");
if (obj instanceof ResponseMessage) {
ResponseMessage message = (ResponseMessage) obj;
if (message.getCode() == 200 && message.isOnlyData())
obj = message.getData();
if (obj instanceof String)
text = ((String) obj);
else
text = JSON.toJSONString(obj, parseFilter(message), features);
if (callback == null) callback = message.getCallback();
text = JSON.toJSONString(obj, parseFilter(message), features);
} else {
text = JSON.toJSONString(obj, features);
}

View File

@@ -17,6 +17,7 @@
package org.hswebframework.web.controller.authorization;
import org.hswebframework.web.authorization.Permission;
import org.hswebframework.web.authorization.annotation.AuthInfo;
import org.hswebframework.web.authorization.Authorization;
import org.hswebframework.web.authorization.annotation.Authorize;
@@ -28,6 +29,7 @@ import org.hswebframework.web.entity.authorization.UserEntity;
import org.hswebframework.web.logging.AccessLogger;
import org.hswebframework.web.service.authorization.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import static org.hswebframework.web.controller.message.ResponseMessage.*;
@@ -41,6 +43,7 @@ import static org.hswebframework.web.controller.message.ResponseMessage.*;
@RequestMapping("${hsweb.web.mappings.user:user}")
@Authorize(permission = "user")
@AccessLogger("用户管理")
@Transactional
public class UserController implements QueryController<UserEntity, String, QueryParamEntity>, CreateController<UserEntity, String> {
private UserService userService;
@@ -64,16 +67,16 @@ public class UserController implements QueryController<UserEntity, String, Query
@Authorize(action = "update")
@PutMapping(path = "/{id}")
@AccessLogger("根据主键修改数据")
@AccessLogger("{update_by_primary_key}")
public ResponseMessage updateByPrimaryKey(@PathVariable String id, @RequestBody UserEntity data) {
data.setId(id);
getService().update(data);
return ok();
}
@Authorize
@Authorize(merge = false)
@PutMapping(path = "/password")
@AccessLogger("修改当前用户密码")
@AccessLogger("{update_password_login_user}")
public ResponseMessage updateLoginUserPassword(@AuthInfo Authorization authorization,
@RequestParam String password,
@RequestParam String oldPassword) {
@@ -83,7 +86,7 @@ public class UserController implements QueryController<UserEntity, String, Query
@Authorize(action = "update")
@PutMapping(path = "/password/{id}")
@AccessLogger("修改密码")
@AccessLogger("{update_password_by_id}")
public ResponseMessage updateByPasswordPrimaryKey(@PathVariable String id,
@RequestParam String password,
@RequestParam String oldPassword) {
@@ -93,14 +96,14 @@ public class UserController implements QueryController<UserEntity, String, Query
@Authorize(action = "enable")
@PutMapping(path = "/{id}/enable")
@AccessLogger("启用用户")
@AccessLogger("{enable_user}")
public ResponseMessage enable(@PathVariable String id) {
return ok(getService().enable(id));
}
@Authorize(action = "disable")
@PutMapping(path = "/{id}/disable")
@AccessLogger("禁用用户")
@AccessLogger("{disable_user}")
public ResponseMessage disable(@PathVariable String id) {
return ok(getService().disable(id));
}