Spring Security:将登录表单提交到“.loginProcessingUrl(”/perform_login”)”后出现 stackoverflow 错误

问题描述 投票:0回答:1

intelliJ 调试日志如下。

在此输入图片描述

stackoverflow 错误日志是

2024-07-26T14:01:13.600+09:00 DEBUG 17896 --- [nio-8880-exec-9] o.s.security.web.FilterChainProxy        : Securing POST /perform_login
2024-07-26T14:01:13.638+09:00 ERROR 17896 --- [nio-8880-exec-9] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Filter execution threw an exception] with root cause

java.lang.StackOverflowError: null
    at java.base/java.lang.Exception.<init>(Exception.java:103) ~[na:na]
    at java.base/java.lang.ReflectiveOperationException.<init>(ReflectiveOperationException.java:90) ~[na:na]
    at java.base/java.lang.reflect.InvocationTargetException.<init>(InvocationTargetException.java:67) ~[na:na]
    at jdk.internal.reflect.GeneratedMethodAccessor50.invoke(Unknown Source) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:354) ~[spring-aop-6.1.8.jar:6.1.8]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:216) ~[spring-aop-6.1.8.jar:6.1.8]
    at jdk.proxy2/jdk.proxy2.$Proxy171.authenticate(Unknown Source) ~[na:na]
    at jdk.internal.reflect.GeneratedMethodAccessor50.invoke(Unknown Source) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:354) ~[spring-aop-6.1.8.jar:6.1.8]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:216) ~[spring-aop-6.1.8.jar:6.1.8]
    at jdk.proxy2/jdk.proxy2.$Proxy171.authenticate(Unknown Source) ~[na:na]
    at jdk.internal.reflect.GeneratedMethodAccessor50.invoke(Unknown Source) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]

这里是相关登录流程代码。

会员实体

package org.springframework.samples.manageCS.jpa.manageCS.entity;


import jakarta.persistence.*;
import org.springframework.core.style.ToStringCreator;
import org.springframework.samples.manageCS.jpa.manageCS.MemberRole;

@Entity
@Table(name = "members")
public class Members {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "mem_Id", unique = true)
    private String memId;

    @Column(name = "mem_pw")
    private String memPw;

    @Column(name = "mem_nm")
    private String memNm;
    @Transient
    private String memPwCheck;

    @Column(name = "mem_role")
    @Enumerated(EnumType.STRING)
    private MemberRole memRole;

    public Integer getId() {return this.id;}

    public void setId(Integer id) { this.id = id;}

    public String getMemId() {return this.memId;}

    public void setMemId(String memId) { this.memId = memId;}
    public String getMemPw() {return this.memPw;}
    public void setMemPw(String memPw) { this.memPw = memPw;}
    public String getMemNm() {return this.memNm;}
    public void setMemNm(String memNm) { this.memNm = memNm;}
    public String getMemPwCheck() {return this.memPwCheck;}
    public void setMemPwCheck(String memPwCheck) { this.memPwCheck = memPwCheck;}

    public MemberRole getMemRole() {return memRole;}

    public void setMemRole(MemberRole memRole) {this.memRole = memRole;}

    public boolean isNew() {
       return this.memId == null;
    }

    @Override
    public String toString() {
       return new ToStringCreator(this)
          .append("id", this.id)
          .append("memId", this.memId)
          .append("memPw", this.memPw)
          .append("memNm", this.memNm)
          .toString();
    }
}

会员存储库


package org.springframework.samples.manageCS.jpa.manageCS.repository;

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.Param;
import org.springframework.samples.manageCS.jpa.manageCS.entity.Members;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

public interface MemberRepository extends Repository<Members, Integer> {

    Optional<Members> findByMemId(String memId);

    @Query("SELECT mb FROM Members mb WHERE mb.memId = :memId")
    @Transactional(readOnly = true)
    Optional<Members> findByUsername(String memId);

    @Query("SELECT mb FROM Members mb WHERE mb.memId =:memId")
    @Transactional(readOnly = true)
    Members findMembersById(@Param("memId") String memId);

    void save(Members member);

    @Query("SELECT mem FROM Members mem ORDER BY mem.memId")
    @Transactional(readOnly = true)
    List<Members> findMembers();
}

会员角色

package org.springframework.samples.manageCS.jpa.manageCS;

import lombok.Getter;

@Getter
public enum MemberRole {
    ROLE_ADMIN("ROLE_ADMIN"),
    ROLE_USER("ROLE_USER");

    private final String value;

    MemberRole(String value) {
       this.value = value;
    }

    @Override
    public String toString() {
       return value;
    }
}

自定义用户详细信息服务

package org.springframework.samples.manageCS.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.samples.manageCS.jpa.manageCS.entity.Members;
import org.springframework.samples.manageCS.jpa.manageCS.repository.MemberRepository;
import org.springframework.samples.manageCS.system.CustomUserDetails;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.Collections;

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private MemberRepository memberRepository;

    @Override
    public UserDetails loadUserByUsername(String memId) throws UsernameNotFoundException {
       return memberRepository.findByMemId(memId).map(this::createUserDetails)
          .orElseThrow(() -> new UsernameNotFoundException(memId + "no exist in database"));
    }

    private UserDetails createUserDetails(Members member) {

       String role = member.getMemRole().toString();
       GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role);

       return new org.springframework.security.core.userdetails.User(
          String.valueOf(member.getId()),
          member.getMemPw(),
          Collections.singleton(grantedAuthority)
       );
    }
}

会员安全服务

package org.springframework.samples.manageCS.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.springframework.samples.manageCS.jpa.manageCS.MemberRole;
import org.springframework.samples.manageCS.jpa.manageCS.entity.Members;
import org.springframework.samples.manageCS.jpa.manageCS.repository.MemberRepository;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class MemberSecurityService implements UserDetailsService {
    private final MemberRepository memberRepository;

    @Override
    public UserDetails loadUserByUsername(String memId) throws UsernameNotFoundException {
       System.out.println("loadUserByUsername");
       Optional<Members> _member = this.memberRepository.findByMemId(memId);
       System.out.println(_member);
       if (_member.isEmpty()) {
          throw new UsernameNotFoundException("사용자를 찾을수 없습니다.");
       }
       Members member = _member.get();
       List<GrantedAuthority> authorities = new ArrayList<>();
       if ("admin".equals(memId)) {
          authorities.add(new SimpleGrantedAuthority(MemberRole.ROLE_ADMIN.getValue()));
       } else {
          authorities.add(new SimpleGrantedAuthority(MemberRole.ROLE_USER.getValue()));
       }
       return new User(member.getMemId(), member.getMemPw(), authorities);
    }
}

自定义用户详细信息

package org.springframework.samples.manageCS.system;

import org.springframework.samples.manageCS.jpa.manageCS.entity.Members;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.Collections;

public class CustomUserDetails implements UserDetails {

    private final Members members;

    public CustomUserDetails(Members members) {
       this.members = members;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
       return Collections.singletonList(new SimpleGrantedAuthority(members.getMemRole().toString()));
    }

    @Override
    public String getPassword() {
       return members.getMemPw();
    }

    @Override
    public String getUsername() {
       return members.getMemId();
    }

    @Override
    public boolean isAccountNonExpired() {
       return true;
    }

    @Override
    public boolean isAccountNonLocked() {
       return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
       return true;
    }

    @Override
    public boolean isEnabled() {
       return true;
    }
}

安全配置

package org.springframework.samples.manageCS.system;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
//import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

import static org.springframework.security.config.Customizer.withDefaults;

/**
 * An example of explicitly configuring Spring Security with the defaults.
 *
 * @author Rob Winch
 */
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

//  @Override
//  protected void configure(HttpSecurity http) throws Exception {
//     http
//        .csrf().disable() // Disable CSRF for development purposes
//        .authorizeRequests(authorize -> authorize
//           .anyRequest().authenticated()
//        )
//        .httpBasic(withDefaults())
//        .formLogin(withDefaults());
//  }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
       // @formatter:off
       http
          .csrf((csrf) -> csrf.disable())
          .authorizeHttpRequests((authorize) -> authorize
//           permitAll() : 모든 사용자에게 접근허용, hasAnyRole() : 지정되 사용자에게 접근허용, authenticated() : 로그인 요구
             .requestMatchers("/member/**", "/").permitAll()
//           .requestMatchers("/caller/**").hasAnyRole("USER")
             .requestMatchers("/caller/**").permitAll()
             .requestMatchers("/admin/**").hasAnyRole("ADMIN")
             .anyRequest().permitAll()
          )
          .formLogin((formLogin) ->
             formLogin
                .loginPage("/")                         // 사용자 정의 로그인 url
                .loginProcessingUrl("/perform_login")
                .defaultSuccessUrl("/member/main",true)                // 로그인 성공 후 이동 페이지
                .usernameParameter("memId")
                .passwordParameter("memPw")
                .permitAll()
          );

       http.logout(log -> log
             .logoutUrl("/perform_logout")         // 로그아웃 URL
             .deleteCookies("JSESSIONID")           // 로그아웃 시 쿠키 삭제
             .logoutSuccessUrl("/")                 // 로그아웃 성공 후 리디렉션될 URL
             .permitAll()

       );
       // @formatter:on
       return http.build();
    }

    // @formatter:off
//  @Bean
//  public InMemoryUserDetailsManager userDetailsService() {
//     UserDetails user = User.withDefaultPasswordEncoder()
//        .username("user")
//        .password("password")
//        .roles("USER")
//        .build();
//     return new InMemoryUserDetailsManager(user);
//  }
    // @formatter:on

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

    @Bean
    AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
       return authenticationConfiguration.getAuthenticationManager();
    }
}

错过 CustomUserDetails 或 CustomUserServices。

或角色错误。

或不包含 antMatcher()。

spring authentication security stack-overflow
1个回答
0
投票

谢谢回复。我尝试修复如下代码。和状态发生变化但尚未解决。

@Bean 
AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
    return authenticationConfiguration.getAuthenticationManager();
}

更改以下代码。

@Bean
public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
    AuthenticationManagerBuilder authenticationManagerBuilder =
        http.getSharedObject(AuthenticationManagerBuilder.class);
    authenticationManagerBuilder.userDetailsService(customUserDetailsService)
        .passwordEncoder(passwordEncoder());
    return authenticationManagerBuilder.build();
}

然后运行后,无限运行并生成以下代码。

2024-07-26T16:39:32.285+09:00 DEBUG 9060 --- [nio-8880-exec-3] o.s.s.a.dao.DaoAuthenticationProvider    : Failed to find user 'test3'
2024-07-26T16:39:32.285+09:00 DEBUG 9060 --- [nio-8880-exec-3] o.s.s.a.dao.DaoAuthenticationProvider    : Failed to find user 'test3'
2024-07-26T16:39:32.285+09:00 DEBUG 9060 --- [nio-8880-exec-3] o.s.s.a.dao.DaoAuthenticationProvider    : Failed to find user 'test3'
2024-07-26T16:39:32.285+09:00 DEBUG 9060 --- [nio-8880-exec-3] o.s.s.a.dao.DaoAuthenticationProvider    : Failed to find user 'test3'
2024-07-26T16:39:32.285+09:00 DEBUG 9060 --- [nio-8880-exec-3] o.s.s.a.dao.DaoAuthenticationProvider    : Failed to find user 'test3'
2024-07-26T16:39:32.285+09:00 DEBUG 9060 --- [nio-8880-exec-3] o.s.s.a.dao.DaoAuthenticationProvider    : Failed to find user 'test3'
© www.soinside.com 2019 - 2024. All rights reserved.