spring-security 尽管有一个自定义的登录表单,但直接进入默认的登录表单,Sping Boot (2.7.2)+ Spring security(5.4)+ SecurityFilterChain + Thymeleaf

gorkyyrv  于 2022-11-11  发布在  Spring
关注(0)|答案(2)|浏览(145)

我正在尝试创建一个简单的spring Boot 应用程序,带有spring security和自定义登录表单。

**问题:**应用程序直接打开默认登录页面,而不是自定义登录视图&它也无法解析任何其他thymeleaf视图。

直接访问所有其他视图(/home/error)会导致以下错误:This localhost page can't be found.打开http://localhost:8080/login只会进入默认登录页面,而不会进入自定义页面。

**注意:**html模板位于/src/main/resources/templates文件夹下-这是springboot查找的默认位置。

这里的关键是,我使用了@EnableWebSecurity注解和SecurityFilterChain bean(在Spring Security 5.4中引入),如下所示:

@EnableWebSecurity
public class WebSecurityConfig {
      @Bean
      public SecurityFilterChain configure(HttpSecurity http) throws Exception {
    ...
}

而不是更常见的(但是,从Spring Security 5.7.0-M2开始就不赞成使用)扩展WebSecurityConfigurerAdapter类的方式,如下所示。

@Configuration
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
    ...
    }

看起来后者工作正常,没有任何问题。

在各种论坛上建议的解决方案很少-我已经尝试过了。

1.保持项目结构,使所有其他包都放置在基础包(带有@SprintBootApplication注解的主类的包)下。
1.如果软件包按照第1点中提到的顺序排列,则使用@ComponentScan={<your_base_package>}。如果软件包彼此独立,则使用@ComponentScan={<package-1>, <package-2>, etc}

  • 以上两种解决方案都是为了避免404错误和查看未解决的问题而建议的。*

1.请使用@RestController而不是@Controller

  • 建议在出现WhiteLabel错误以及视图名称仅作为字符串而不是视图返回时使用此选项。*

1.保持控制器方法中的Mapurl值(如/login)和视图名称不同。如果Mapurl为/login,则将视图名称更改为loginpage.html(或其他名称)。

  • 这是针对循环路径问题而建议的-在解析视图名称时。*

1.有些人建议在类级别而不是方法级别使用@RequestMapping("/login"),但是我没有看到这两种方法有什么区别。
请注意,以上所有内容都基于WebSecurityConfigurerAdapter,而不是SecurityFilterChain
对于此要求(使用SecurityFilterChain自定义登录),我可以从官方文档/博客中找到的唯一参考是以下两个:
https://docs.spring.io/spring-security/site/docs/4.1.3.RELEASE/guides/html5/form-javaconfig.html
二、https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter
不幸的是,执行那里给出的步骤没有得到结果。我认为下面的问题是相同的(或相关的),但也没有给出解决方案。
https://github.com/spring-projects/spring-security/issues/10542
而且,* 几乎 * 所有其他可用的git/博客/网站/视频参考,都只使用WebSecurityConfigurerAdapter类。

Web安全配置类:

@EnableWebSecurity
public class WebSecurityConfig {
//  @Bean
    public SecurityFilterChain configure(HttpSecurity http) throws Exception {
        http
            .antMatcher("/**")
            .authorizeRequests()
                .antMatchers("/", "/home", "/login**","/callback/", "/webjars/**", "/css**", "/error**")
                .permitAll()
            .anyRequest()
                .authenticated()
            .and()
            .formLogin()
//                  .loginPage("/loginpage")
                    .usernameParameter("email")
                    .passwordParameter("password")
                    .loginPage("/login").loginProcessingUrl("/login")
                    .permitAll()
                    .defaultSuccessUrl("/home")
                    .failureUrl("/login?message=error")
             .and()
//           .logout()
//              .logoutUrl("/perform_logout")
//              .logoutSuccessUrl("/login?message=logout");
             .logout()
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                .logoutSuccessUrl("/login")
                .permitAll();

            return http.build();
    }
}

登录控制器:

(留下一些注解代码,以提示到目前为止还尝试了哪些操作)。

  • 在某个时间点,控件进入登录方法并打印模型对象的值(字符串)。但是,登录视图仍然没有得到解析,仅导致404错误。*
//@RestController
@Controller
@ResponseBody
//@RequestMapping("/")
public class LoginController {

    @GetMapping({"/", "/home"})
//  public String home() {
//  @GetMapping({"/", "/showHome"})
    public ModelAndView home(ModelAndView mav) {
        System.out.println("Inside GetMapping(/home) method of LoginController.");
        mav.setViewName("home");
        mav.addObject("Using @Controller and @ResponseBody, in the Controller");
        System.out.println("View Object: " + mav.getView());
        System.out.println("View Name: " + mav.getViewName());
        System.out.println("mav.hasView(): " + mav.hasView());
        return mav;
//      return "home";
    }

    @GetMapping("/login-error")
//  @GetMapping("/error")
    @RequestMapping("/login")
    public String login(HttpServletRequest request, Model model) {
        HttpSession session = request.getSession(false);
        String errorMessage = null;
        if (session != null) {
            AuthenticationException ex = (AuthenticationException) session
                    .getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
            if (ex != null) {
                errorMessage = ex.getMessage();
            }
        }
        System.out.println("--->" + errorMessage);
        model.addAttribute("errorMessage", errorMessage);
        return "login";
    }

}

我也为MVC添加了一个主配置,尽管我相信这些是Springboot自己假设的默认配置,即使没有这个也可以工作。

主配置类:

@Configuration
@ComponentScan("vlan.test.springboot.customLogin")
public class MainConfiguration implements WebMvcConfigurer {

        private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
                "classpath:/static/**", "classpath:/public/**", "classpath:/templates/**", "classpath:/resources/**"
        };

        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/**").addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS);
            registry.addResourceHandler("/templates/**").addResourceLocations("/templates/");
            registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
        }

        @Override
        public void addViewControllers(ViewControllerRegistry viewRegistry) {
            viewRegistry.addViewController("/").setViewName("home");
            viewRegistry.addViewController("/home").setViewName("home");
            viewRegistry.addViewController("/login").setViewName("login");
        }
    }

Gradle构建文件:

implementation 'org.springframework.boot:spring-boot-devtools'<br/>
implementation 'org.springframework.boot:spring-boot-starter-web'<br/>
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'<br/>
implementation 'org.springframework.boot:spring-boot-starter-security'<br/>
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'<br/>
implementation 'org.zalando:logbook-spring-boot-starter:2.14.0'

项目结构:

从日志中摘录-我发现相关/值得注意。

请检查 "..无效的会话ID...""..无法授权过滤器调用..." 部分。

2022-08-18 12:09:43.297  INFO 16596 --- [http-nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 10 ms
2022-08-18 12:09:43.334 DEBUG 16596 --- [http-nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Securing GET /
2022-08-18 12:09:43.351 DEBUG 16596 --- [http-nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext
2022-08-18 12:09:43.363 DEBUG 16596 --- [http-nio-8080-exec-1]****o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext
2022-08-18 12:09:43.364 DEBUG 16596 --- [http-nio-8080-exec-1] o.s.s.w.session.SessionManagementFilter  : Request requested invalid session id EF53E44688D581C69527A5442A987DB6
2022-08-18 12:09:43.394 DEBUG 16596 --- [http-nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor    : Failed to authorize filter invocation [GET /] with attributes [authenticated]
2022-08-18 12:09:43.445 DEBUG 16596 --- [http-nio-8080-exec-1] o.s.s.w.s.HttpSessionRequestCache        : Saved request http://localhost:8080/ to session****
2022-08-18 12:09:43.448 DEBUG 16596 --- [http-nio-8080-exec-1] s.w.a.DelegatingAuthenticationEntryPoint : Trying to match using And [Not [RequestHeaderRequestMatcher [expectedHeaderName=X-Requested-With, expectedHeaderValue=XMLHttpRequest]], MediaTypeRequestMatcher [contentNegotiationStrategy=org.springframework.web.accept.HeaderContentNegotiationStrategy@51020050, matchingMediaTypes=[application/xhtml+xml, image/*, text/html, text/plain], useEquals=false, ignoredMediaTypes=[*/*]]]
2022-08-18 12:09:43.450 DEBUG 16596 --- [http-nio-8080-exec-1] s.w.a.DelegatingAuthenticationEntryPoint : Match found! Executing org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint@416ee886
2022-08-18 12:09:43.454 DEBUG 16596 --- [http-nio-8080-exec-1] o.s.s.web.DefaultRedirectStrategy        : Redirecting to http://localhost:8080/login
2022-08-18 12:09:43.459 DEBUG 16596 --- [http-nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext
2022-08-18 12:09:43.466 DEBUG 16596 --- [http-nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : Did not store empty SecurityContext
2022-08-18 12:09:43.466 DEBUG 16596 --- [http-nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
2022-08-18 12:09:43.487 DEBUG 16596 --- [http-nio-8080-exec-2] o.s.security.web.FilterChainProxy        : Securing GET /login

在此处填写日志:https://codeshare.io/dwlLDK

(似乎它只会活24小时。请让我知道,如果你不能访问它)。

**编辑(2022-08-22):**摘录自application.properties文件(仅 thymeleaf & security config)-这是问题的原因(如已接受的[self-identified]答案中所解释)。


# thymeLeaf

spring.thymeleaf.enabled=true
spring.thymeleaf.cache=false
spring.thymeleaf.check-template=true
spring.thymeleaf.check-template-location=true
spring.thymeleaf.prefix=classpath:/templates
spring.thymeleaf.suffix=.html

# security

server.error.whitelabel.enabled=false
cbjzeqam

cbjzeqam1#

我在这里提供了一个Sping Boot 应用程序示例:https://github.com/Aliuken/JobVacanciesApp_Java11
希望能有所帮助。

eimct9ow

eimct9ow2#

我自己解决了这个问题。

  • (将其添加为可接受的答案,以便无意中看到此页面的人更容易找到。)*

实际问题是application.properties文件中的thymeleaf prefix config中缺少尾随/spring.thymeleaf.prefix=classpath:/templates .
这应该是spring.thymeleaf.prefix=classpath:/templates/
一旦我添加了它,它就开始识别模板(包括自定义登录模板/视图)。

相关问题