没有WebSecurityConfigurerAdapter的数据库身份验证



我正在尝试使用自定义UserDetails实现实现数据库身份验证。我有三个角色,分别是STUDENT, ADMIN和ADMINTRAINEE(这些是枚举)和为它们提供的一些权限,我从内存db中获取(但我要切换到外部)。这是web安全配置:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ApplicationSecurityConfig {
private final PasswordEncoder passwordEncoder;
private final ApplicationUserService userService;
@Autowired
public ApplicationSecurityConfig(PasswordEncoder passwordEncoder,ApplicationUserService userService) {
this.passwordEncoder = passwordEncoder;
this.userService = userService;
}
@Bean
protected SecurityFilterChain filterChain(HttpSecurity http) 
throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/", "index", "/css/*", "/js/*").permitAll()
.antMatchers("/api/**").hasRole(STUDENT.name())
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.defaultSuccessUrl("/courses", true)
.passwordParameter("password")
.usernameParameter("username")
.and()
.rememberMe()
.tokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(21))
.key("example")
.rememberMeParameter("remember-me") 
.and()
.logout()
.logoutUrl("/logout")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET"))
.clearAuthentication(true)
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID", "remember-me")
.logoutSuccessUrl("/login"); // custom address to redirect after logout
return http.build();
}

// This is what I need to rewrite
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(daoAuthenticationProvider());
}
// Is used to utilize a custom impl of UserDetailsService
@Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setPasswordEncoder(passwordEncoder);
provider.setUserDetailsService(userService);
return provider;
}
}

UserDetailsService的实现是这样的:

@Service
public class ApplicationUserService implements UserDetailsService {
private final ApplicationUserDao applicationUserDao;
@Autowired
public ApplicationUserService(@Qualifier("fake") ApplicationUserDao applicationUserDao) {
this.applicationUserDao = applicationUserDao;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return applicationUserDao
.selectApplicationUserByUsername(username)
.orElseThrow(() ->
new UsernameNotFoundException(String.format("Username %s not found", username)));
}
}

它调用selectApplicationUserByUsername()方法在这里

public interface ApplicationUserDao {
Optional<ApplicationUser> selectApplicationUserByUsername(String username);
}

这是该接口的实现:

@Repository("fake")
public class FakeApplicationUserDaoService implements 
ApplicationUserDao {
private final PasswordEncoder passwordEncoder;
@Autowired
public FakeApplicationUserDaoService(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
@Override
public Optional<ApplicationUser> selectApplicationUserByUsername(String username) {
return getApplicationUsers().stream()
.filter(applicationUser -> username.equals(applicationUser.getUsername()))
.findFirst();
}
private List<ApplicationUser> getApplicationUsers() {
List<ApplicationUser> applicationUsers = Lists.newArrayList(
new ApplicationUser(
"annasmith",
passwordEncoder.encode("password"),
STUDENT.getGrantedAuthorities(),
true,
true,
true,
true
),
new ApplicationUser(
"linda",
passwordEncoder.encode("password"),
ADMIN.getGrantedAuthorities(),
true,
true,
true,
true
),
new ApplicationUser(
"tom",
passwordEncoder.encode("password"),
ADMINTRAINEE.getGrantedAuthorities(),
true,
true,
true,
true
)
);
return applicationUsers;
}
}

这是ApplicationUser类,它是Spring Security使用的UserDetails默认实现类的自定义替代品:

public class ApplicationUser implements UserDetails {
private final Set<? extends GrantedAuthority> grantedAuthorities;
private final String password;
private final String username;
private final boolean isAccountNonExpired;
private final boolean isAccountNonLocked;
private final boolean isCredentialsNonExpired;
private final boolean isEnabled;
public ApplicationUser(String password,
String username,
Set<? extends GrantedAuthority> grantedAuthorities,
boolean isAccountNonExpired,
boolean isAccountNonLocked,
boolean isCredentialsNonExpired,
boolean isEnabled) {
this.grantedAuthorities = grantedAuthorities;
this.password = password;
this.username = username;
this.isAccountNonExpired = isAccountNonExpired;
this.isAccountNonLocked = isAccountNonLocked;
this.isCredentialsNonExpired = isCredentialsNonExpired;
this.isEnabled = isEnabled;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return grantedAuthorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return isAccountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
return isAccountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return isCredentialsNonExpired;
}
@Override
public boolean isEnabled() {
return isEnabled;
}
}

所以这些是我写的角色和权限的枚举(这些的目的只是为了创建角色和用户拥有的权限;学生没有任何权限):

public enum ApplicationUserRole {
STUDENT(Sets.newHashSet()), // Sets is a class from the external library Guava
ADMIN(Sets.newHashSet(COURSE_READ,  COURSE_WRITE, STUDENT_READ, STUDENT_WRITE)),
ADMINTRAINEE(Sets.newHashSet(COURSE_READ, STUDENT_READ));
private final Set<ApplicationUserPermission> permissions;
ApplicationUserRole(Set<ApplicationUserPermission> permissions) {
this.permissions = permissions;
}
public Set<ApplicationUserPermission> getPermissions() {
return permissions;
}
public Set<SimpleGrantedAuthority> getGrantedAuthorities() {
Set<SimpleGrantedAuthority> permissions = getPermissions().stream()
.map(permission -> new SimpleGrantedAuthority(permission.getPermission()))
.collect(Collectors.toSet());
permissions.add(new SimpleGrantedAuthority("ROLE_" + this.name()));
return permissions;
}
}

这是ApplicationUserPermission类:

public enum ApplicationUserPermission {
STUDENT_READ("student:read"),
STUDENT_WRITE("student:write"),
COURSE_READ("course:read"),
COURSE_WRITE("course:write");
private final String permission;
ApplicationUserPermission(String permission) {
this.permission = permission;
}
public String getPermission() {
return permission;
}
}

和PasswordConfig类:

@Configuration
public class PasswordConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(10);
}
}
然后,通过@PreAuthorize注释在ADMIN和ADMINTRAINEE的控制器类中使用授予的权限。所以我的问题是在ApplicationSecurityConfig。我不知道如何调用AuthenticationManangerBuilder来传递我拥有的daoAuthenticationProvider。在旧版本的Spring Security中,我可以用AuthenticationManagerBuilder实例作为参数覆盖configure方法,但由于该抽象类目前已被弃用,因此不再是这种情况。如何重写这个方法呢?或者我一定要这么做吗?如有任何帮助,不胜感激。

我的错误是关于ApplicationUser构造函数中凭据变量的顺序。事实证明这是非常重要的。错误是什么:

public ApplicationUser(String password,
String username,
Set<? extends GrantedAuthority> grantedAuthorities,
boolean isAccountNonExpired,
boolean isAccountNonLocked,
boolean isCredentialsNonExpired,
boolean isEnabled) {
this.grantedAuthorities = grantedAuthorities;
this.password = password;
this.username = username;
this.isAccountNonExpired = isAccountNonExpired;
this.isAccountNonLocked = isAccountNonLocked;
this.isCredentialsNonExpired = isCredentialsNonExpired;
this.isEnabled = isEnabled;

}

应该怎么写:

public ApplicationUser(String username,
String password,
Set<? extends GrantedAuthority> grantedAuthorities,
boolean isAccountNonExpired,
boolean isAccountNonLocked,
boolean isCredentialsNonExpired,
boolean isEnabled) {
this.username = username;
this.password = password;
this.grantedAuthorities = grantedAuthorities;
this.isAccountNonExpired = isAccountNonExpired;
this.isAccountNonLocked = isAccountNonLocked;
this.isCredentialsNonExpired = isCredentialsNonExpired;
this.isEnabled = isEnabled;
}

如果您使用的是Spring Boot版本,WebSecutiryConfigurerAdapter已弃用,则不需要该配置方法。您只需使用第一种方法构建securityFilterChain,然后提供密码编码器并设置userDetailsService用于数据库身份验证。

相关内容

最新更新