最近一个项目的认证是用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的加载机制有关系