首页 > 解决方案 > Spring自定义登录页面始终将用户标记为匿名

问题描述

我正在尝试制作一个自定义 JSP 登录页面以使用 Spring Security。我遵循了多个示例,但是在使用有效凭据按下自定义登录页面的提交按钮后,结果始终是 403 Forbidden 错误,尽管用户在 DB 中具有正确的访问权限,并且如果我删除了自定义登录页面,我可以使用相同的凭据成功登录.

这是日志

12:03:24,923 DEBUG [io.undertow.request] (default I/O-5) Matched prefix path /FitnessTracker for path /FitnessTracker/login.html
12:03:24,924 DEBUG [io.undertow.request.security] (default task-1) Attempting to authenticate /FitnessTracker/login.html, authentication required: false
12:03:24,925 DEBUG [io.undertow.request.security] (default task-1) Authentication outcome was NOT_ATTEMPTED with method io.undertow.security.impl.CachedAuthenticatedSessionMechanism@358df3b9 for /FitnessTracker/login.html
12:03:24,925 DEBUG [io.undertow.request.security] (default task-1) Authentication result was ATTEMPTED for /FitnessTracker/login.html
12:03:24,925 DEBUG [org.springframework.security.web.FilterChainProxy] (default task-1) /login.html at position 1 of 12 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
12:03:24,925 DEBUG [org.springframework.security.web.FilterChainProxy] (default task-1) /login.html at position 2 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
12:03:24,925 DEBUG [org.springframework.security.web.context.HttpSessionSecurityContextRepository] (default task-1) HttpSession returned null object for SPRING_SECURITY_CONTEXT
12:03:24,925 DEBUG [org.springframework.security.web.context.HttpSessionSecurityContextRepository] (default task-1) No SecurityContext was available from the HttpSession: io.undertow.servlet.spec.HttpSessionImpl@e399605d. A new one will be created.
12:03:24,925 DEBUG [org.springframework.security.web.FilterChainProxy] (default task-1) /login.html at position 3 of 12 in additional filter chain; firing Filter: 'HeaderWriterFilter'
12:03:24,925 DEBUG [org.springframework.security.web.FilterChainProxy] (default task-1) /login.html at position 4 of 12 in additional filter chain; firing Filter: 'CsrfFilter'
12:03:24,925 DEBUG [org.springframework.security.web.FilterChainProxy] (default task-1) /login.html at position 5 of 12 in additional filter chain; firing Filter: 'LogoutFilter'
12:03:24,925 DEBUG [org.springframework.security.web.util.matcher.AntPathRequestMatcher] (default task-1) Request 'GET /login.html' doesn't match 'POST /logout
12:03:24,925 DEBUG [org.springframework.security.web.FilterChainProxy] (default task-1) /login.html at position 6 of 12 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
12:03:24,925 DEBUG [org.springframework.security.web.util.matcher.AntPathRequestMatcher] (default task-1) Request 'GET /login.html' doesn't match 'POST /login.html
12:03:24,925 DEBUG [org.springframework.security.web.FilterChainProxy] (default task-1) /login.html at position 7 of 12 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
12:03:24,925 DEBUG [org.springframework.security.web.savedrequest.DefaultSavedRequest] (default task-1) pathInfo: both null (property equals)
12:03:24,925 DEBUG [org.springframework.security.web.savedrequest.DefaultSavedRequest] (default task-1) queryString: both null (property equals)
12:03:24,925 DEBUG [org.springframework.security.web.savedrequest.DefaultSavedRequest] (default task-1) requestURI: arg1=/FitnessTracker/; arg2=/FitnessTracker/login.html (property not equals)
12:03:24,925 DEBUG [org.springframework.security.web.savedrequest.HttpSessionRequestCache] (default task-1) saved request doesn't match
12:03:24,926 DEBUG [org.springframework.security.web.FilterChainProxy] (default task-1) /login.html at position 8 of 12 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
12:03:24,926 DEBUG [org.springframework.security.web.FilterChainProxy] (default task-1) /login.html at position 9 of 12 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
12:03:24,926 DEBUG [org.springframework.security.web.authentication.AnonymousAuthenticationFilter] (default task-1) Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@7b207c43: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffed504: RemoteIpAddress: 127.0.0.1; SessionId: 5KhX3tAA2iFK9bLF5nkrmmyaH1EQWcSGMRCpP_5N; Granted Authorities: ROLE_ANONYMOUS'
12:03:24,927 DEBUG [org.springframework.security.web.FilterChainProxy] (default task-1) /login.html at position 10 of 12 in additional filter chain; firing Filter: 'SessionManagementFilter'
12:03:24,927 DEBUG [org.springframework.security.web.FilterChainProxy] (default task-1) /login.html at position 11 of 12 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
12:03:24,927 DEBUG [org.springframework.security.web.FilterChainProxy] (default task-1) /login.html at position 12 of 12 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
12:03:24,927 DEBUG [org.springframework.security.web.access.intercept.FilterSecurityInterceptor] (default task-1) Secure object: FilterInvocation: URL: /login.html; Attributes: [permitAll]
12:03:24,927 DEBUG [org.springframework.security.web.access.intercept.FilterSecurityInterceptor] (default task-1) Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@7b207c43: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffed504: RemoteIpAddress: 127.0.0.1; SessionId: 5KhX3tAA2iFK9bLF5nkrmmyaH1EQWcSGMRCpP_5N; Granted Authorities: ROLE_ANONYMOUS
12:03:24,927 DEBUG [org.springframework.security.access.vote.AffirmativeBased] (default task-1) Voter: org.springframework.security.web.access.expression.WebExpressionVoter@7f7eaf3b, returned: 1
12:03:24,927 DEBUG [org.springframework.security.web.access.intercept.FilterSecurityInterceptor] (default task-1) Authorization successful
12:03:24,927 DEBUG [org.springframework.security.web.access.intercept.FilterSecurityInterceptor] (default task-1) RunAsManager did not change Authentication object
12:03:24,927 DEBUG [org.springframework.security.web.FilterChainProxy] (default task-1) /login.html reached end of additional filter chain; proceeding with original chain
12:03:24,928 DEBUG [org.springframework.web.servlet.DispatcherServlet] (default task-1) DispatcherServlet with name 'DispatcherServlet' processing GET request for [/FitnessTracker/login.html]
12:03:24,928 DEBUG [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] (default task-1) Looking up handler method for path /login.html
12:03:24,929 DEBUG [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] (default task-1) Returning handler method [public java.lang.String org.learning.spring.controller.LoginController.login()]
12:03:24,929 DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] (default task-1) Returning cached instance of singleton bean 'loginController'
12:03:24,929 DEBUG [org.springframework.web.servlet.DispatcherServlet] (default task-1) Last-Modified value for [/FitnessTracker/login.html] is: -1
12:03:24,929 DEBUG [org.springframework.web.servlet.DispatcherServlet] (default task-1) Rendering view [org.springframework.web.servlet.view.JstlView: name 'login'; URL [/WEB-INF/jsp/login.jsp]] in DispatcherServlet with name 'DispatcherServlet'
12:03:24,929 DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] (default task-1) Returning cached instance of singleton bean 'requestDataValueProcessor'
12:03:24,930 DEBUG [org.springframework.web.servlet.view.JstlView] (default task-1) Forwarding to resource [/WEB-INF/jsp/login.jsp] in InternalResourceView 'login'
12:03:24,930 DEBUG [org.apache.jasper.servlet] (default task-1) JspEngine --> /WEB-INF/jsp/login.jsp
12:03:24,930 DEBUG [org.apache.jasper.servlet] (default task-1)          ServletPath: /WEB-INF/jsp/login.jsp
12:03:24,930 DEBUG [org.apache.jasper.servlet] (default task-1)             PathInfo: null
12:03:24,930 DEBUG [org.apache.jasper.servlet] (default task-1)             RealPath: D:\Programs\Development\JAVA\Server\WildFly\wildfly-12.0.0.Final\standalone\tmp\vfs\temp\tempf4aa4a688921fb1a\FitnessTracker.war-9ab7386a6c27f7be\WEB-INF\jsp\login.jsp
12:03:24,930 DEBUG [org.apache.jasper.servlet] (default task-1)           RequestURI: /FitnessTracker/WEB-INF/jsp/login.jsp
12:03:24,930 DEBUG [org.apache.jasper.servlet] (default task-1)          QueryString: null
12:03:24,931 DEBUG [org.springframework.security.web.header.writers.HstsHeaderWriter] (default task-1) Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@6c23b3f5
12:03:24,931 DEBUG [org.springframework.security.web.context.HttpSessionSecurityContextRepository] (default task-1) SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
12:03:24,932 DEBUG [org.springframework.web.servlet.DispatcherServlet] (default task-1) Successfully completed request
12:03:24,932 DEBUG [org.springframework.security.web.access.ExceptionTranslationFilter] (default task-1) Chain processed normally
12:03:24,932 DEBUG [org.springframework.security.web.context.SecurityContextPersistenceFilter] (default task-1) SecurityContextHolder now cleared, as request processing completed
12:03:27,686 DEBUG [io.undertow.request] (default I/O-5) Matched prefix path /FitnessTracker for path /FitnessTracker/j_spring_security_check
12:03:27,687 DEBUG [io.undertow.request.security] (default task-1) Attempting to authenticate /FitnessTracker/j_spring_security_check, authentication required: false
12:03:27,687 DEBUG [io.undertow.request.security] (default task-1) Authentication outcome was NOT_ATTEMPTED with method io.undertow.security.impl.CachedAuthenticatedSessionMechanism@358df3b9 for /FitnessTracker/j_spring_security_check
12:03:27,687 DEBUG [io.undertow.request.security] (default task-1) Authentication result was ATTEMPTED for /FitnessTracker/j_spring_security_check
12:03:27,687 DEBUG [org.springframework.security.web.FilterChainProxy] (default task-1) /j_spring_security_check at position 1 of 12 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
12:03:27,688 DEBUG [org.springframework.security.web.FilterChainProxy] (default task-1) /j_spring_security_check at position 2 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
12:03:27,688 DEBUG [org.springframework.security.web.context.HttpSessionSecurityContextRepository] (default task-1) HttpSession returned null object for SPRING_SECURITY_CONTEXT
12:03:27,688 DEBUG [org.springframework.security.web.context.HttpSessionSecurityContextRepository] (default task-1) No SecurityContext was available from the HttpSession: io.undertow.servlet.spec.HttpSessionImpl@e399605d. A new one will be created.
12:03:27,688 DEBUG [org.springframework.security.web.FilterChainProxy] (default task-1) /j_spring_security_check at position 3 of 12 in additional filter chain; firing Filter: 'HeaderWriterFilter'
12:03:27,688 DEBUG [org.springframework.security.web.FilterChainProxy] (default task-1) /j_spring_security_check at position 4 of 12 in additional filter chain; firing Filter: 'CsrfFilter'
12:03:27,688 DEBUG [org.springframework.security.web.csrf.CsrfFilter] (default task-1) Invalid CSRF token found for http://localhost:8080/FitnessTracker/j_spring_security_check
12:03:27,688 DEBUG [org.springframework.security.web.header.writers.HstsHeaderWriter] (default task-1) Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@6c23b3f5
12:03:27,688 DEBUG [org.springframework.security.web.context.HttpSessionSecurityContextRepository] (default task-1) SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
12:03:27,688 DEBUG [org.springframework.security.web.context.SecurityContextPersistenceFilter] (default task-1) SecurityContextHolder now cleared, as request processing completed

这是安全配置

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/**")
                .hasRole("USER")
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                .permitAll();
    }

    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        JdbcDaoImpl jdbcDao = new JdbcDaoImpl();
        jdbcDao.setDataSource(dataSource);
        return jdbcDao;
    }

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

我的登录控制器

@Controller
public class LoginController {

    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String login() {
        return "login";
    }
}

登录页面

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<body>
<form action="j_spring_security_check" name="f" method="post">
    <table>
        <tr>
            <td>User:</td>
            <td><input type="text" name="j_username" value=""></td>
        </tr>
        <tr>
            <td>Password:</td>
            <td><input type="password" name="j_password" ></td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" name="Submit" value="Submit"></td>
        </tr>
    </table>
</form>
</body>
</html>

这是我在 POM 文件中的安全依赖项:

    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
        <version>5.0.4.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-config</artifactId>
        <version>5.0.4.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-taglibs</artifactId>
        <version>5.0.4.RELEASE</version>
    </dependency>

我的网络初始化程序

public class WebAppInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        WebApplicationContext context = getContext();

        servletContext.addListener(new ContextLoaderListener(context));

        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet", new DispatcherServlet(context));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("*.html");
        dispatcher.addMapping("*.json");
        dispatcher.addMapping("/pdfs/**");
        dispatcher.addMapping("/images/**");
    }

    private AnnotationConfigWebApplicationContext getContext() {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(WebConfig.class);
        return context;
    }
}

而 WebConfig [它不包含任何与安全相关的东西,它只包含数据源配置、实体管理器、视图解析器等]

@EnableWebMvc
@Configuration
@EnableTransactionManagement
@ComponentScan("org.learning.spring")
@EnableJpaRepositories("org.learning.spring.repository")
public class WebConfig implements WebMvcConfigurer {

    @Bean
    public DriverManagerDataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();

        dataSource.setDriverClassName("org.postgresql.Driver");
        dataSource.setUrl("jdbc:postgresql://localhost:5432/FitnessTracker");
        dataSource.setUsername("postgres");
        dataSource.setPassword("sa");

        return dataSource;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();

        entityManagerFactoryBean.setPersistenceUnitName("punit");
        entityManagerFactoryBean.setDataSource(dataSource());
        entityManagerFactoryBean.setJpaVendorAdapter(hibernateJpaVendorAdapter());
        entityManagerFactoryBean.setJpaPropertyMap(getJPAPropertyMap());
        entityManagerFactoryBean.setPackagesToScan(new String[]{"org.learning.spring"});

        return entityManagerFactoryBean;
    }

    @Bean
    public HibernateJpaVendorAdapter hibernateJpaVendorAdapter() {
        HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
        hibernateJpaVendorAdapter.setShowSql(true);
        return hibernateJpaVendorAdapter;
    }

    @Bean
    public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
        jpaTransactionManager.setEntityManagerFactory(entityManagerFactory);
        return jpaTransactionManager;
    }

    @Bean
    public InternalResourceViewResolver internalResourceViewResolver() {
        InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
        internalResourceViewResolver.setPrefix("/WEB-INF/jsp/");
        internalResourceViewResolver.setSuffix(".jsp");
        return internalResourceViewResolver;
    }

    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("messages");
        return messageSource;
    }

    @Bean
    public SessionLocaleResolver localeResolver() {
        SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
        sessionLocaleResolver.setDefaultLocale(Locale.ENGLISH);
        return sessionLocaleResolver;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
        localeChangeInterceptor.setParamName("language");
        registry.addInterceptor(localeChangeInterceptor);
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/assets/**").addResourceLocations("/assets/");
        registry.addResourceHandler("/pdfs/**").addResourceLocations("/pdfs/");
    }

    private Map<String, String> getJPAPropertyMap() {
        Map<String, String> jpaPropertyMap = new HashMap<>();
        jpaPropertyMap.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQL95Dialect");
        jpaPropertyMap.put("hibernate.hbm2ddl.auto", "update");
        jpaPropertyMap.put("hibernate.format_sql", "true");
        return jpaPropertyMap;
    }
}

标签: springspring-security

解决方案


由于spring-security4.x 版本默认login-processing-url不再j_spring_security_checklogin. 您可以查看Spring Security Reference 5.0.4-RELEASE

所以将登录表单的操作更改为在 jsp 中登录:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<body>
<form action="login" name="f" method="post">
    <table>
        <tr>
            <td>User:</td>
            <td><input type="text" name="j_username" value=""></td>
        </tr>
        <tr>
            <td>Password:</td>
            <td><input type="password" name="j_password" ></td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" name="Submit" value="Submit"></td>
        </tr>
    </table>
</form>
</body>
</html>

并且假设您已将此映射设置为 dispatcher-servlet:

ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet", new DispatcherServlet(context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("*.html");
dispatcher.addMapping("*.json");
dispatcher.addMapping("/pdfs/**");
dispatcher.addMapping("/images/**");

您的控制器应使用“.html”后缀进行映射:

@Controller
public class LoginController {

    @RequestMapping(value = "/login.html", method = RequestMethod.GET)
    public String login() {
        return "login";
    }
}

推荐阅读