Spring Security 401(未经授权)使用UserDetailsService,jpa



我有一个嵌入的h2数据库,我在其中存储用户详细信息,并试图使用这些存储用户的数据授权请求,但只有permitAll((请求有效。

安全配置类:错误很可能来自自动化配置。

package engine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@ComponentScan(basePackages = {"engine"})
@EnableWebSecurity(debug = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
private final APIUserDetailsService userDetailsService;
@Autowired
public SecurityConfiguration(APIUserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) {
http.csrf().disable()
.httpBasic().and()
.authorizeRequests()
.antMatchers("/api/quizzes/**", "/api/quizzes").hasAuthority("ROLE_USER")
.antMatchers("/", "/actuator/shutdown","/h2-console/**", "/api/register").permitAll()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().headers().frameOptions().disable();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

类实现UserDetails我已经硬编码了一些东西,在db中不是必需的。我还在下面附加了User类。

package engine;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
public class APIUserDetails implements UserDetails {
private String userName;
private String password;
private List<GrantedAuthority> authorities;
public APIUserDetails(User user) {
this.userName = user.getEmail();
this.password = user.getPassword();
this.authorities = Arrays.asList(new SimpleGrantedAuthority(user.getRole()));
}
public APIUserDetails() {
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return userName;
}
@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 engine;
import org.springframework.beans.factory.annotation.Autowired;
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.Optional;
@Service
public class APIUserDetailsService implements UserDetailsService {
private UserRepository userRepository;
@Autowired
public APIUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
Optional<User> user = userRepository.findByEmail(userName);
user.orElseThrow(() -> new UsernameNotFoundException("Not Found: " + userName));
return user.map(APIUserDetails::new).get();
}
}

用户

import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
@Email(regexp = ".+@.+\..+")
@NotNull
@Column(unique = true)
private String email;
@NotNull
@Length(min = 5)
private String password;
@JsonIgnore
private String role;
@JsonIgnore
@OneToMany(mappedBy = "user",
cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
private List<Quiz> quizzes;
public User() {
this.quizzes = new ArrayList<>();
this.role = "ROLE_USER";
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public List<Quiz> getQuizzes() {
return quizzes;
}
public void setQuizzes(List<Quiz> quizzes) {
this.quizzes = quizzes;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

用户存储库

package engine;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
Optional<User> findByEmail(String email);
}

关于使用GET/api/测验


Request received for GET '/api/quizzes':
org.apache.catalina.connector.RequestFacade@309aa9c2
servletPath:/api/quizzes
pathInfo:null
headers: 
authorization: Basic dGVzdEBnb29nbGUuY29tOnF3ZXJ0eQ==
user-agent: PostmanRuntime/7.26.8
accept: */*
postman-token: 06cb7b25-176d-411c-9e8d-47b40e5a7820
host: localhost:8889
accept-encoding: gzip, deflate, br
connection: keep-alive

Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
LogoutFilter
BasicAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]

************************************************************

2020-11-14 01:58:42.790  WARN 6630 --- [nio-8889-exec-2] o.s.s.c.bcrypt.BCryptPasswordEncoder     : Encoded password does not look like BCrypt
2020-11-14 01:58:42.795  INFO 6630 --- [nio-8889-exec-2] Spring Security Debugger                 : 
************************************************************

Request received for GET '/error':
org.apache.catalina.core.ApplicationHttpRequest@54bf9f9a
servletPath:/error
pathInfo:null
headers: 
authorization: Basic dGVzdEBnb29nbGUuY29tOnF3ZXJ0eQ==
user-agent: PostmanRuntime/7.26.8
accept: */*
postman-token: 06cb7b25-176d-411c-9e8d-47b40e5a7820
host: localhost:8889
accept-encoding: gzip, deflate, br
connection: keep-alive

Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
LogoutFilter
BasicAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]

由于您使用的是BCryptPasswordEncoder,请确保数据库中的密码经过了正确的哈希处理。要进行检查,应确保密码以$2a开头。

注意删除以下代码是安全的,因为Spring Security自动使用作为Bean公开的UserDetailsPasswordEncoder

private final APIUserDetailsService userDetailsService;
@Autowired
public SecurityConfiguration(APIUserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}

最新更新