记录下开发中的奇葩问题(一):Java的Properties读取乱序问题引发Shiro授权异常
使用Shiro控制资源的访问权限一般会采用JavaConfig的方式在代码中实现,示例如下:
Map<String, String> map = new LinkedHashMap<>(); // 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 map.put("/install", "anon"); map.put("/install/step?", "anon"); map.put("/login", "anon"); map.put("/password/reset", "anon"); map.put("/search", "anon"); map.put("/register", "anon"); map.put("/index", "anon"); map.put("/", "anon"); map.put("/assets/**", "anon"); map.put("/bundles/**", "anon"); map.put("/install/**", "anon"); map.put("/themes/**", "anon"); // authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问 // all other paths require a logged in user map.put("/logout", "logout"); map.put("/**", "authc"); return map;
注意其中的注释说明,以及Map的实现类型,要求给出的过滤链必须是有顺序的。
近日突发奇想,让这些配置使用properties的方式在外部配置即节省了代码编码,又方便移植使用,于是代码修改为如下:
Map<String, String> map = new LinkedHashMap<>(); try { Properties properties = PropertiesLoaderUtils.loadProperties(new ClassPathResource(filterChainMappingLocation)); properties.forEach((k, v) -> map.put(String.valueOf(k), String.valueOf(v)); } catch (IOException e) { logger.warn("Can not location Shiro filter chain path definition mapping file: " + filterChainMappingLocation); } return map;
程序和原来一样正常运行,但发现页面显示的样式不正常了,JavaScript脚本也屡屡报错,这是原来的程序没有出现的情况,经过仔细对页面源代码进行检查发现,程序对原来CSS和JavaScript的资源文件进行了保护不允许访问了,这就奇怪了,在Shiro的配置中已经将这些资源文件开放了,允放匿名访问,问题出在哪了?
继续查看源代码和经过思考终于发现修改后的过滤链顺序发现了改变,特别是/**这样的大范围权限控制必须按顺序放在最后。而顺序改变的原因是读取properties本身就不是按属性文件中保存的顺序获得,于是上网上找到了如下的几篇文章进行了修改,十分感谢作者的贡献,特别记录一下:
[@PropertySource存入map属性 并解决乱序问题](https://blog.csdn.net/qq_42784606/article/details/118800925)
[实现对properties文件的顺序读出,并顺序写入](https://blog.csdn.net/flyfish778/article/details/12653595)
[在sprinboot中使用@PropertySource注解读取properties文件存放到map中导致顺序错乱的问题](https://blog.csdn.net/u012722296/article/details/107709638)
通过修改,创建了一个新的有顺序的属性类LinkedHashMapProperties,使用此属性类获取Shiro滤过器配置
Map<String, String> map = new LinkedHashMap<>(); try { Properties properties = new LinkedHashMapProperties(); PropertiesLoaderUtils.fillProperties(properties, new ClassPathResource(filterChainMappingLocation)); properties.forEach((k, v) -> map.put(String.valueOf(k), String.valueOf(v)); } catch (IOException e) { logger.warn("Can not location Shiro filter chain path definition mapping file: " + filterChainMappingLocation); } return map;
结果发现问题依然存在,还有哪的问题没有解决?
思考了许久,终于想到原来是流处理也是乱序的,不能使用properties的forEach方法,只能用过程式的顺序编程,好在数据量不大,不用考虑性能问题,最终代码修改如下:
Map<String, String> map = new LinkedHashMap<>(); try { Properties properties = new LinkedHashMapProperties(); PropertiesLoaderUtils.fillProperties(properties, new ClassPathResource(filterChainMappingLocation)); Set<String> keys = properties.stringPropertyNames(); for (String key : keys) { map.put(key, properties.getProperty(key)); } } catch (IOException e) { logger.warn("Can not location Shiro filter chain path definition mapping file: " + filterChainMappingLocation); } return map;
问题终于解决了,但是浪费了许多时间,问题也是一波几折,最终还是语言的基础和细节深入得不够,编程中存在大量的细节问题导致程序运行和结果不正确,有些可能是致命的,因此要花费更多时间去掌握和解决这些奇葩问题。