Spring Security Login处理URL不可用

时间:2017-06-29 08:58:34

标签: spring spring-security jsf-2

我有一个项目,其中Spring与JSF一起使用(使用PrimeFaces)。到目前为止,我们已经为Spring Security使用了xml配置,但我的任务是将其移植到基于java的配置。

我现在已经从applicationContext.xml中的xml配置开始了:

<!-- Security Config -->
<security:http security="none" pattern="/javax.faces.resource/**"/>
<security:http auto-config="true" use-expressions="true">
    <security:intercept-url pattern="/login.xhtml" access="permitAll"/>
    <security:intercept-url pattern="/**" access="permitAll"/>

    <security:form-login login-page="/login.xhtml" 
        login-processing-url="/do_login"
        authentication-failure-url="/login.xhtml"
        authentication-success-handler-ref="authSuccessHandler"
        username-parameter="email" 
        password-parameter="password"/>
    <security:logout logout-success-url="/login.xhtml"
        logout-url="/do_logout"
        delete-cookies="JSESSIONID"/>
</security:http>

<bean id="userDetails" class="com.madmob.madmoney.security.UserDetailsServiceImpl"></bean>
<bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
    <constructor-arg name="strength" value="10" />
</bean>
<bean id="authSuccessHandler" class="com.madmob.madmoney.security.UserAuthenticationSuccessHandler"></bean>

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

以及web.xml中的以下内容:

<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>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

以下基于java的配置:

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    UserAuthenticationSuccessHandler authSuccessHandler;
    @Autowired
    DataSource dataSource;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) 
        throws Exception {
        auth.jdbcAuthentication().usersByUsernameQuery("SELECT email, passowrd, enabled FROM app_user WHERE email = ?")
        .authoritiesByUsernameQuery("SELECT role_name FROM role WHERE role_id = (SELECT role_id FROM user_role WHERE email = ?)")
        .dataSource(dataSource).passwordEncoder(new BCryptPasswordEncoder(10));
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/javax.faces.resource/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();

        http.authorizeRequests().anyRequest().authenticated()
        .and()
        .formLogin().loginPage("/login.xhtml").loginProcessingUrl("/do_login")
        .failureUrl("/login.xhtml").successHandler(authSuccessHandler)
        .usernameParameter("email").passwordParameter("password").permitAll()
        .and()
        .logout().logoutSuccessUrl("/login.xhtml").logoutUrl("/do_logout")
        .deleteCookies("JSESSIONID");

        // temp user add form
        // TODO remove
        http.antMatcher("/userForm.xhtml").authorizeRequests().anyRequest().permitAll();
    }
}

import java.util.EnumSet;

import javax.servlet.DispatcherType;

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer {

    protected  EnumSet<DispatcherType> getSecurityDispatcherTypes() {
        return EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC, DispatcherType.FORWARD);
    }
}

登录在基于xml的配置上运行良好,但是如果我尝试登录Tomcat,则切换返回404:The requested resource is not available.

无论是否登录,所有页面都可以访问。

以下是我的登录表单:

<h:form id="loginForm" prependId="false" styleClass="panel-body">
    <div>
        <p:inputText id="email" required="true" label="email" 
                    value="#{loginBean.email}" styleClass="form-control f-75"
                    placeholder="Email Address"></p:inputText>
        <h:message for="email" styleClass="validationMsg"/>
    </div>
    <div class="spacer"/>
    <div>
        <p:password id="password" required="true" label="password"
                    value="#{loginBean.password}" placeholder="Password"></p:password>
        <h:message for="password" styleClass="validationMsg"/>

        <h:messages globalOnly="true" styleClass="validationMsg" />
    </div>
    <div class="spacer"/>
    <p:commandButton id="login" value="Log in"
                    actionListener="#{loginBean.login}" ajax="false"/>
</h:form>

和我的支持bean中的登录方法:

/**
 * Forwards login parameters to Spring Security
 */
public void login(ActionEvent loginEvt){
    // setup external context
    logger.info("Starting login");
    ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();

    // setup dispatcher for spring security
    logger.info("Setup dispatcher");
    RequestDispatcher dispatcher = ((ServletRequest) context.getRequest())
            .getRequestDispatcher("/do_login");

    try {
        // forward request
        logger.info("Forwarding request to spring security");
        dispatcher.forward((ServletRequest) context.getRequest(), 
                (ServletResponse) context.getResponse());
    } catch (ServletException sEx) {
        logger.error("The servlet has encountered a problem", sEx);
    } catch (IOException ioEx) {
        logger.error("An I/O error has occured", ioEx);
    } catch (Exception ex) {
        logger.error("An error has occured", ex);
    }

    // finish response
    FacesContext.getCurrentInstance().responseComplete();
}

这是在Java 1.8,Tomcat 7,Spring和Spring Security 3.2.5,JSF 2.1,Primefaces 5上运行

遇到问题后我尝试过的事情:

  • 添加了SpringSecurityInitializer,因为我最初只使用SecurityConfig
  • 尝试使用默认的Spring Security网址(j_spring_security_check),而不是指定处理网址并转发到该网址。
  • 已停用csrf
  • getSecurityDispatcherTypes方法添加到SpringSecurityInitializer以匹配来自web.xml的配置
  • 寻找解决方案时的其他各种小事

1 个答案:

答案 0 :(得分:0)

我发现了这个问题。问题在于:

http.antMatcher("/userForm.xhtml").authorizeRequests().anyRequest().permitAll();

这只是我的方法纯粹错误地链接它的外观。我真的不明白为什么它导致了登录处理URL的问题,而不仅仅是意外的访问行为,但我改变了该方法的整个片段,如下所示:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable()
    .authorizeRequests().antMatchers("/userForm.xhtml").permitAll() //TODO remove temp user add form
    .anyRequest().authenticated()
    .and()
    .formLogin().loginPage("/login.xhtml").loginProcessingUrl("/do_login")
    .failureUrl("/login.xhtml").successHandler(authSuccessHandler)
    .usernameParameter("email").passwordParameter("password").permitAll()
    .and()
    .logout().logoutSuccessUrl("/login.xhtml").logoutUrl("/do_logout")
    .deleteCookies("JSESSIONID");
}

这当然也更有意义,但我最初主要将它们分开,因为正如评论所指出的那样,userForm.xhtml是暂时的。