Spring boot 无法使用表单身份验证进行身份验证

Spring boot 无法使用表单身份验证进行身份验证,spring-boot,spring-security,Spring Boot,Spring Security,我很难弄清楚为什么我无法进行身份验证。在查看跟踪后,我认为会话没有被创建,或者被清除,或者类似的性质。在我的Admin-Controller-login-post方法中有一段代码,如果没有注释,身份验证可以工作,但是我不想手动执行,因为Spring应该为我处理它。任何帮助都将不胜感激,如果我能提供额外的源代码帮助,请告诉我 详细信息: 我正在使用Spring Boot、Spring Security、Thymleaf,并通过本地主机运行此功能 每次我在/admin/**上查看页面时,都会出现登录

我很难弄清楚为什么我无法进行身份验证。在查看跟踪后,我认为会话没有被创建,或者被清除,或者类似的性质。在我的Admin-Controller-login-post方法中有一段代码,如果没有注释,身份验证可以工作,但是我不想手动执行,因为Spring应该为我处理它。任何帮助都将不胜感激,如果我能提供额外的源代码帮助,请告诉我

详细信息:

我正在使用Spring BootSpring SecurityThymleaf,并通过本地主机运行此功能

每次我在/admin/**上查看页面时,都会出现登录页面,我需要提供凭据。输入凭据后,登录页面将处理并显示/admin/index(根据控制器),但是当我尝试导航到另一个页面(例如:/admin/orgs)时,登录屏幕将重新出现。除了显而易见的问题外,我知道用户没有通过身份验证,因为我检查用户是否在/admin/index上通过了身份验证

项目结构

+ src
    + main
        + java
            - org.neric (package)
                - SecurityConfig
                - AdminController
                - AppController
                - Application
                - WebConfig
        + resources
            + templates
                - index.html 
                + admin
                    - index.html
                    - login.html
                    - orgs.html
            + static
                + css
                + js
                + img
跟踪(application.properties Trace=true)

POM

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

    <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        <version>3.0.4.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
<dependencies>
SecurityConfig

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

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

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser("user@neric.org")
                .password(passwordEncoder().encode("123456"))
                .roles("USER")
            .and()
                .withUser("admin@neric.org")
                    .password(passwordEncoder().encode("123456"))
                    .roles("ADMIN");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
       http
        .authorizeRequests()
            .antMatchers("/admin/**").hasRole("ADMIN")
        .and()
            .formLogin()
                .loginPage("/admin/login")
                    .usernameParameter("email")
                    .passwordParameter("password")
                .loginProcessingUrl("admin/login") //dont change, it's not the same as the line above.... /smh...
                .failureUrl("/404")
            .permitAll()
        .and()
            .logout()
                .logoutUrl("/admin/logout")
                .invalidateHttpSession(true)
            .permitAll()
        .and()
            .csrf()
                .disable()
        //.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
       ;
    }
}
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/webjars/**", "/img/**", "/css/**", "/js/**")
            .addResourceLocations("classpath:/META-INF/resources/webjars/", "classpath:/static/img/", "classpath:/static/css/", "classpath:/static/js/")
        ;
    }

}
网络配置

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

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

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser("user@neric.org")
                .password(passwordEncoder().encode("123456"))
                .roles("USER")
            .and()
                .withUser("admin@neric.org")
                    .password(passwordEncoder().encode("123456"))
                    .roles("ADMIN");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
       http
        .authorizeRequests()
            .antMatchers("/admin/**").hasRole("ADMIN")
        .and()
            .formLogin()
                .loginPage("/admin/login")
                    .usernameParameter("email")
                    .passwordParameter("password")
                .loginProcessingUrl("admin/login") //dont change, it's not the same as the line above.... /smh...
                .failureUrl("/404")
            .permitAll()
        .and()
            .logout()
                .logoutUrl("/admin/logout")
                .invalidateHttpSession(true)
            .permitAll()
        .and()
            .csrf()
                .disable()
        //.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
       ;
    }
}
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/webjars/**", "/img/**", "/css/**", "/js/**")
            .addResourceLocations("classpath:/META-INF/resources/webjars/", "classpath:/static/img/", "classpath:/static/css/", "classpath:/static/js/")
        ;
    }

}
AdminController

@Controller
public class AdminController {

    @GetMapping("/admin/logout")
    public String logout(Login login) {
        return "admin/login";
    }

    @PostMapping("/admin/logout")
    public String logoutPost(Login login) {
        return "admin/login";
    }

    @GetMapping("/admin/login")
    public String login(Login login) {
        return "admin/login";
    }

    @PostMapping("/admin/login")
    public String processForm(HttpServletRequest req, Login login)  {
        System.out.println(login);

        /* If I uncomment this, I can force authentication. Though, I shouldn't have to
        UsernamePasswordAuthenticationToken authReq = new UsernamePasswordAuthenticationToken(login.getEmail(), login.getPassword());
        Authentication auth = authManager.authenticate(authReq);

        SecurityContext sc = SecurityContextHolder.getContext();
        sc.setAuthentication(auth);
        HttpSession session = req.getSession(true);
        session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, sc);*/

        return "admin/index";
    }

    @GetMapping("/admin")
    public String showAdmin() {
        return "admin/index";
    }

    @GetMapping("/admin/orgs")
    public String showOrgs(Organization organization) {
        return "admin/orgs";
    }
}
/admin/login(/resources/templates/admin/login.html)


欢迎回来,,
请在下面登录您的帐户。
登录
(功能(){
"严格使用",;
addEventListener('load',function()){
//获取要应用自定义引导验证样式的所有表单
var forms=document.getElementsByClassName('needs-validation');
//对它们进行循环并防止提交
var validation=Array.prototype.filter.call(表单,函数(表单){
表单.addEventListener('submit',函数(事件){
if(form.checkValidity()==false){
event.preventDefault();
event.stopPropagation();
}
form.classList.add('was-validated');
},假);
});
},假);
})();

管理员/index(/resources/templates/admin/index.html)

重要部分

    <div sec:authorize="isAuthenticated()">
        Authenticated
    </div>
    <div sec:authorize="!isAuthenticated()">
        Not authenticated
    </div>

认证
未经认证
整页

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security" lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- Favicon -->
    <link rel="icon" href="../../assets/img/brand/favicon.png" type="image/png">
    <!-- Fonts -->
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700">
    <!-- Icons -->
    <link rel="stylesheet" th:href="@{/css/nucleo.css}" />
    <link rel="stylesheet" th:href="@{/css/all.min.css}" />
    <!-- Argon CSS -->
    <link rel="stylesheet" th:href="@{/css/argon.css}" />
    <!--<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">-->
</head>

<body class="docs">
<header class="navbar navbar-horizontal navbar-expand navbar-dark flex-row align-items-md-center ct-navbar">
    <a class="navbar-brand mr-0 mr-md-2" th:href="@{/admin/}" aria-label="Admin">
        <img src="https://www.capitalregionboces.org/wp-content/themes/twentyseventeen-child/images/crb-logo-header.svg">
        <sup>Admin</sup>
    </a>
</header>
<div class="container-fluid">
    <div class="row flex-xl-nowrap">
        <div class="col-12 col-md-3 col-xl-2 ct-sidebar">
            <nav class="collapse ct-links" id="ct-docs-nav">
                <!-- Show links for all groups -->
                <div class="ct-toc-item active">
                    <a class="ct-toc-link" href="../../docs/getting-started/overview.html">Manage</a>
                    <ul class="nav ct-sidenav">
                        <li class="active ct-sidenav-active">
                            <a th:href="@{/admin/orgs}">Organizations</a>
                        </li>
                        <li>
                            <a th:href="@{/admin/buildings}">Buildings</a>
                        </li>
                        <li>
                            <a th:href="@{/admin/roles}">Roles</a>
                        </li>
                    </ul>
                </div>
                <!-- Show links for all groups -->
                <div class="ct-toc-item active">
                    <a class="ct-toc-link" href="../../docs/foundation/colors.html">Reports</a>
                    <ul class="nav ct-sidenav">
                        <li>
                            <a th:href="@{/admin/reports/dailyUsage}">Daily Usage</a>
                        </li>
                    </ul>
                </div>

            </nav>
        </div>

        <!-- Main Content: Side Bar -->
        <div class="d-none d-xl-block col-xl-2 ct-toc">
            <ul class="section-nav">
                <li class="toc-entry toc-h3"><a th:href="@{/admin/orgs/add}">Add Organization</a></li>
            </ul>
        </div>

        <main class="col-12 col-md-8 col-xl-7 py-md-3 pl-md-5 ct-content" role="main">
            <!-- Main Content: Header -->
            <div class="ct-page-title">
                <h1 class="ct-title" id="content">Index</h1>
                <div class="avatar-group mt-3"></div>
            </div>
            <!--<p class="ct-lead">A list of all the organizations.</p>-->

            <hr>

            <!-- Main Content -->

            <div sec:authorize="isAuthenticated()">
                Authenticated
            </div>

            <div sec:authorize="!isAuthenticated()">
                Not authenticated
            </div>

            <!-- End Main Content -->

        </main>
    </div>
</div>
<!-- Core -->
<script th:src="@{/js/jquery.min.js}"></script>
<script th:src="@{/js/bootstrap.bundle.min.js}"></script>
<script th:src="@{/js/js.cookie.js}"></script>
<script th:src="@{/js/jquery.scrollbar.min.js}"></script>
<script th:src="@{/js/jquery-scrollLock.min.js}"></script>
<!-- Docs JS -->
<script th:src="@{/js/anchor.min.js}"></script>
<script th:src="@{/js/clipboard.min.js}"></script>
<script th:src="@{/js/holder.min.js}"></script>
<script th:src="@{/js/prism.js}"></script>
<!-- Argon JS -->
<script th:src="@{/js/argon.min.js}"></script>
</body>

</html>

指数
认证 未经认证
问题在您的安全配置中

你在保护一切。见下文

http
    .authorizeRequests()
        .antMatchers("/admin/**").hasRole("ADMIN")
因此,它会让你一次又一次地发送登录页面。因为它没有成功通过安全过滤器

我建议将您的控制器映射更改为
@GetMapping(“/login”)
,并通过添加下面的配置更改允许每个人都可以访问它

 @Override
protected void configure(HttpSecurity http) throws Exception {
   http
    .authorizeRequests().antMatchers("/login").permitAll()
        .antMatchers("/admin/**").hasRole("ADMIN")
    .and()
        .formLogin().loginPage("/login") // cutom login page. 
        .usernameParameter("email")
        .passwordParameter("password")
        .loginProcessingUrl("/login")  // this should be public too(which we did) as user's request would reach here then authentication will happen. 
        .defaultSuccessUrl("/loginSuccess") // this has to be done since you have two different role so we need to identify whether user role or admin role and based on that redirection should happen.
    .and()
        .logout()
            .logoutUrl("/logout") // logout should be common for both roles so don't add /admin/logout  
             .invalidateHttpSession(true)
        .permitAll()
    .and()
        .csrf()
            .disable()
    //.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
   ;
}
这是控制器:

@Controller 
public class AdminController {
@GetMapping("/login")
public String login() {
    return "admin/login";
}
@GetMapping("/loginSuccess")
public String showAdmin(@AuthenticationPrincipal User userDetails) {
    // check the role if user then redirect to user page, if admin then redirect to admin page. 
    List<GrantedAuthority> authorties = userDetails.getAuthorities().stream().collect(Collectors.toList());

    boolean isAdmin=false;
    if(authorties.stream().anyMatch(p->p.getAuthority().equals("ROLE_ADMIN"))) {
        return "admin/index";
    } else {
        return "user/index";
    }
}
@控制器
公共类管理员控制器{
@GetMapping(“/login”)
公共字符串登录(){
@Controller 
public class AdminController {
@GetMapping("/login")
public String login() {
    return "admin/login";
}
@GetMapping("/loginSuccess")
public String showAdmin(@AuthenticationPrincipal User userDetails) {
    // check the role if user then redirect to user page, if admin then redirect to admin page. 
    List<GrantedAuthority> authorties = userDetails.getAuthorities().stream().collect(Collectors.toList());

    boolean isAdmin=false;
    if(authorties.stream().anyMatch(p->p.getAuthority().equals("ROLE_ADMIN"))) {
        return "admin/index";
    } else {
        return "user/index";
    }
}