拦截器之权限注解

接触了项目,在阅读代码时对其架构中拦截器的设计有很多值得记录的地方,在此记录一下。
对拦截器的理解:
在项目架构的搭建中,通过注册拦截器,可以减少很多重复代码,将请求拦截下来进行对应的操作,是一种AOP思想的运用。通过使用拦截器来对请求开始,执行过程,结束时都执行所指定的代码,实现业务与功能代码的解耦,让整个代码的结构更加优雅。

这里说说拦截器的应用之一:权限注解

在项目开发中想要设定权限,根据访问用户来判断其是否有权限进行操作。

  • 注解代码 @Permission
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Permission {

    String R_销售概览 = "销售概览";
    String R_门店管理 = "门店管理";
    String R_门店列表 = "门店列表";
    String R_ID卡列表 = "ID卡列表";
    String R_货柜管理 = "货柜管理";
    String R_货柜列表 = "货柜列表";
    String R_货架列表 = "货架列表";
    String R_日志管理 = "日志管理";
    String R_进货单列表 = "进货单列表";
    //。。。。此处可以防止各种权限,通过拦截用户操作,判断该用户是否有对应的权限,一般权限是记录再数据库中,通过查询用户权限字段来判断用户权限


    String A_查看 = "查看";
    String A_删除 = "删除";

    /**
     * 要检查的权限,包括Action,首字母小写的驼峰形式
     */
    String[] resuorce() default "";

    String[] actions() default A_查看;

}
  • 建立拦截器,此处需要实现HandlerInterceptor接口,这里实现了他的前置拦截方法
public class AdminInterceptor4User implements HandlerInterceptor {

    private static Logger logger = Logger.getLogger(AdminInterceptor4User.class);

    private SystemConfig systemConfig;

    private LsAdminUserViewService lsAdminUserViewService;

	//此处还用了单例模式
    public LsAdminUserViewService getAdminUserService(HttpServletRequest request) {
        if (lsAdminUserViewService == null) {
            synchronized (this) {
                logger.info("动态加载lsCompanyService Bean");
                BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
                lsAdminUserViewService = (LsAdminUserViewService) factory.getBean("lsAdminUserViewService");
            }
        }
        return lsAdminUserViewService;
    }

    public SystemConfig getSystemConfig(HttpServletRequest request) {
        if (systemConfig == null) {
            synchronized (this) {
                logger.info("动态加载SystemConfig Bean");
                BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
                systemConfig = (SystemConfig) factory.getBean("systemConfig");
            }
        }
        return systemConfig;
    }


    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws Exception {
        if ("OPTIONS".equals(request.getMethod())) {
            return true;
        }
        if ("GET".equals(request.getMethod())) {
            return true;
        }
        try {
            HandlerMethod methodHandle = (HandlerMethod) handler;
            preHandleDo(request, response, methodHandle);
        } catch (Exception ex) {
            logger.info("", ex);
            BaseResponse baseResponse = new CmsExceptionHandler().handle(ex);
            request.setAttribute("errmsg", baseResponse.getErrmsg());
            request.setAttribute("errcode", baseResponse.getErrcode());

            response.setStatus(HttpStatus.OK.value());
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            String h = response.getHeader("Access-Control-Allow-Origin");
            if (ValueHelper.isNone(h)) {
                String origin = request.getHeader("Origin");
                response.setHeader("Access-Control-Allow-Origin", origin);
                response.setHeader("Access-Control-Allow-Credentials", "true");
            }
            try {
                response.getWriter().write(JSONObject.toJSONString(baseResponse));
                response.getWriter().flush();
            } catch (Exception e) {
                e.printStackTrace();
            }

            return false;
        }

        return true;
    }


    void preHandleDo(HttpServletRequest request, HttpServletResponse response, HandlerMethod methodHandle) throws Exception {
        //获取请求的域名
        String referer = request.getHeader("Referer");
        if (referer == null) {
            if ("dev".equals(getSystemConfig(request).getProfile())) {
                referer = request.getParameter("Referer");
            }

            if (referer == null) {
                return;
            }
        }
        URL url = new URL(referer);
        String domain = "," + url.getHost() + ",";

        //获取用户信息:
        Long id = 0L;
        String tokenStr = request.getParameter(Constants.ADMIN_PARAM_TOKEN);
        if (tokenStr != null) {
            id = ValueHelper.tryParseLong(AESUtil.AESDecodeForWeb(tokenStr), 0L);
        }

        LsAdminUserView adminUser = null;
        if (id > 0) {
            adminUser = getAdminUserService(request).selectById(id);
            if (adminUser != null) {
                request.setAttribute(Constants.REQUEST_ADMIN_USER_KEY, adminUser);
                request.setAttribute(Constants.REQUEST_ADMIN_USER_ID_KEY, adminUser.getId());
            }
        }

		//此处来校验!!
        Permission permission = methodHandle.getMethodAnnotation(Permission.class);
        if (permission != null) {
            if (adminUser == null) {
                throw new OauthFailedException("用户信息验证失败!");
            }
            String jsonString = adminUser.getPermissionJson();
            JSONArray jsonArray = JSONObject.parseArray(jsonString);
            List<ResourcesPermission> permissionList = new ArrayList<>();
            getRealPermission(jsonArray, permissionList);

            Set<String> resource = Arrays.stream(permission.resuorce()).collect(Collectors.toSet());
            Set<String> set = Arrays.stream(permission.actions()).collect(Collectors.toSet());

            if (permissionList.stream().noneMatch(resourcesPermission -> {

                Set<String> temp = new HashSet<>(resourcesPermission.getActions());
                temp.retainAll(set);
                return resource.contains(resourcesPermission.getName()) && temp.size() != 0;
            })) {
                throw new NoPermissionException("您没有该权限");
            }
          
        }

    }


    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response, Object handler,
                                Exception ex)
            throws Exception {
    }

    public void getRealPermission(JSONArray jsonArray, List<ResourcesPermission> permissionList) {
        for (Object object : jsonArray) {
            JSONObject jsonObject = (JSONObject) object;
            JSONArray childArray = jsonObject.getJSONArray(ResourcesPermission.CHILDREN_KEY);
            if (childArray != null) {
                getRealPermission(childArray, permissionList);
            } else {
                permissionList.add(new ResourcesPermission(jsonObject));
            }
        }
    }
}
  • 注册拦截器到项目配置中,此处要继承WebMvcConfigurerAdapter这个虚类,这里参考的项目先创建了BaseWebMvcConfigurerAdapter来继承,再创建AppsServerApplication继承,由它来实现拦截器配置以及整个SpringBoot项目的启动。
public class BaseWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        FastJsonHttpMessageConverter fastJsonConverter = new FastJsonHttpMessageConverter();
        FastJsonConfig fjc = new FastJsonConfig();
        //1、序列化重点
        fjc.setSerializerFeatures(SerializerFeature.BrowserCompatible, SerializerFeature.DisableCircularReferenceDetect);
        fastJsonConverter.setFastJsonConfig(fjc);
        converters.add(fastJsonConverter);
    }
}
@SpringBootApplication
@ImportResource("classpath:META-INF/spring/applicationContext.xml") //不要用 *号 保证清晰的引用
public class AppsServerApplication extends BaseWebMvcConfigurerAdapter {

    private static Logger logger = Logger.getLogger(AppsServerApplication.class);

    public static boolean isStartUp = false;


    static public void main(String [] args) {
        isStartUp = false;
        logger.info(">>>>> huduo Apps Server 正在启动 <<<<<");
        SpringApplication.run(AppsServerApplication.class,args);
        logger.info(">>>>> huduo Apps Server 启动完成 <<<<<");
        isStartUp = true;
    }

    /**
     * 配置拦截器
     * @author yanhy
     * @param registry
     */
    public void addInterceptors(InterceptorRegistry registry) {
        //Admin 路径,官网的数据请求
        registry.addInterceptor(new AdminInterceptor()).addPathPatterns("/admin/**");
        registry.addInterceptor(new AdminInterceptor4User()).addPathPatterns("/admin/**");

        registry.addInterceptor(new AndroidInterceptor()).addPathPatterns("/android/**");
        registry.addInterceptor(new AndroidInterceptor4User()).addPathPatterns("/android/**");


        //租户拦截器
        registry.addInterceptor(new TenantInterceptor()).addPathPatterns("/tenant/**");


        registry.addInterceptor(new CommonInterceptor()).addPathPatterns("/common/**");
        registry.addInterceptor(new CommonInterceptor4User()).addPathPatterns("/common/**");

        //根据不同路径设置不同的拦截器
        registry.addInterceptor(new MiniProgramInterceptor()).addPathPatterns("/miniProgram/**");
        registry.addInterceptor(new MiniProgramInterceptor4User()).addPathPatterns("/miniProgram/**");
    }
}

总结

至此,基本的权限注解功能就实现了,以后可以通过这个模板来实现。
将拦截器注册后,以后都直接使用注解就可以实现,极大的简化了开发。
涉及的类或接口:
WebMvcConfigurerAdapterHandlerInterceptor