这是我的场景:我有一个 spring web 应用程序(使用 webmvc),我刚刚添加了 spring security。我有一个 CRUD 用户管理器页面,允许具有足够权限的用户添加用户。在服务器上,这是由 com.myapp.UserController 类处理的。我还有自己的 org.springframework.security.core.userdetails.UserDetailsService 实现,称为 com.myapp.UserDetailsServiceImpl。 UserController 和我的 UserDetailsService impl 都使用 com.myapp.UserService 来跟踪 java.util.List 中的用户。
我发现,当我通过网络界面创建新用户、注销并尝试使用该新用户重新登录时,找不到该用户。仔细观察,这是因为 com.myapp.UserService 有两个不同的实例。 UserController使用的有新用户,而UserDetailsServiceImpl使用的没有。
我追踪原因是当我启动这个 web 应用程序时运行了两个不同的 spring 容器。一种用于 webmvc (myapp-servlet.xml),一种用于安全性 (myapp-security.xml)。每个都有自己的 UserService 实例。据我了解,这是配置 spring webmvc 安全性的一个非常常见的场景:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<servlet>
<servlet-name>myapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myapp</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/myapp-servlet.xml, /WEB-INF/myapp-security.xml</param-value>
</context-param>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
在我的 myapp-servlet.xml 中,我正在进行组件扫描,它会获取 UserService,因为它用 @Service 进行注释:
<context:component-scan base-package="com.myapp" />
但是 UserService 也被 myapp-security.xml 获取,因为它被引用为我的身份验证管理器配置的一部分:
<authentication-manager>
<authentication-provider user-service-ref="userDetailsService" />
</authentication-manager>
为了完整性,以下是 com.myapp.UserDetailsServiceImpl 的相关部分:
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
@Qualifier("testUserService")
private UserService userService;
...
}
还有我的 UserController:
@Controller
@RequestMapping("/admin/users")
public class UserController {
@Autowired
@Qualifier("testUserService")
private UserService userService;
...
}
我的问题是:有没有一种方法可以将 webmvc 和安全性合并到一个 spring 容器中,这样我就不会创建重复的 UserService 实例?
最佳答案
简短回答
从 contextConfigLocation 上下文参数中删除 /WEB-INF/myapp-servlet.xml。
长答案
ContextLoaderListener 根据 contextConfigLocation 中定义的配置文件创建根应用程序上下文,并在初始化任何 Servlet 之前将其加载到 ServletContext 中。
DispatcherServlet 同时将创建具有指定配置的子应用程序上下文。您没有显式指定任何 bean 定义文件,因此按照惯例,它将采用/WEB-INF/appName-servlet.xml (在您的情况下为/WEB-INF/myapp-servlet.xml ,它偶然存在)。碰巧您的根应用程序上下文和子应用程序上下文都会有一些通用的 bean(重复,因为它们加载相同的配置文件)。
您有两个选择:
1) 从 contextConfigLocation 中删除/WEB-INF/myapp-servlet.xml。另外,如果您想更明确,请将配置添加到 ServletDispatcher 声明中:
<servlet>
<servlet-name>myapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/myapp-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myapp</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/myapp-security.xml</param-value>
</context-param>
注意:myapp-servlet.xml 中配置的 beans 仅对子应用程序上下文可见,如果您想从根应用程序上下文访问它们,请使用选项 2 或重新组织配置文件。
2) 加载根应用程序上下文(您当前的配置)中的所有 bean,并在调度程序 servlet 中添加空配置参数。
<servlet>
<servlet-name>myapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
关于java - 是否可以在一个 spring 容器中运行带有 spring security 的 spring webmvc webapp?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18646801/