第一次使用aop开发后台权限,之前使用过aop开发过日志功能。
看了网上很多案例。感觉跟自己想要的还是有一定的区别,然后参照网上的案例,自己摸索写一个。
供大家参考。
1:首先,后台所有的功能菜单都放入数据库中。(用户列表,等这种功能菜单。)
2:后台所有的功能权限也放入到数据库中。(user:拥有用户列表权限,拥有用户列表查看,修改权限,没有删除,新增权限)
数据库要提前建立好。
开发代码:
创建操作枚举(增删改查)
/**
* 操作枚举
* 增(INSERT)
* 删(DELETE)
* 改(UPDATE)
* 查(SELECT)
* @author think
*/
public enum OperationEnum {
//0:默认可以使用该权限
//99:没有权限
INSERT("INSERT","0"),
DELETE("DELETE","0"),
UPDATE("UPDATE","0"),
SELECT("SELECT","0");
private String key;
private String value;
private OperationEnum(String key,String value) {
// TODO Auto-generated constructor stub
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
创建权限注解,默认只有查询功能。
/**
* 权限注解
* @author think
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Authority {
OperationEnum value() default OperationEnum.SELECT;
}
创建aop工具类
/**
* 解析权限注解
* @author think
*/
@Aspect
@Component
public class AnnotationParse {
@Autowired(required = false)
private HttpServletRequest request;
@Autowired
private ManagerPermissionsService managerPermissionsService;
//对应你的权限注解类,上面一个方法
@Pointcut("@annotation(com.mag.pro.pojo.aop.Authority)")
public void operationAuthority() {
}
//执行你的切面方法。
//这里面的权限逻辑没有,
//查询出用户有哪些权限,然后根据获得注解上的 操作权限来判断,该用户是否拥有权限操作。
//这里面有几个坑,
//1:ProceedingJoinPoint 请使用该类 2:一定要有返回值,不然报错搞死你。返回值object类型,不然搞死你。
//3:myExcpetion 是自定义异常,toBeanMap是自己写的方法,用于页面返回json格式,没有写?搞死你。。。
//4:PathKeyEnum 这个是自己定义的 枚举方法,主要存放 code msg entity。就是json格式
@Around("operationAuthority()")
public Object selectAuth(ProceedingJoinPoint joinPoint)throws Throwable{
MyException myException = null;
//获取controller参数集合,
Enumeration parameter = request.getParameterNames();
String users_only_md5 = getUsersId(parameter);
Object result = "";//aop环绕返回值专用
//当url传递的参数(users_only_id)为空时,则判断出系统异常
//本来该判断是在controller里面处理的。但是环绕通知会优先执行,导致这里需要进行判断约束
if(StringUtils.isNullString(users_only_md5)){
myException = new MyException(PathKeyEnum.ERROR.getKey(),PathKeyEnum.ERROR.getValue(),PathCommonEnum.ERROR.getValue());
return myException.toArgsUsersId();
}
//查询出用户所拥有的权限列表
List
//当用户权限查询出来为null时,则定义为该用户没有权限
if(StringUtils.isListNull(listManagerPermissions)){
myException = new MyException(PathKeyEnum.AUTHORITY.getKey(),PathKeyEnum.AUTHORITY.getValue(),PathCommonEnum.ERROR.getValue());
return myException.toBeanMap();
}
OperationEnum operationEnum = getControllerMethodDescription(joinPoint);
String controllerUrl = getUrl();
if(StringUtils.isNullString(controllerUrl)){
myException = new MyException(PathKeyEnum.AUTHORITY.getKey(),PathKeyEnum.AUTHORITY.getValue(),PathCommonEnum.ERROR.getValue());
return myException.toBeanMap();
}
try{
//listManagerPermissions 当这个集合不为空时,循环判断str跟集合里的权限是否有存在。
for (int i = 0; i < listManagerPermissions.size(); i++) {
if(listManagerPermissions.get(i).getFunction_permissions_controller().equals(controllerUrl)){
if("SELECT".equals(operationEnum.getKey())){
if(listManagerPermissions.get(i).getFunction_select() == Integer.parseInt(operationEnum.getValue())){
result = joinPoint.proceed();
break;
}else{
myException = new MyException(PathKeyEnum.AUTHORITY.getKey(),PathKeyEnum.AUTHORITY.getValue(),PathCommonEnum.ERROR.getValue());
return myException.toBeanMap();
}
}else if("UPDATE".equals(operationEnum.getKey())){
if(listManagerPermissions.get(i).getFunction_update() == Integer.parseInt(operationEnum.getValue())){
result = joinPoint.proceed();
break;
}else{
myException = new MyException(PathKeyEnum.AUTHORITY.getKey(),PathKeyEnum.AUTHORITY.getValue(),PathCommonEnum.ERROR.getValue());
return myException.toBeanMap();
}
}else if("DELETE".equals(operationEnum.getKey())){
if(listManagerPermissions.get(i).getFunction_delete() == Integer.parseInt(operationEnum.getValue())){
result = joinPoint.proceed();
break;
}else{
myException = new MyException(PathKeyEnum.AUTHORITY.getKey(),PathKeyEnum.AUTHORITY.getValue(),PathCommonEnum.ERROR.getValue());
return myException.toBeanMap();
}
}else if("INSERT".equals(operationEnum.getKey())){
if(listManagerPermissions.get(i).getFunction_add() == Integer.parseInt(operationEnum.getValue())){
result = joinPoint.proceed();
break;
}else{
myException = new MyException(PathKeyEnum.AUTHORITY.getKey(),PathKeyEnum.AUTHORITY.getValue(),PathCommonEnum.ERROR.getValue());
return myException.toBeanMap();
}
}
}
}
} catch (Exception e) {
// TODO: handle exception
}
if("" == result){
myException = new MyException(PathKeyEnum.AUTHORITY.getKey(),PathKeyEnum.AUTHORITY.getValue(),PathCommonEnum.ERROR.getValue());
return myException.toBeanMap();
}
return result;
}
/**
* 解析注解上的权限
* @param joinPoint
* @return
*/
public static OperationEnum getControllerMethodDescription(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Authority controllerAuthority = method.getAnnotation(Authority.class);
OperationEnum discription = controllerAuthority.value();
return discription;
}
//因为我们这边只需要用户的ID 所以取的id后退出循环
private String getUsersId(Enumeration parameter){
while(parameter.hasMoreElements()) {
String args = (String) parameter.nextElement();
if("users_only_md5".equals(args)){
return request.getParameter(args);
}
}
return PathCommonEnum.NULL.getValue();
}
/**
* 截取url的controller
* @return
*/
private String getUrl(){
String url = request.getRequestURI();
//url: /xxxx/xx/xxx.htmls
//我这边取的是第二个xx 即controller的mapping值,
//第一个xxxx应该是项目名称 所以下面的 截取需要根据自己的连接来调整值。
if(!StringUtils.isNullString(url)){
return url.substring(13,url.lastIndexOf("/"));
}
return null;
}
}
创建自定义异常类:
/**
* 自定义异常
* @author think
*/
public class MyException extends RuntimeException{
private String code;
private String msg;
private Object entity;
public MyException(String code, String msg,Object entity) {
// TODO Auto-generated constructor stub
this.code = code;
this.msg = msg;
this.entity = entity;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getEntity() {
return entity;
}
public void setEntity(Object entity) {
this.entity = entity;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return JsonMethod.setJsonMethod(code, msg, entity).toString();
}
/**
* 返回map格式
* @return
*/
public Map
return JsonMethod.setJsonMethod(code, msg, entity);
}
/**
* 返回map格式
* @return
*/
public Map
return JsonMethod.setJsonMethod(code, msg, entity);
}
创建常量枚举类:
/**
* 常量key枚举
* 枚举具有安全性,不可修改性,默认final
* @author boss 3
*/
public enum PathKeyEnum {
SUCCESS("20000","执行成功"),
ERROR("90000","系统繁忙,请稍后再试"),
LOGINE("90001","该环境无法登录后台管理系统"),
AUTHORITY("90003","权限不足");
private String key;
private String value;
/**
* 枚举必须使用私有化
* @param key
* @param value
*/
private PathKeyEnum(String key,String value) {
this.key = key;
this.value = value;
}
/** 获取key值 */
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
/** 获取对应的key的描述 */
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
下面就是controller类:
/**
* 查询所有功能菜单
* @param req
* @param res
* @return
*/
@Authority(OperationEnum.DELETE)//这个就是功能子权限,增删改查,这边我自己写的是测试
@OperLog(logDescription="查询所有功能菜单")//这个是系统日志aop 目前没有贴处来,可以删除掉
@RequestMapping(value="selectFunctionPermissions",method=RequestMethod.GET)
@ResponseBody
public Map
HttpServletResponse res){
res.setCharacterEncoding("UTF-8");
System.out.println("进入controller");
List
if(StringUtils.isListNull(listFunctionPermissions))
return JsonMethod.setJsonMethod(PathKeyEnum.ERROR.getKey(), PathKeyEnum.ERROR.getValue(), PathCommonEnum.ERROR.getValue());
return JsonMethod.setJsonMethod(PathKeyEnum.SUCCESS.getKey(), PathKeyEnum.SUCCESS.getValue(), listFunctionPermissions);
}
spring-mvc.xml配置环绕通知:
最后是测试的效果:
1:@Authority(OperationEnum.DELETE) 当我们将方法定义为delete时请求的效果:
因为数据库里没有值,所以返回无数据。
走了service方法,说明执行了数据库查询。
2:我们将delte改成其他权限,
@Authority(OperationEnum.SELECT)
因为aop判断的是delete,所以select是走异常,是没有权限的。
运行结果是:
以上就是我写的一个后台权限判断。需要在权限aop方法里,加入自己查询到的用户m来遍历判断。
目前aop代码没有优化,if else 三层嵌套,不太好,需要优化下。
(更新:请看底部优化方式)
附上数据库图
1:功能菜单表
2:用户权限表(0:启用 99:禁用)
controller请求的接口
参考该代码时,需要自己创建数据库,直接是运行不了的。
JsonMethod.setJsonMethod 这个方法是我自己封装的一个json格式,各位可以用自己的json格式就行。
还有自己的ssm框架没问题就行。需要框架,可以看我博客里面的融云即时聊天二有csdn下载链接。需要1积分。头疼。
以上已完结,本人运行没有任何问题。如有问题,可以留言。大家互相探讨,有好的建议,优化方法,也可以提出来,大家一起学习,本人小白在此献丑了!!!!
更新:权限if 嵌套循环优化
这里使用了状态设计模式来优化代码:
1:创建状态抽象类
/**
* 权限状态抽象类
* 使用状态设计模式
* @author think
*/
public abstract class ConcreteState {
/**
* 如果是查询,则返回查询对应的状态信息
*/
public abstract Object selectOperationEnum(List
OperationEnum operationEnum, ProceedingJoinPoint joinPoint, int i) throws Throwable;
/**
* 如果是修改,则返回修改对应的状态信息
*/
public abstract Object updateOperationEnum(List
OperationEnum operationEnum, ProceedingJoinPoint joinPoint, int i) throws Throwable;
/**
* 如果是新增,则返回新增对应的状态信息
*/
public abstract Object insertOperationEnum(List
OperationEnum operationEnum, ProceedingJoinPoint joinPoint, int i) throws Throwable;
/**
* 如果是删除,则返回删除对应的状态信息
*/
public abstract Object deleteOperationEnum(List
OperationEnum operationEnum, ProceedingJoinPoint joinPoint, int i) throws Throwable;
/**
* 如果是启用/禁用,则返回启用/禁用对应的状态信息
*/
public abstract Object disableOperationEnum(List
OperationEnum operationEnum, ProceedingJoinPoint joinPoint, int i) throws Throwable;
}
2:创建状态实现类
/**
* 权限状态实现类
* 使用状态设计模式
* @author think
*/
public class ConcreteStateImpl extends ConcreteState{
//默认返回map,无需修改
private final MyException myException = new MyException(PathKeyEnum.AUTHORITY.getKey(),PathKeyEnum.AUTHORITY.getValue(),PathCommonEnum.ERROR.getValue());
/**
* 如果是查询,则返回查询对应的状态信息
*/
@Override
public Object selectOperationEnum(List
OperationEnum operationEnum, ProceedingJoinPoint joinPoint, int i) throws Throwable{
// TODO Auto-generated method stub
if(listManagerPermissions.get(i).getFunction_select() == Integer.parseInt(operationEnum.getValue())){
return joinPoint.proceed();
} else {
return myException.toBeanMap();
}
}
/**
* 如果是修改,则返回修改对应的状态信息
*/
@Override
public Object updateOperationEnum(List
OperationEnum operationEnum, ProceedingJoinPoint joinPoint, int i) throws Throwable{
// TODO Auto-generated method stub
if(listManagerPermissions.get(i).getFunction_update() == Integer.parseInt(operationEnum.getValue())){
return joinPoint.proceed();
} else {
return myException.toBeanMap();
}
}
/**
* 如果是新增,则返回新增对应的状态信息
*/
@Override
public Object insertOperationEnum(List
OperationEnum operationEnum, ProceedingJoinPoint joinPoint, int i) throws Throwable{
// TODO Auto-generated method stub
if(listManagerPermissions.get(i).getFunction_delete() == Integer.parseInt(operationEnum.getValue())){
return joinPoint.proceed();
} else {
return myException.toBeanMap();
}
}
/**
* 如果是删除,则返回删除对应的状态信息
*/
@Override
public Object deleteOperationEnum(List
OperationEnum operationEnum, ProceedingJoinPoint joinPoint, int i) throws Throwable{
// TODO Auto-generated method stub
if(listManagerPermissions.get(i).getFunction_add() == Integer.parseInt(operationEnum.getValue())){
return joinPoint.proceed();
} else {
return myException.toBeanMap();
}
}
/**
* 如果是启用/禁用,则返回启用/禁用对应的状态信息
*/
@Override
public Object disableOperationEnum(List
ProceedingJoinPoint joinPoint, int i) throws Throwable {
// TODO Auto-generated method stub
if(listManagerPermissions.get(i).getFunction_disable() == Integer.parseInt(operationEnum.getValue())){
return joinPoint.proceed();
}else{
return myException.toBeanMap();
}
}
}
该方法在AnnotationParse 类里面写即可
3:创建调用实现类方法
/**
* 状态设计模式
* @param stateStr
* @param listManagerPermissions
* @param operationEnum
* @param joinPoint
* @param i
* @return
* @throws Throwable
*/
private Object stateUtils(String stateStr,List
OperationEnum operationEnum, ProceedingJoinPoint joinPoint, int i) throws Throwable{
ConcreteStateImpl concreteStateImpl = new ConcreteStateImpl();
switch (stateStr) {
case "SELECT":
return concreteStateImpl.selectOperationEnum(listManagerPermissions, operationEnum, joinPoint, i);
case "UPDATE":
return concreteStateImpl.updateOperationEnum(listManagerPermissions, operationEnum, joinPoint, i);
case "INSERT":
return concreteStateImpl.insertOperationEnum(listManagerPermissions, operationEnum, joinPoint, i);
case "DELETE":
return concreteStateImpl.deleteOperationEnum(listManagerPermissions, operationEnum, joinPoint, i);
case "DISABLE":
return concreteStateImpl.disableOperationEnum(listManagerPermissions, operationEnum, joinPoint, i);
default:
return null;
}
}
4:修改for循环里面的if代码
只需要这段代码。参数正常传递即可。
这样。if else if 代码优化已完成。并且已测试过。该代码优化运行正常。