最近一个项目的认证是用shiro实现的,以前没有做过spring boot + shiro的整合,所以遇到了一些奇奇怪怪的坑
shiro的整合具体这边就不做阐述了,大概有以下几个类:
- JwtFilter
- ShiroConfig
- JwtRealm
- JwtToken
这里需要关注的只有前两个类,自定义的filter:JwtFilter,以及Shiro配置类
ShiroConfig.java
|  1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 |  @Bean("securityManager")
public DefaultWebSecurityManager getManager(@Autowired JwtRealm jwtRealm) {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    securityManager.setRealm(jwtRealm);
    DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
    DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
    defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
    subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
    securityManager.setSubjectDAO(subjectDAO);
    return securityManager;
}
@Bean("shiroFilter")
public ShiroFilterFactoryBean factory(DefaultWebSecurityManager securityManager) {
    ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
    Map<String, Filter> filterMap = new LinkedHashMap<>();
    filterMap.put(SHIRO_JWT_FILTER_NAME, new JwtFilter());
    factoryBean.setFilters(filterMap);
    factoryBean.setSecurityManager(securityManager);
    Map<String, String> filterRuleMap = new LinkedHashMap<>();
    filterRuleMap.put("/auth/token", "anon");
    filterRuleMap.put("/v2/api-docs", "anon");
    filterRuleMap.put("/swagger-resources/**", "anon");
    filterRuleMap.put("/api/**", "anon");
    filterRuleMap.put("/**", SHIRO_JWT_FILTER_NAME);
    factoryBean.setFilterChainDefinitionMap(filterRuleMap);
    return factoryBean;
}
 | 
 
anon失效
因为需要在自定义filter中注入一些属性,所以把JwtFilter交给Spring管理
| 1
2
3
4
 | @Bean
public JwtFilter jwtFilter() {
    return new JwtFilter();
}
 | 
 
同时修改shiroFilter中的filterMap设置
| 1
 | filterMap.put(SHIRO_JWT_FILTER_NAME, jwtFilter());
 | 
 
但是在修改后,filterChainDefinitionMap中设置为anon的路由规则就失效了,所有的接口请求都会走JwtFilter,而不是先走ShiroFilter。
原因:
在将JwtFilter交给Spring管理后,Spring将其注册到filterChain中了,与ShiroFilter同级,所以即使设置了filter的order,在shiroFilter完了之后也会经过JwtFilter,从而导致认证请求调用链的异常
解决方法:
- 不要把JwtFilter交由Spring管理,直接new一个实例,交由ShiroFilterFactoryBean管理。这样可以保证JwtFilter和ShiroFilter的父子关系,保证filter调用链的正确性
- 但是这样的话,在JwtFilter中就不能进行依赖注入,只能获取applicationContext来获取对应的bean,比较麻烦
 
- 依然把JwtFilter交由Spring管理,但是设置这个bean不要注册到filter调用链中
第一个方法只需要把filterMap中JwtFilter获取方式改回new,同时将该类中依赖注入的地方改用获取Spring上下文从而获取bean的方式,这个方式比较麻烦。
第二个方法则是通过FilterRegistrationBean取消JwtFilter的自动注册,参考 文档
| 1
2
3
4
5
6
7
8
 | @Bean
public FilterRegistrationBean registerJwtFilter(@Autowired JwtFilter jwtFilter) {
    // 设置jwt filter不自动注册到spring管理的监听器中,防止与shiro filter同级,导致该监听器必定执行
    FilterRegistrationBean<JwtFilter> jwtFilterRegister = new FilterRegistrationBean<>(jwtFilter);
    jwtFilterRegister.setEnabled(false);
    return jwtFilterRegister;
}
 | 
 
小结
Spring Boot整合Shiro很方便,但是还是有一些注意点的,甚至有的问题还是与Spring的加载机制有关系