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

一、定义自定义注解

/**
 * 自定义注解
 * 如果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();  
  
    /** 
     * 功能点名称
     */  
    String name();  
    
    /**
     * 是否需要进行权限验证
     */
    boolean is_validate();
    
    /**
     * 功能点或者功能集描述
     */
    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);
   
    return new ModelAndView(page_path + "list.jsp"); 
    }
    
    /**
* 查询列表
* @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<AccessMode> list(int page, int rows, AccessMode record) throws IOException{
    PageHelper.startPage(page, rows);
    record.setIsDel(0); //查询未删除的
    List<AccessMode> list = AccessModeService.selectListByCondition(record);
     
        PageInfo<AccessMode> pg = new PageInfo<AccessMode>(list);
        return pg;
}
    
    /**
     * 跳转到添加页面
     */
    @RequestLimit(fun_assembly_url = "", desc = "跳转到添加页面", is_validate = true, name = "添加", function_name = "toAdd")
    @RequestMapping("/toAdd")
    public ModelAndView toAdd(){
    return new ModelAndView(page_path + "add.jsp"); 
    }

}

三、重新定义拦截器,在拦截器中队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; //菜单权限

    /**  
     * preHandle方法是进行处理器拦截用的,顾名思义,该方法将在Controller处理之前进行调用,SpringMVC中的Interceptor拦截器是链式的,可以同时存在  
     * 多个Interceptor,然后SpringMVC会根据声明的前后顺序一个接一个的执行,而且所有的Interceptor中的preHandle方法都会在  
     * Controller方法调用之前调用。SpringMVC的这种Interceptor链式结构也是可以进行中断的,这种中断方式是令preHandle的返  
     * 回值为false,当preHandle的返回值为false的时候整个请求就结束了。  
     */    
    @Override  
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)  
            throws Exception { 
    if (handler instanceof HandlerMethod) {
    HandlerMethod myHandlerMethod = (HandlerMethod) handler;
    Method method = myHandlerMethod.getMethod();
    @SuppressWarnings("unchecked")
Class<AccessModeController> type = (Class<AccessModeController>) myHandlerMethod.getBeanType(); //对应的controller方法
    RequestLimit requestLimit = method.getAnnotation(RequestLimit.class); //功能点对应的请求方法
   
       if (requestLimit == null) {
           // 如果注解为null, 说明不需要拦截, 直接放过
                return true;
            }
       
            if (requestLimit.is_validate()) {//需要进行权限判断
            //项目访问路径
        String basePath = request.getScheme()+"://"+request.getServerName();
    if(request.getServerPort()!=80){
    basePath+=":"+request.getServerPort();
    }
    basePath += ((HttpServletRequest) request).getContextPath() +"/";
   
                // 如果权限配置不为空, 则取出配置值
            String action = requestLimit.function_name(); //功能点路径
            String fun_url = requestLimit.fun_assembly_url(); //controller方法路径
           
            if(StringUtils.isBlank(fun_url)){//功能点对应注解上的功能集路径为空
            RequestLimit parent = type.getAnnotation(RequestLimit.class); //controller的注解
            fun_url = parent.fun_assembly_url(); //controller方法中注解上的功能集的路径
            }
           
            HttpSession session = request.getSession();
           
            if (BasePopedomService == null) {//解决service为null无法注入问题 
                    System.out.println("BasePopedomService is null!!!"); 
                    BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext()); 
                    BasePopedomService = (BasePopedomService) factory.getBean("BasePopedomService"); 
                 } 
           
            boolean is_validate = false; //是否有权限
    JSONArray basePopedomList = BasePopedomService.selectListByMenuUrl(session, fun_url); //菜单对应的按钮列表
    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;
}
}
}
   
    if(is_validate){//按钮具有权限
    return true;
    }else{//按钮没有权限
    response.setContentType("text/html");
    String noPermissionsPage = basePath + "manager/login/error?msg=MSG_NO_PERMISSIONS"; //返回登陆首页的方法
    response.sendRedirect(noPermissionsPage);
   
    return false;
    }
            }
        }
        return true;// 只有返回true才会继续向下执行,返回false取消当前请求  
    }  
  
    /**  
     * 这个方法只会在当前这个Interceptor的preHandle方法返回值为true的时候才会执行。postHandle是进行处理器拦截用的,它的执行时间是在处理器进行处理之  
     * 后,也就是在Controller的方法调用之后执行,但是它会在DispatcherServlet进行视图的渲染之前执行,也就是说在这个方法中你可以对ModelAndView进行操  
     * 作。这个方法的链式结构跟正常访问的方向是相反的,也就是说先声明的Interceptor拦截器该方法反而会后调用,这跟Struts2里面的拦截器的执行过程有点像,  
     * 只是Struts2里面的intercept方法中要手动的调用ActionInvocation的invoke方法,Struts2中调用ActionInvocation的invoke方法就是调用下一个Interceptor  
     * 或者是调用action,然后要在Interceptor之前调用的内容都写在调用invoke之前,要在Interceptor之后调用的内容都写在调用invoke方法之后。  
     */    
    @Override  
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,  
                           ModelAndView modelAndView) throws Exception {  
       // System.out.println(">>>MyInterceptor1>>>>>>>请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)");  
    String url = request.getRequestURI();
    if(response.getStatus()==500){  
    //modelAndView.addObject("msg", Constants.MSG_500);
    String ticket = request.getQueryString(); //返回的ticket
    if(StringUtils.isNotBlank(ticket)){//有tikect,说明是该url不准许登陆
    modelAndView.setViewName(page_path + "login_no_permissions.jsp");  
    }else{//请求出错
    modelAndView.setViewName(page_path + "500.jsp");  
    }
    //modelAndView.addObject("msg", Constants.MSG_500);
        }else if(response.getStatus()==404 && (url.contains("plugins") && url.contains(".map"))){//IE8下报找不到.map错误  
    //modelAndView.addObject("msg", Constants.MSG_404);
    modelAndView.setViewName(page_path + "404.jsp");  
        } 
    //modelAndView.setViewName(page_path + "/pages/error.jsp");  
    }  
  
    /**  
     * 该方法也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行。该方法将在整个请求完成之后,也就是DispatcherServlet渲染了视图执行,  
     * 这个方法的主要作用是用于清理资源的,当然这个方法也只能在当前这个Interceptor的preHandle方法的返回值为true时才会执行。  
     */    
    @Override  
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)  
            throws Exception {  
       // System.out.println(">>>MyInterceptor1>>>>>>>在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)");  
    }  
}  

四、在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);  
    }  

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

扫描二维码关注公众号,回复: 2414696 查看本文章

}

猜你喜欢

转载自blog.csdn.net/shunhua19881987/article/details/78094701