#场景 为了敏捷开发,作者在平台中把dao、service、甚至controller三层基本的增删改查都抽象到顶层,这样在一个新模块中只要有了这三层就拥有了增删改查的功能,结合代码生成器大大提升了开发效率,但是在这种情况下,shiro自带的权限验证_@RequiresPermissions_注解就显得有点力不从心了。
#方案 1、采用自定义注解将资源权限分成两块,一块是功能模块注解(@PermissionModule),一块是资源操作注解(@PermissionOperate),这样在抽象出去的controller层面就可以只在@PermissionOperate()定义可访问的资源权限名称,在实际的功能模块类中定义模块名称,两块拼在一起即一个完整的资源权限
代码如下:
/**
* use for:资源模块
* Created by javahao on 2017/2/20.
* auth:JavaHao
*/
@Documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionModule {
String value() default "";
}
/**
* use for:资源
* Created by javahao on 2017/2/20.
* auth:JavaHao
*/
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionOperate {
/**
* 资源
* @return
*/
String[] value() default "";
/**
* 关系
* @return
*/
Relation relation() default Relation.AND;
}
/**
* use for:关系
* Created by javahao on 2017/2/24.
* auth:JavaHao
*/
public enum Relation {
OR,AND;
}
2、使用AOP在添加了指定注解的方法加切入点,这样在访问指定资源之前,先进行权限校验,如果验证通过则继续访问,如果验证失败则跳转越权页面。
代码如下:
@Aspect
@Component
public class PermissionAop {
@Before(value="@annotation(operate)")
public void permission(JoinPoint jp,PermissionOperate operate) throws Throwable{
Class aspectClz = jp.getTarget().getClass();
if(aspectClz.isAnnotationPresent(PermissionModule.class)){
PermissionModule module = (PermissionModule) aspectClz.getAnnotation(PermissionModule.class);
String[] operates = operate.value();
String[] checks = new String[operates.length];
for(int i = 0 ; i < operates.length ; i++){
checks[i] = module.value()+":"+operates[i];
}
if(!UserUtil.isPermitted(operate.relation(),checks))
throw new UnauthorizedException("用户:["+UserUtil.getName()+"]正在越权访问资源:"+ ArraysUtil.joinStringArray(Arrays.asList(operates),","));
}
}
}
/**
* use for:检测到请求未授权回调
* Created by javahao on 2017/2/24.
* auth:JavaHao
*/
@ControllerAdvice
public class UnAuthorizationHandler {
@ExceptionHandler({UnauthorizedException.class})
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public ModelAndView processUnauthenticatedException(NativeWebRequest request, UnauthorizedException e) {
ModelAndView mv = new ModelAndView();
mv.addObject("exception", e);
mv.setViewName("unauthorized");
return mv;
}
}
注:
此处作者遇到的难点在aop的@Before地方,初始写法是
@Before(value="@annotation()")
public void permission(JoinPoint jp) throws Throwable{
}
大家可能注意到了变化@annotation()和方法无参数,这种情况下,如果想要获取到完整的权限标示是比较困难的,作者尝试过N种方法都没能成功这篇博客虽然说得很详细,但是按照博主的方法测试之后,始终还是获取到的代理方法,无法获取到切面的注解。 最终作者在这篇博客中受到启发
@Before(value="@annotation(operate)")
public void permission(JoinPoint jp,PermissionOperate operate) throws Throwable{
......
}
还有一点资源权限验证是作者扣的shiro的标签源码,大家需要“自扣”即可
附赠一段使用aop的列选择代码
@AfterReturning(pointcut="within(com..*) && @annotation(columnSelect)",returning="modelAndView")
public void getCurrentDictColumn(Object modelAndView,ColumnSelect columnSelect) throws Exception{
//加入列选择
DictColumns dictColumns = new DictColumns();
Module module = new Module();
module.setId(columnSelect.moduleId());
String accountId=CurrentProperties.getCurrentAccountCache().getAccount().getId();
dictColumns.setAccountId(accountId);
dictColumns.setModule(module);
List dictColumnsList=dictColumnsService.listDictColumnsByAccount(dictColumns);
// HttpServletResponse response = CurrentProperties.getCurrentResponse();
// response.setCharacterEncoding("UTF-8");
// PrintWriter printWriter = response.getWriter();
ModelMap modelMap = ((ModelAndView)modelAndView).getModelMap();
modelMap.put("dictColumnsList",dictColumnsList);
modelMap.put("module",module);
/*JsonConfig jsonConfig = new JsonConfig();
//排除不需要进行转换的属性
jsonConfig.setExcludes(new String[] { "beanClass","prefix","callback","propertyEditorClass","propertyType","propertyValue","propertyValues","readMethod","writeMethod","writeMethodForActualAccess","writeMethodParameter"});
//防止自包含形成死循环
jsonConfig.setCycleDetectionStrategy(CycleDetectionStrategy.LENIENT);
// 当输出时间格式时,采用和JS兼容的格式输出
//注:JsDateJsonBeanProcessor 是json-lib已经提供的类,我们也可以实现自己的JsonBeanProcessor
jsonConfig.registerJsonBeanProcessor(Date.class,new JsDateJsonBeanProcessor());
JSONObject json = JSONObject.fromObject(modelMap,jsonConfig);
printWriter.print(json);
printWriter.close();*/
}
更详细的aop讲解 请看这里 共同学习,共同进步!