spring boot 自定义注解实现权限验证

太过爱你忘了你带给我的痛 2022-06-08 12:56 467阅读 0赞

一、定义自定义注解

/**
* 自定义注解
* 如果Controller 有该标记,那么这个Controller下面所有的方法都会被过滤器进行验证
* 如果Controller 没有有该标记,但Controller中的某个方法拥有该标记,那么这个方法将被过滤器验证(其他没有被标记的不会被验证)
* @author Chen,Shunhua
* @date 2017年9月21日 下午3:09:11
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE}) //该注解修饰类中的方法,准许controller和子方法添加本注解
//@Target(ElementType.METHOD) //该注解修饰类中的方法,只准许子方法添加本注解
@Order(Ordered.HIGHEST_PRECEDENCE) //最高优先级
public @interface RequestLimit {
/**
* controller对应的访问路径
* 功能集的路径
*/
String fun_assembly_url();
/**
* 功能点的路径
*/
String function_name();

  1. /\*\*
  2. \* 功能点名称
  3. \*/
  4. String name();
  5. /\*\*
  6. \* 是否需要进行权限验证
  7. \*/
  8. boolean is\_validate();
  9. /\*\*
  10. \* 功能点或者功能集描述
  11. \*/
  12. String desc();

}

二、在需要进行权限验证的controller类的方法中添加自定义注解

/**
* 设备接入方式管理
* 如果AccessModeController类名上不添加@RequestLimit注解,则需要在每个method方法中添加功能集的路径
* @author Chen,Shunhua
* @date 2017年4月17日 下午3:24:23
*/
@Controller
@RequestLimit(fun_assembly_url = “/manager/AccessMode”, desc = “接入方式管理”, is_validate = false, name = “mapping”, function_name = “”)
@RequestMapping(“/manager/AccessMode”)
public class AccessModeController {
static Logger logger = LogManager.getLogger(AccessModeController.class.getName());

@Autowired
private AccessModeService AccessModeService;

@Autowired
private BasePopedomService BasePopedomService; //菜单权限

private String page_path = “/pages/demo/AccessMode/“; //页面所在目录

/**
* 跳转到list页面
*/
@RequestLimit(fun_assembly_url = “”, desc = “跳转到list页面”, is_validate = false, name = “默认显示界面”, function_name = “load”)
@RequestMapping(“/load”)
public ModelAndView load(String menuPathName, HttpSession session, HttpServletRequest request,
HttpServletResponse response, ModelMap modelMap) throws IOException{
String function_name = request.getRequestURI();
String fun_function_name = function_name.substring(0, function_name.lastIndexOf(“/“));
JSONArray basePopedomList = BasePopedomService.selectListByMenuUrl(request.getSession(), fun_function_name); //功能集对应的功能点列表
modelMap.put(“button_list”, basePopedomList);
modelMap.put(“menuPathName”, menuPathName);

  1. return new ModelAndView(page\_path + "list.jsp");
  2. \}
  3. /\*\*

* 查询列表
* @param typeName 类型名称
* @param typeCode 类型编码
* @param page 查询每页的条数
* @param rows 每页查询的条数
*/
@RequestLimit(fun_assembly_url = “”, desc = “分页查询”, is_validate = false, name = “查询列表”, function_name = “list”)
@RequestMapping(“/list”)
@ResponseBody
public PageInfo list(int page, int rows, AccessMode record) throws IOException{
PageHelper.startPage(page, rows);
record.setIsDel(0); //查询未删除的
List list = AccessModeService.selectListByCondition(record);

  1. PageInfo<AccessMode> pg = new PageInfo<AccessMode>(list);
  2. return pg;

}

  1. /\*\*
  2. \* 跳转到添加页面
  3. \*/
  4. @RequestLimit(fun\_assembly\_url = "", desc = "跳转到添加页面", is\_validate = true, name = "添加", function\_name = "toAdd")
  5. @RequestMapping("/toAdd")
  6. public ModelAndView toAdd()\{
  7. return new ModelAndView(page\_path + "add.jsp");
  8. \}

}

三、重新定义拦截器,在拦截器中队controller的权限进行验证

/**
* 自定义拦截器
* @author Chen,Shunhua
* @date 2017年6月19日 上午10:27:15
*/
@Controller
public class AuthenticationInterceptor implements HandlerInterceptor {
private String page_path = “/pages/error/“; //页面所在目录

//@Resource
@Autowired
private BasePopedomService BasePopedomService; //菜单权限

  1. /\*\*
  2. \* preHandle方法是进行处理器拦截用的,顾名思义,该方法将在Controller处理之前进行调用,SpringMVC中的Interceptor拦截器是链式的,可以同时存在
  3. \* 多个Interceptor,然后SpringMVC会根据声明的前后顺序一个接一个的执行,而且所有的Interceptor中的preHandle方法都会在
  4. \* Controller方法调用之前调用。SpringMVC的这种Interceptor链式结构也是可以进行中断的,这种中断方式是令preHandle的返
  5. \* 回值为false,当preHandle的返回值为false的时候整个请求就结束了。
  6. \*/
  7. @Override
  8. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
  9. throws Exception \{
  10. if (handler instanceof HandlerMethod) \{

HandlerMethod myHandlerMethod = (HandlerMethod) handler;
Method method = myHandlerMethod.getMethod();
@SuppressWarnings(“unchecked”)
Class type = (Class) myHandlerMethod.getBeanType(); //对应的controller方法
RequestLimit requestLimit = method.getAnnotation(RequestLimit.class); //功能点对应的请求方法

  1. if (requestLimit == null) \{
  2. // 如果注解为null, 说明不需要拦截, 直接放过
  3. return true;
  4. \}
  5. if (requestLimit.is\_validate()) \{//需要进行权限判断
  6. //项目访问路径
  7. String basePath = request.getScheme()+"://"+request.getServerName();
  8. if(request.getServerPort()!=80)\{
  9. basePath+=":"+request.getServerPort();
  10. \}
  11. basePath += ((HttpServletRequest) request).getContextPath() +"/";
  12. // 如果权限配置不为空, 则取出配置值
  13. String action = requestLimit.function\_name(); //功能点路径
  14. String fun\_url = requestLimit.fun\_assembly\_url(); //controller方法路径
  15. if(StringUtils.isBlank(fun\_url))\{//功能点对应注解上的功能集路径为空
  16. RequestLimit parent = type.getAnnotation(RequestLimit.class); //controller的注解
  17. fun\_url = parent.fun\_assembly\_url(); //controller方法中注解上的功能集的路径
  18. \}
  19. HttpSession session = request.getSession();
  20. if (BasePopedomService == null) \{//解决service为null无法注入问题
  21. System.out.println("BasePopedomService is null!!!");
  22. BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
  23. BasePopedomService = (BasePopedomService) factory.getBean("BasePopedomService");
  24. \}
  25. boolean is\_validate = false; //是否有权限
  26. JSONArray basePopedomList = BasePopedomService.selectListByMenuUrl(session, fun\_url); //菜单对应的按钮列表
  27. if(null != basePopedomList && basePopedomList.size() > 0)\{

action = action.toLowerCase(); //变成小写
for(int m=0; m<basePopedomList.size(); m++){
JSONObject son_temp = JSONObject.fromObject(basePopedomList.get(m));//获取数组第一个json的字符串 并转化成json对象
String son_mod_code = son_temp.getString(“function_name”); //功能点方法
son_mod_code = son_mod_code.toLowerCase();

if(action.contains(son_mod_code)){//有权限
is_validate = true;
break;
}
}
}

  1. if(is\_validate)\{//按钮具有权限
  2. return true;
  3. \}else\{//按钮没有权限
  4. response.setContentType("text/html");
  5. String noPermissionsPage = basePath + "manager/login/error?msg=MSG\_NO\_PERMISSIONS"; //返回登陆首页的方法
  6. response.sendRedirect(noPermissionsPage);
  7. return false;
  8. \}
  9. \}
  10. \}
  11. return true;// 只有返回true才会继续向下执行,返回false取消当前请求
  12. \}
  13. /\*\*
  14. \* 这个方法只会在当前这个InterceptorpreHandle方法返回值为true的时候才会执行。postHandle是进行处理器拦截用的,它的执行时间是在处理器进行处理之
  15. \* 后,也就是在Controller的方法调用之后执行,但是它会在DispatcherServlet进行视图的渲染之前执行,也就是说在这个方法中你可以对ModelAndView进行操
  16. \* 作。这个方法的链式结构跟正常访问的方向是相反的,也就是说先声明的Interceptor拦截器该方法反而会后调用,这跟Struts2里面的拦截器的执行过程有点像,
  17. \* 只是Struts2里面的intercept方法中要手动的调用ActionInvocationinvoke方法,Struts2中调用ActionInvocationinvoke方法就是调用下一个Interceptor
  18. \* 或者是调用action,然后要在Interceptor之前调用的内容都写在调用invoke之前,要在Interceptor之后调用的内容都写在调用invoke方法之后。
  19. \*/
  20. @Override
  21. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
  22. ModelAndView modelAndView) throws Exception \{
  23. // System.out.println(">>>MyInterceptor1>>>>>>>请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)");
  24. String url = request.getRequestURI();
  25. if(response.getStatus()==500)\{
  26. //modelAndView.addObject("msg", Constants.MSG\_500);
  27. String ticket = request.getQueryString(); //返回的ticket
  28. if(StringUtils.isNotBlank(ticket))\{//有tikect,说明是该url不准许登陆
  29. modelAndView.setViewName(page\_path + "login\_no\_permissions.jsp");
  30. \}else\{//请求出错
  31. modelAndView.setViewName(page\_path + "500.jsp");
  32. \}
  33. //modelAndView.addObject("msg", Constants.MSG\_500);
  34. \}else if(response.getStatus()==404 && (url.contains("plugins") && url.contains(".map")))\{//IE8下报找不到.map错误
  35. //modelAndView.addObject("msg", Constants.MSG\_404);
  36. modelAndView.setViewName(page\_path + "404.jsp");
  37. \}
  38. //modelAndView.setViewName(page\_path + "/pages/error.jsp");
  39. \}
  40. /\*\*
  41. \* 该方法也是需要当前对应的InterceptorpreHandle方法的返回值为true时才会执行。该方法将在整个请求完成之后,也就是DispatcherServlet渲染了视图执行,
  42. \* 这个方法的主要作用是用于清理资源的,当然这个方法也只能在当前这个InterceptorpreHandle方法的返回值为true时才会执行。
  43. \*/
  44. @Override
  45. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
  46. throws Exception \{
  47. // System.out.println(">>>MyInterceptor1>>>>>>>在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)");
  48. \}

}

四、在spring boot启动类中引入自定义的拦截器

public class Application extends WebMvcConfigurerAdapter{

/**
* 拦截器配置
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 多个拦截器组成一个拦截器链
// addPathPatterns 用于添加拦截规则
// excludePathPatterns 用户排除拦截
registry.addInterceptor(new AuthenticationInterceptor()).addPathPatterns(“/**“);
super.addInterceptors(registry);
}

  1. public static void main(String\[\] args) \{
  2. SpringApplication.run(Application.class, args);
  3. \}

}

发表评论

表情:
评论列表 (有 0 条评论,467人围观)

还没有评论,来说两句吧...

相关阅读