添加自定义过滤器 Apache Shiro + Spring 启动
Adding Custom filter Apache Shiro + Spring Boot
我正在尝试将基于 Spring MVC xml 的项目配置重构为基于 Spring Boot java 的配置。设置shiro配置时如下:
@Configuration
public class ShiroConfig {
@Bean
public Realm realm() {
JdbcRealm myRealm = new JdbcRealm();
myRealm.setCredentialsMatcher(sha256Matcher());
myRealm.setPermissionsLookupEnabled(true);
myRealm.setSaltStyle(JdbcRealm.SaltStyle.COLUMN);
return myRealm;
}
@Bean
public HashedCredentialsMatcher sha256Matcher() {
HashedCredentialsMatcher sha256Matcher = new HashedCredentialsMatcher();
sha256Matcher.setHashAlgorithmName("SHA-256");
return sha256Matcher;
}
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public CacheManager cacheManager() {
return new MemoryConstrainedCacheManager();
}
@Bean
public Filter jwtv() {
return new JWTVerifyingFilter();
}
@Bean
public Filter ljwtv() {
return new LimitedAccessJWTVerifyingFilter();
}
@Bean
public Filter logout() {
LogoutFilter logoutFilter = new LogoutFilter();
logoutFilter.setRedirectUrl("/login.jsp");
return logoutFilter;
}
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
chainDefinition.addPathDefinition("/login", "authc");
chainDefinition.addPathDefinition("/logout", "logout");
chainDefinition.addPathDefinition("/my/test/**", "anon");
chainDefinition.addPathDefinition("/my/xyz/**/abc", "ljwtv"); chainDefinition.addPathDefinition("/my/xyz/**/mno", "ljwtv");
chainDefinition.addPathDefinition("/my/**", "jwtv");
return chainDefinition;
}
}
我遇到以下错误:
Error starting Tomcat context. Exception:
org.springframework.beans.factory.BeanCreationException. Message:
Error creating bean with name 'filterShiroFilterRegistrationBean'
defined in class path resource
[org/apache/shiro/spring/config/web/autoconfigure/ShiroWebFilterConfiguration.class]:
Bean instantiation via factory method failed; nested exception is
org.springframework.beans.BeanInstantiationException: Failed to
instantiate
[org.springframework.boot.web.servlet.FilterRegistrationBean]: Factory
method 'filterShiroFilterRegistrationBean' threw exception; nested
exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'shiroFilterFactoryBean': FactoryBean
threw exception on object creation; nested exception is
java.lang.IllegalArgumentException: There is no filter with name
'jwtv' to apply to chain [/my/**] in the pool of available Filters.
Ensure a filter with that name/path has first been registered with the
addFilter method(s).
自定义文件管理器 JWTVerifyingFilter
是一个扩展 org.apache.shiro.web.filter.AccessControlFilter
的 @Component
。不知道我错过了什么,这一切都适用于 xml 配置。请帮忙。
看起来 Boot 集成没有从 Spring 上下文中添加过滤器。 (预引导/Java 配置,这通常在 XML 中定义,不是问题)。
虽然解决方法非常简单,只需添加这个 bean def:
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Value("#{ @environment['shiro.loginUrl'] ?: '/login.jsp' }") String loginUrl,
@Value("#{ @environment['shiro.successUrl'] ?: '/' }") String successUrl,
@Value("#{ @environment['shiro.unauthorizedUrl'] ?: null }") String unauthorizedUrl,
SecurityManager securityManager,
ShiroFilterChainDefinition shiroFilterChainDefinition,
Map<String, Filter> filterMap) {
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
filterFactoryBean.setLoginUrl(loginUrl);
filterFactoryBean.setSuccessUrl(successUrl);
filterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
filterFactoryBean.setSecurityManager(securityManager);
filterFactoryBean.setFilterChainDefinitionMap(shiroFilterChainDefinition.getFilterChainMap());
filterFactoryBean.setFilters(filterMap);
return filterFactoryBean;
}
我还创建了一个 PR 以将其添加回 Shiro(一旦我们添加测试)。
更新:
我认为您的 JSP 处理配置不正确,但重定向问题是由您对注销过滤器的定义引起的。每个请求都会处理该过滤器(Spring)。您需要使用映射到路径的 FitlerRegistrationBean
来代替:
@Bean
public FilterRegistrationBean logout() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
LogoutFilter logoutFilter = new LogoutFilter();
logoutFilter.setRedirectUrl("/login.jsp");
filterRegistrationBean.setFilter(logoutFilter);
filterRegistrationBean.addUrlPatterns("/logout.htm");
return filterRegistrationBean;
}
更新:自动过滤器注册更有趣
我认为这两个问题的根源是 Spring 自动添加了过滤器,然后 Shiro 也使用了过滤器。
如果您定义过滤器 bean,并使用 FilterRegistrationBean
禁用它,Spring 不会自动添加过滤器 (IIRC)。这意味着 Shiro 可以管理过滤器链(这是我们想要发生的)
使用以下代码段:
```
@豆
public 过滤器 jwtv() {
return 新的 JWTVerifyingFilter();
}
@Bean
public FilterRegistrationBean jwtvFilterRegBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(jwtv());
filterRegistrationBean.setEnabled(false);
return filterRegistrationBean;
}
```
我在点击 /
时触发了你的过滤器:
HTTP/1.1 403
Content-Length: 0
Date: Thu, 27 Sep 2018 20:01:26 GMT
message: NO TOKEN!
但在击中 /test
时不会。
对于任何尝试实施自定义过滤器(如 JWT 令牌化授权)的人:
Sample Project
我正在尝试将基于 Spring MVC xml 的项目配置重构为基于 Spring Boot java 的配置。设置shiro配置时如下:
@Configuration
public class ShiroConfig {
@Bean
public Realm realm() {
JdbcRealm myRealm = new JdbcRealm();
myRealm.setCredentialsMatcher(sha256Matcher());
myRealm.setPermissionsLookupEnabled(true);
myRealm.setSaltStyle(JdbcRealm.SaltStyle.COLUMN);
return myRealm;
}
@Bean
public HashedCredentialsMatcher sha256Matcher() {
HashedCredentialsMatcher sha256Matcher = new HashedCredentialsMatcher();
sha256Matcher.setHashAlgorithmName("SHA-256");
return sha256Matcher;
}
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public CacheManager cacheManager() {
return new MemoryConstrainedCacheManager();
}
@Bean
public Filter jwtv() {
return new JWTVerifyingFilter();
}
@Bean
public Filter ljwtv() {
return new LimitedAccessJWTVerifyingFilter();
}
@Bean
public Filter logout() {
LogoutFilter logoutFilter = new LogoutFilter();
logoutFilter.setRedirectUrl("/login.jsp");
return logoutFilter;
}
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
chainDefinition.addPathDefinition("/login", "authc");
chainDefinition.addPathDefinition("/logout", "logout");
chainDefinition.addPathDefinition("/my/test/**", "anon");
chainDefinition.addPathDefinition("/my/xyz/**/abc", "ljwtv"); chainDefinition.addPathDefinition("/my/xyz/**/mno", "ljwtv");
chainDefinition.addPathDefinition("/my/**", "jwtv");
return chainDefinition;
}
}
我遇到以下错误:
Error starting Tomcat context. Exception: org.springframework.beans.factory.BeanCreationException. Message: Error creating bean with name 'filterShiroFilterRegistrationBean' defined in class path resource [org/apache/shiro/spring/config/web/autoconfigure/ShiroWebFilterConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.web.servlet.FilterRegistrationBean]: Factory method 'filterShiroFilterRegistrationBean' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'shiroFilterFactoryBean': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalArgumentException: There is no filter with name 'jwtv' to apply to chain [/my/**] in the pool of available Filters. Ensure a filter with that name/path has first been registered with the addFilter method(s).
自定义文件管理器 JWTVerifyingFilter
是一个扩展 org.apache.shiro.web.filter.AccessControlFilter
的 @Component
。不知道我错过了什么,这一切都适用于 xml 配置。请帮忙。
看起来 Boot 集成没有从 Spring 上下文中添加过滤器。 (预引导/Java 配置,这通常在 XML 中定义,不是问题)。
虽然解决方法非常简单,只需添加这个 bean def:
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Value("#{ @environment['shiro.loginUrl'] ?: '/login.jsp' }") String loginUrl,
@Value("#{ @environment['shiro.successUrl'] ?: '/' }") String successUrl,
@Value("#{ @environment['shiro.unauthorizedUrl'] ?: null }") String unauthorizedUrl,
SecurityManager securityManager,
ShiroFilterChainDefinition shiroFilterChainDefinition,
Map<String, Filter> filterMap) {
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
filterFactoryBean.setLoginUrl(loginUrl);
filterFactoryBean.setSuccessUrl(successUrl);
filterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
filterFactoryBean.setSecurityManager(securityManager);
filterFactoryBean.setFilterChainDefinitionMap(shiroFilterChainDefinition.getFilterChainMap());
filterFactoryBean.setFilters(filterMap);
return filterFactoryBean;
}
我还创建了一个 PR 以将其添加回 Shiro(一旦我们添加测试)。
更新:
我认为您的 JSP 处理配置不正确,但重定向问题是由您对注销过滤器的定义引起的。每个请求都会处理该过滤器(Spring)。您需要使用映射到路径的 FitlerRegistrationBean
来代替:
@Bean
public FilterRegistrationBean logout() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
LogoutFilter logoutFilter = new LogoutFilter();
logoutFilter.setRedirectUrl("/login.jsp");
filterRegistrationBean.setFilter(logoutFilter);
filterRegistrationBean.addUrlPatterns("/logout.htm");
return filterRegistrationBean;
}
更新:自动过滤器注册更有趣
我认为这两个问题的根源是 Spring 自动添加了过滤器,然后 Shiro 也使用了过滤器。
如果您定义过滤器 bean,并使用 FilterRegistrationBean
禁用它,Spring 不会自动添加过滤器 (IIRC)。这意味着 Shiro 可以管理过滤器链(这是我们想要发生的)
使用以下代码段: ``` @豆 public 过滤器 jwtv() { return 新的 JWTVerifyingFilter(); }
@Bean
public FilterRegistrationBean jwtvFilterRegBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(jwtv());
filterRegistrationBean.setEnabled(false);
return filterRegistrationBean;
}
```
我在点击 /
时触发了你的过滤器:
HTTP/1.1 403
Content-Length: 0
Date: Thu, 27 Sep 2018 20:01:26 GMT
message: NO TOKEN!
但在击中 /test
时不会。
对于任何尝试实施自定义过滤器(如 JWT 令牌化授权)的人: Sample Project