【问题标题】:Spring security throws javax.servlet.ServletException: Could not resolve view with name 'j_spring_security_check'Spring 安全抛出 javax.servlet.ServletException:无法解析名称为“j_spring_security_check”的视图
【发布时间】:2015-06-13 05:15:20
【问题描述】:

我正在尝试向我的 Spring MVC 应用程序添加带有 自定义登录页面数据库访问权限Spring security。我的映射似乎是错误的,因为它无法映射j_spring_security_check

为了解决这个问题,我查看了以下页面1,2,3,但还没有解决问题。

如果您没有太多时间,请阅读下面的第 2 部分,这是问题编辑部分的开始。否则,请同时阅读PART 1PART 2

第 1 部分

我还在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>
    </filter-mapping>

Caused by: java.lang.IllegalStateException: Duplicate Filter registration for 'springSecurityFilterChain'. Check to ensure the Filter is only configured once.
    at org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer.registerFilter(AbstractSecurityWebApplicationInitializer.java:215)
    at org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer.insertSpringSecurityFilterChain(AbstractSecurityWebApplicationInitializer.java:147)
    at org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer.onStartup(AbstractSecurityWebApplicationInitializer.java:121)
    at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:175)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5423)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    ... 6 more

Jun 13, 2015 2:44:54 PM org.apache.catalina.core.ContainerBase startInternal
SEVERE: A child container failed during start
java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost]]
    at java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.util.concurrent.FutureTask.get(FutureTask.java:188)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1123)
    at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:302)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.StandardService.startInternal(StandardService.java:443)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:732)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:691)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:322)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:456)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)
Caused by: org.apache.catalina.LifecycleException: A child container failed during start
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1131)
    at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:800)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    ... 6 more

Jun 13, 2015 2:44:54 PM org.apache.catalina.startup.Catalina start
SEVERE: The required Server component failed to start so Tomcat is unable to start.
org.apache.catalina.LifecycleException: Failed to start component [StandardServer[8005]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:691)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:322)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:456)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardService[Catalina]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
    at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:732)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    ... 7 more
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
    at org.apache.catalina.core.StandardService.startInternal(StandardService.java:443)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    ... 9 more
Caused by: org.apache.catalina.LifecycleException: A child container failed during start
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1131)
    at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:302)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    ... 11 more

不添加过滤器,一旦表单提交,请求将被以下控制器捕获。

@Controller
public class MainController {

    @RequestMapping("/{viewName}")
    public String index(@PathVariable String viewName) {
        ...
    }

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
          http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">
    <listener>
        <listener-class>org.apache.tiles.extras.complete.CompleteAutoloadTilesListener</listener-class>
    </listener>


    <servlet>
        <servlet-name>my</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

<!--    <listener> -->
<!--        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> -->
<!--    </listener> -->
<!--    <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> -->


    <servlet-mapping>
      <servlet-name>my</servlet-name>
      <url-pattern>/</url-pattern>
    </servlet-mapping>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/config/my-security.xml
        </param-value>
    </context-param>

</web-app>

my-security.xml

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


    <http auto-config="true" access-denied-page="/notFound.jsp"
        use-expressions="true">
        <intercept-url pattern="/" access="permitAll" />
    </http>

    <!-- <beans:import resource="security-db.xml" /> -->

    <authentication-manager>
        <authentication-provider>
            <user-service>
                <user name="alex" password="123456" authorities="ROLE_USER" />
                <user name="mkyong" password="123456" authorities="ROLE_USER, ROLE_ADMIN" />
            </user-service>
        </authentication-provider>
    </authentication-manager>
</beans:beans>

AppConfig.java

@EnableWebMvc
@Configuration
@ComponentScan({ "com.myproject.*" })
@EnableTransactionManagement
@Import({ SecurityConfig.class })
public class AppConfig {


    @Bean
    public SessionFactory sessionFactory() {
        LocalSessionFactoryBuilder builder = new LocalSessionFactoryBuilder(
                dataSource());
        builder.scanPackages("com.myproject.model").addProperties(
                getHibernateProperties());
        return builder.buildSessionFactory();
    }

    private Properties getHibernateProperties() {
        Properties prop = new Properties();
        prop.put("hibernate.format_sql", "true");
        prop.put("hiberate.show_sql", "true");
        prop.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
        return prop;
    }

    @Bean(name = "dataSource")
    public BasicDataSource dataSource() {
        BasicDataSource ds = new BasicDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/test");
        ds.setUsername("jack");
        ds.setPassword("jack");
        return ds;
    }

    @Bean
    public HibernateTransactionManager txManager() {
        return new HibernateTransactionManager(sessionFactory());
    }

}

SecurityConfig.java

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    @Qualifier("userDetailsService")
    UserDetailsService userDetailsService;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth)
            throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(
                passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/profile/**")
                .access("hasRole('ADMIN')").and().formLogin()
                .loginPage("/signin").failureUrl("/signin?error")
                .usernameParameter("username").passwordParameter("password")
                .and().logout().logoutSuccessUrl("/index").and().csrf().and()
                .exceptionHandling().accessDeniedPage("/403");
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder;
    }

}

SpringMVCInitializer.java

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class SpringMvcInitializer extends
        AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { AppConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return null;
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

}

LoginController.java

@Controller
public class LoginController {

    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public ModelAndView login(
            @RequestParam(value = "error", required = false) String error,
            @RequestParam(value = "logout", required = false) String logout) {

        ModelAndView model = new ModelAndView();
        if (error != null) {
            model.addObject("error", "Invalid username and password!");
        }

        if (logout != null) {
            model.addObject("msg", "You've been logged out successfully.");
        }
        model.setViewName("login");

        return model;

    }
}

**

第 2 部分

根据 Thomas 的建议,我将代码更改如下

**

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
          http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">
    <listener>
        <listener-class>org.apache.tiles.extras.complete.CompleteAutoloadTilesListener</listener-class>
    </listener>


    <servlet>
        <servlet-name>my</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
      <servlet-name>my</servlet-name>
      <url-pattern>/</url-pattern>
    </servlet-mapping>



</web-app>

AppConfig.java

@EnableWebMvc
@Configuration
@ComponentScan({ "com.myproject" })
@EnableTransactionManagement
@Import({ SecurityConfig.class })
public class AppConfig {

    @Bean
    public SessionFactory sessionFactory() {
        LocalSessionFactoryBuilder builder = new LocalSessionFactoryBuilder(
                dataSource());
        builder.scanPackages("com.myproject.model").addProperties(
                getHibernateProperties());
        return builder.buildSessionFactory();
    }

    private Properties getHibernateProperties() {
        Properties prop = new Properties();
        prop.put("hibernate.format_sql", "true");
        prop.put("hiberate.show_sql", "true");
        prop.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
        return prop;
    }

    @Bean(name = "dataSource")
    public BasicDataSource dataSource() {
        BasicDataSource ds = new BasicDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/test");
        ds.setUsername("jack");
        ds.setPassword("jack");
        return ds;
    }

    @Bean
    public HibernateTransactionManager txManager() {
        return new HibernateTransactionManager(sessionFactory());
    }

}

SecurityConfig.java

@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    @Qualifier("userDetailsService")
    UserDetailsService userDetailsService;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth)
            throws Exception {
        auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
//      auth.userDetailsService(userDetailsService).passwordEncoder(
//              passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/profile/**")
                .access("hasRole('ADMIN')").and().formLogin()
                .loginPage("/login").failureUrl("/login?error")
                .and().logout().logoutSuccessUrl("/index").and().csrf().and()
                .exceptionHandling().accessDeniedPage("/403");
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder;
    }

}

SpringMVCInitializer.java

public class SpringMvcInitializer extends
        AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { AppConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return null;
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

}

主控制器

@Controller
public class MainController {

    @RequestMapping("/{viewName}")
    public String index(@PathVariable String viewName) {
        System.err.println("View Name is :" + viewName);
        if (isValidView(viewName)) {
            return viewName;
        }
        return null;
    }

关于 MainController,这是我处理静态页面(例如 www.myproject.com/index、www.myproject.com/contactus 等)的方式。关于这个问题我的另一个问题是 here

【问题讨论】:

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


    【解决方案1】:

    您的代码中有几个问题。最重要的一点是您正在为 Spring Security 混合 Java 配置和 XML 配置。确定您喜欢使用的配置。在我的回答中,我将专注于基于 Java 的配置,因为我会从您的代码中完全删除 XML 配置。

    在您的情况下,默认情况下 @EnableWebSecurity 注释已经注册了适当的过滤器。但如果您使用的是 Spring MVC,则应该是 @EnableWebMvcSecurity

    此外,请查看您的 HttpSecurity 配置:

     protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests().antMatchers("/profile/**")
                    .access("hasRole('ADMIN')").and().formLogin()
                    .loginPage("/signin").failureUrl("/signin?error")
                    .usernameParameter("username").passwordParameter("password")
                    .and().logout().logoutSuccessUrl("/index").and().csrf().and()
                    .exceptionHandling().accessDeniedPage("/403");
    }
    

    您显然是在告诉登录页面将位于/signin,但在您的 LoginController 中,映射是针对/login。为您配置.usernameParameter("username") .passwordParameter("password") 做什么?在 Spring Security 默认配置中已经这样做了。

    您还应该添加 SecurityInitializer 类,它在 Spring Security 3.2 中可能如下所示:

    public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
    
        public SecurityInitializer() {
            super(SecurityConfig.class);
        }
    }
    

    所以总结一下,将 Spring Security 添加到您的项目的基本步骤是执行以下操作:

    1. 为 Spring Security 添加适当的(Maven、Gradle)依赖项;
    2. 配置带有@EnableWebMvcSecurity注解的Spring Security类,尤其是在configureGlobal(...)configure(...)方法中;
    3. 注册扩展AbstractSecurityWebApplicationInitializer的类;
    4. 在控制器中提供登录页面的映射,并在 JSP 页面中记下自定义表单。

    基本的登录表单如下所示:

        <form name='loginForm' method='POST'>
    
          <table>
            <tr>
                <td>User:</td>
                <td><input type='text' name='username' value=''></td>
            </tr>
            <tr>
                <td>Password:</td>
                <td><input type='password' name='password' /></td>
            </tr>
            <tr>
                <td colspan='2'><input name="submit" type="submit"
                    value="submit" /></td>
            </tr>
          </table>
    
          <input type="hidden" name="${_csrf.parameterName}"
            value="${_csrf.token}" />
    
        </form>
    

    这些基本步骤在您的项目中启用 Spring Security。有关更多信息,好的教程是: http://docs.spring.io/spring-security/site/docs/3.2.x/guides/hellomvc.html http://docs.spring.io/spring-security/site/docs/3.2.x/guides/form.html

    祝你好运:)

    【讨论】:

    • 我想是的,但我从未使用过 4.x 版本。试试看,如果需要一些更改,请告诉我。关于 'action' 属性,如果您想将身份验证数据发布到相同的路径,则没有必要,例如/login 或 /signin - 在你的代码中就是这样。
    • 我将依赖项更改为链接 3.2.7.RELEASE 中建议的版本,还将登录更改为登录,并将 configureGlobal 的正文更改为 auth.inMemoryAuthentication().withUser("user").password ("密码").roles("用户");它仍然显示相同的错误消息。我知道这是因为控制器,但不知道该怎么做。
    • 在 Spring MVC 中,调度程序 servlet 负责将请求路径转换为控制器方法,因此控制器中的一个 @RequestMapping 方法将解析一个静态(或 JSP)页面。很好的基础教程:byteslounge.com/tutorials/spring-mvc-requestmapping-example。在您的情况下,我将首先检查所有应用程序路径(包括 /login)是否在没有 Spring Security 依赖项的情况下正确解析,然后您才能保护它们。
    • 问题已更新。如果您担心“MainController”,请告诉我应该用什么替换它来处理静态页面,例如 www.myproject.com/index、www.myproject.com/login、www.myproject.com/contactus 请注意我正在使用 Tiles3。
    【解决方案2】:

    我强烈建议您遵循 Java 或 XML 配置。我个人更喜欢 Java 配置。

    • 删除所有与 Spring Security 相关的 XML 配置并创建以下 文件。
    • 将权限地址修改为您拥有的任何内容。

      public class MessageSecurityWebApplicationInitializer extends
              AbstractSecurityWebApplicationInitializer { //register the springSecurityFilterChain with the war
      
      }
      
      
      public class MessageWebApplicationInitializer extends
              AbstractAnnotationConfigDispatcherServletInitializer {
      
          @Override
          protected Class<?>[] getRootConfigClasses() {
              return new Class[] { SecurityConfig.class }; //make sure your config file gets loaded
          }
      
          @Override
          protected Class<?>[] getServletConfigClasses() {
              // TODO Auto-generated method stub
              return null;
          }
      
          @Override
          protected String[] getServletMappings() {
              // TODO Auto-generated method stub
              return null;
          }
      
      
         @Configuration
         @EnableWebMvcSecurity
         public class SecurityConfig extends WebSecurityConfigurerAdapter {
           @Autowired
              public void configureGlobal(AuthenticationManagerBuilder auth)
                  throws Exception {
              System.err.println("in here");
              auth.inMemoryAuthentication().withUser("user@yahoo.com")
                      .password("password").roles("USER"); //access to inmemory credentials
          }
      
          @Override
          protected void configure(HttpSecurity http) throws Exception {
          http.authorizeRequests()
                      .antMatchers("/resources/**", "/", "/index", "/aboutus")
                      .permitAll() //allow access to your static pages and resources
                      .antMatchers("/profile/**")
                      .hasRole("USER") //profile address is only available to users ( do not need to add ROLE_ as Spring does it for you)
                      .and() //and is equivalent of end tag in XML 
                      .formLogin().loginPage("/signin").failureUrl("/signin?error")
                      .permitAll().and().logout().logoutUrl("/singout").permitAll();
          }
      

    更多解释和访问数据库请参考documentation

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-07-11
      • 2015-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-04
      • 1970-01-01
      相关资源
      最近更新 更多