【问题标题】:Is it possible to run a spring webmvc webapp with spring security in one spring container?是否可以在一个弹簧容器中运行具有弹簧安全性的弹簧 webmvc webapp?
【发布时间】:2013-09-09 21:44:53
【问题描述】:

这是我的场景:我有一个 spring webapp(使用 webmvc),我刚刚添加了 spring 安全性。我有一个 CRUD 用户管理器页面,允许具有足够权限的用户添加用户。在服务器上,这是由 com.myapp.UserController 类处理的。我也有自己的 org.springframework.security.core.userdetails.UserDetailsS​​ervice 实现,称为 com.myapp.UserDetailsS​​erviceImpl。 UserController 和我的 UserDetailsS​​ervice impl 都使用 com.myapp.UserService 来跟踪 java.util.List 中的用户。

我发现,当我通过 Web 界面创建一个新用户,注销,然后尝试使用该新用户重新登录时,找不到该用户。仔细观察,这是因为 com.myapp.UserService 有两个不同的实例。 UserController 使用的有新用户,而 UserDetailsS​​erviceImpl 使用的没有。

我将原因归结为当我启动这个 webapp 时运行了两个不同的 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.UserDetailsS​​erviceImpl 的相关部分:

@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    @Qualifier("testUserService")
    private UserService userService;

    ...
}

还有我的用户控制器:

@Controller
@RequestMapping("/admin/users")
public class UserController {

    @Autowired
    @Qualifier("testUserService")
    private UserService userService;

    ...
}

我的问题是:有没有办法将 webmvc 和安全性结合到一个 spring 容器中,这样我就不会创建重复的 UserService 实例?

【问题讨论】:

    标签: java spring security spring-mvc spring-security


    【解决方案1】:

    简答

    从 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 中配置的 bean 仅对子应用程序上下文可见,如果您想从根应用程序上下文访问它们,请使用选项 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>
    

    【讨论】:

    • 感谢 Sergi,您的回答有效。我将 servlet 配置中的 contextConfigLocation 设置为空,并将 myapp-servlet.xml 和 myapp-security.xml 添加到全局 contextConfigLocation。
    【解决方案2】:

    Sergi 的回答是正确的。为了完整起见,这是我的工作 web.xml:

    <?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>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value></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-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>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-09-08
      • 2023-01-03
      • 2017-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多