diff --git a/spring-security/step-1/ReadMe.md b/spring-security/step-1/ReadMe.md index 4ed2da5..afe6132 100644 --- a/spring-security/step-1/ReadMe.md +++ b/spring-security/step-1/ReadMe.md @@ -255,4 +255,42 @@ public class MD5PasswordEncoder implements PasswordEncoder { return true; } } +``` + +## 自定义UserDetailsService + +在Spring Security中,如果需要自定义用户认证逻辑,可以通过实现`UserDetailsService`接口来完成。以下是正确实现方式: + +### 标准实现示例 + +```java +@Service +public class CustomUserDetailsService implements UserDetailsService { + + private final PasswordEncoder passwordEncoder; + + // 推荐使用构造器注入 + public CustomUserDetailsService(PasswordEncoder passwordEncoder) { + this.passwordEncoder = passwordEncoder; + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + // 1. 这里应该根据username从数据库或其他存储中查询用户信息 + // 以下是模拟数据,实际应用中应从数据库查询 + + // 2. 如果用户不存在,抛出UsernameNotFoundException + if (!"bunny".equalsIgnoreCase(username)) { + throw new UsernameNotFoundException("User not found: " + username); + } + + // 3. 构建UserDetails对象返回 + return User.builder() + .username(username) // 使用传入的用户名 + .password(passwordEncoder.encode("123456")) // 密码应该已经加密存储,这里仅为示例 + .roles("USER") // 角色会自动添加ROLE_前缀 + .authorities("read", "write") // 添加具体权限 + .build(); + } +} ``` \ No newline at end of file diff --git a/spring-security/step-1/src/main/java/com/spring/security/config/SecurityWebConfiguration.java b/spring-security/step-1/src/main/java/com/spring/security/config/SecurityWebConfiguration.java index 97fef2f..ec537d2 100644 --- a/spring-security/step-1/src/main/java/com/spring/security/config/SecurityWebConfiguration.java +++ b/spring-security/step-1/src/main/java/com/spring/security/config/SecurityWebConfiguration.java @@ -1,11 +1,14 @@ package com.spring.security.config; +import com.spring.security.handler.SecurityAccessDeniedHandler; +import com.spring.security.handler.SecurityAuthenticationEntryPoint; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; 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.configurers.AbstractHttpConfigurer; import org.springframework.security.web.SecurityFilterChain; @EnableMethodSecurity @@ -18,7 +21,7 @@ public class SecurityWebConfiguration { String[] permitAllUrls = { "/", "/doc.html/**", "/webjars/**", "/images/**", ".well-known/**", "favicon.ico", "/error/**", - "/v3/api-docs/**" + "/swagger-ui/**", "/v3/api-docs/**" }; http.authorizeHttpRequests(authorizeRequests -> @@ -49,7 +52,14 @@ public class SecurityWebConfiguration { .logout(logout -> logout .logoutSuccessUrl("/login-page?logout=true") .permitAll() - ); + ) + .csrf(AbstractHttpConfigurer::disable) + .exceptionHandling(configurer -> configurer + .accessDeniedHandler(new SecurityAccessDeniedHandler()) + .authenticationEntryPoint(new SecurityAuthenticationEntryPoint()) + ) + ; + return http.build(); } } \ No newline at end of file diff --git a/spring-security/step-1/src/main/java/com/spring/security/service/CustomUserDetailsService.java b/spring-security/step-1/src/main/java/com/spring/security/service/CustomUserDetailsService.java new file mode 100644 index 0000000..fb727b3 --- /dev/null +++ b/spring-security/step-1/src/main/java/com/spring/security/service/CustomUserDetailsService.java @@ -0,0 +1,35 @@ +package com.spring.security.service; + +import lombok.RequiredArgsConstructor; +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.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class CustomUserDetailsService implements UserDetailsService { + + private final PasswordEncoder passwordEncoder; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + // 1. 这里应该根据username从数据库或其他存储中查询用户信息 + // 以下是模拟数据,实际应用中应从数据库查询 + + // 2. 如果用户不存在,抛出UsernameNotFoundException + if (!"bunny".equalsIgnoreCase(username)) { + throw new UsernameNotFoundException("User not found: " + username); + } + + // 3. 构建UserDetails对象返回 + return User.builder() + .username(username) // 使用传入的用户名 + .password(passwordEncoder.encode("123456")) // 密码应该已经加密存储,这里仅为示例 + .roles("USER") // 角色会自动添加ROLE_前缀 + .authorities("read", "write") // 添加具体权限 + .build(); + } +} \ No newline at end of file