简体   繁体   中英

RestEASY, Spring Security, Spring Session Integration

Has anyone successfully integrated RestEASY with Spring Security and Spring Session? I am running into problems with the ContextLoadListener. I was able to integrate RestEASY and Spring Security with the following web.xml.

<web-app>
<display-name>Admin Service</display-name>
<context-param>
    <param-name>resteasy.servlet.mapping.prefix</param-name>
    <param-value>/admin</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>
<listener>
    <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>
<listener>
    <listener-class>org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class>
</listener>
<servlet>
    <servlet-name>AdminService</servlet-name>
    <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
    <init-param>
        <param-name>javax.ws.rs.Application</param-name>
        <param-value>com.etouchpoint.admin.service.AdminApplication</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>AdminService</servlet-name>
    <url-pattern>/admin/*</url-pattern>
</servlet-mapping>

After reading the documentation for Spring Session, the web.xml would end up looking something like this:

<web-app>
<display-name>Admin Service</display-name>

<!-- Context for Spring HttpSession -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/session.xml</param-value>
</context-param>

<!-- Context for RestEasy -->
<context-param>
    <param-name>resteasy.servlet.mapping.prefix</param-name>
    <param-value>/admin</param-value>
</context-param>

<!-- Filter and Mapping for Spring Session -->
<filter>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<!-- Filter and Mapping for Spring Security -->
<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>

<!-- Listener for Spring Session -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- Listeners for RestEasy -->
<listener>
    <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>
<listener>
    <listener-class>org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class>
</listener>

<servlet>
    <servlet-name>AdminService</servlet-name>
    <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
    <init-param>
        <param-name>javax.ws.rs.Application</param-name>
        <param-value>com.etouchpoint.admin.service.AdminApplication</param-value>
    </init-param>
</servlet>

<servlet-mapping>
    <servlet-name>AdminService</servlet-name>
    <url-pattern>/admin/*</url-pattern>
</servlet-mapping>

The problem with this configuration is that there are 2 ContextLoadListeners which is not allowed. So, then I tried creating a Class which would stem from Spring Security and add Spring Session and RestEASY Context Listeners to it.

Session Configuration:

@Configuration
@EnableJdbcHttpSession
public class SessionConfig {
    @Bean
    public PlatformTransactionManager transactionManager(final DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }}

ContextLoadListener:

public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {

public SecurityInitializer() {
    super(SecurityConfig.class, SessionConfig.class, SpringContextLoaderListener.class);
}}

This doesn't work either. You end with this exception:

java.lang.NoSuchMethodException: org.springframework.security.access.SecurityConfig.<init>()

I am at a loss, at the moment, of how to get these 3 libraries to play nicely. Has anyone done this? What was the general solution? Do you move everything into Java, or are you able to do it in XML?

Finally figured it out!!

web.xml

Couple of things to note here.

  • This config is for Servlet 3.0 containers. So follow some of the Restesy set up for Servlet 3.0 Containers
  • Use the org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap and org.jboss.resteasy.plugins.spring.SpringContextLoaderListener even though a Servlet 3.0 container is used. Do not use the org.springframework.web.context.ContextLoaderListener. See the Spring Integration for Resteasy .
<?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"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">

    <display-name>Admin</display-name>

    <!-- Filter for Spring Session -->
    <filter>
        <filter-name>springSessionRepositoryFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSessionRepositoryFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>ERROR</dispatcher>
    </filter-mapping>

    <!-- Filter for Spring Security -->
    <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>

    <!-- Listener for Resteasy -->
    <listener>
        <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
    </listener>
    <listener>
        <listener-class>org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class>
    </listener>

</web-app>

applicationContext.xml

<beans
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">

    <!-- This context will contain all of the spring configs -->
    <import resource="classpath:admin-context.xml" />

    <!-- This context will contain all of the spring session/security configs -->
    <import resource="classpath:admin-security-context.xml" />

</beans>

admin-security-context.xml

<beans
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:security="http://www.springframework.org/schema/security"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">

    <context:annotation-config/>

    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="dataSource"/>
    </bean>

    <bean class="....CustomJdbcHttpSessionConfiguration" />

    <bean id="adminAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler">
        <property name="defaultTargetUrl" value="/index.html" />
        <property name="alwaysUseDefaultTargetUrl" value="true" />
        <property name="useReferer" value="true" />
    </bean>

    <bean id="adminLoginFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
        <property name="defaultFailureUrl" value="/login.html" />
        <property name="forwardToDestination" value="true" />
    </bean>

    <security:http pattern="/static/**" security="none" />
    <security:http pattern="/favicon.ico" security="none" />
    <security:http pattern="/robots.txt" security="none" />

    <security:http>
        <security:csrf disabled="true"/>

        <security:intercept-url pattern="/login.html" access="hasAnyRole('ANONYMOUS')" requires-channel="any" />
        <security:intercept-url pattern="/login" access="hasAnyRole('ANONYMOUS')" requires-channel="any" />

        <security:intercept-url pattern="/**" access="hasAnyRole('ADMIN')" requires-channel="any" />

        <!-- All of these parameters are needed for login to work correctly -->    
        <security:form-login login-page="/login.html" login-processing-url="/login" authentication-success-handler-ref="adminAuthenticationSuccessHandler" authentication-failure-handler-ref="adminLoginFailureHandler" username-parameter="username" password-parameter="password"/>

        <!-- Change cookie name to 'SESSION' because that is what is used with Spring Session -->
        <!-- And all parameters are needed -->
        <security:logout logout-url="/logout" invalidate-session="true" delete-cookies="SESSION" logout-success-url="/login.html" />

    </security:http>

    <security:authentication-manager>
        <security:authentication-provider user-service-ref="userDetailsService">
            <security:password-encoder hash="sha" />
        </security:authentication-provider>
    </security:authentication-manager>

</beans>

CustomJdbcHttpSessionConfiguration.java

Creating a custom object here so there can be multiple apps deployed and all of them use the same cookie. Be careful here because this sets the cookie to '/' which means if there needs to be different domains for cookies, this custom object will override the domains.

public class CustomJdbcHttpSessionConfiguration extends JdbcHttpSessionConfiguration {

    @Bean
    public CookieSerializer cookieSerializer() {

        final DefaultCookieSerializer serializer = new DefaultCookieSerializer();
        serializer.setCookieName("SESSION"); // <1>
        serializer.setCookiePath("/"); // <2>
        serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$"); // <3>

        return serializer;
    }

}

AdminApplication.java

@ApplicationPath("service")
public class AdminApplication extends Application {

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM