今天在使用Spring Boot集成Shiro时出现了无法注入Service的问题,解决后来记录一下。

问题现象:

ShiroRealm中进行身份验证,要将登陆模块的Service注入进来进行验证,但是其值为null。

1
2
3
4
5
6
7
public class ShiroRealm extends AuthorizingRealm {

@Autowired
ILoginService loginService; // <---这里值为null

...
}

问题原因:

  1. ShiroRelam属于filter即过滤器,它在Spring未完成注入bean之前就已经拦截了,因此无法注入。
  2. 对于SpringBoot,没有将ShiroRealm注入Bean

Spring MVC 解决办法:

对于Spring MVC,解决办法很简单,直接将Spring MVCweb.xml中提到shiro之前即可:

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
37
38
39
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">

...

<!-- Spring MVC 前端控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

...

<!-- Shiro拦截器 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
</web-app>

Spring Boot 解决办法:

ShiroConfig在注入SecurityManagersetRealm()的参数ShiroRealm不能自己new出来,而要先将其注入Bean,然后调用(这里是个坑,很多人其实挂在这一步)。

错误写法:

1
2
3
4
5
6
7
8
9
10
11
@Configuration
public class ShiroConfig {
...
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(new shiroRealm());
return manager;
}
...
}

正确写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Configuration
public class ShiroConfig {
...
/**
* 注入ShiroRealm
* 不能省略,会导致Service无法注入
*/
@Bean
public ShiroRealm myShiroRealm() {
return new ShiroRealm();
}

@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(myShiroRealm());
return manager;
}
...
}

终极大杀器:

如果上面的方法使用后还是不能注入Service,那么试试下面这样做(这种方法适用于在普通类中注入Service层):

创建一个SpringBeanFactoryUtils,用于获取Bean,记得添加@Component注解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
* @author jitwxs
* @date 2018/3/20 14:22
*/
@Component
public class SpringBeanFactoryUtils implements ApplicationContextAware {
private static ApplicationContext context = null;

public static <T> T getBean(Class<T> type) {
return context.getBean(type);
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringBeanFactoryUtils.context == null) {
SpringBeanFactoryUtils.context = applicationContext;
}
}
}

后面使用的时候,使用getBean()方法即可:

1
2
3
if (loginService == null) {
loginService = SpringBeanFactoryUtils.getBean(ILoginService.class);
}