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()。
谢谢回复。我尝试修复如下代码。和状态发生变化但尚未解决。
@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'