将数据库身份验证添加到 Spring Data Rest 应用程序

Add database authentication to Spring Data Rest application

我正在使用 Spring Data REST with Thymeleaf 创建一个应用程序。

最初我创建了我的模型、控制器、dao 和服务。一切正常。我现在正在尝试为我的应用程序添加安全性。现在我只关注 login/logout.

我已经能够如下创建内存中身份验证:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    

    
    @Autowired
    @Qualifier("securityDataSource")
    private DataSource securityDataSource;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        
        // add users for in memory authentication
        UserBuilder users = User.withDefaultPasswordEncoder();
        
        auth.inMemoryAuthentication()
        .withUser(users.username("paul").password("test123").roles("MEMBER", "ADMIN"))
        .withUser(users.username("sandra").password("test123").roles("MEMBER", "ADMIN"))
        .withUser(users.username("matthew").password("test123").roles("MEMBER"));
    }

}

不过我想将其更改为数据库身份验证。我很确定我可以创建一个 jdbc 连接并将我的配置方法更改为如下所示:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.jdbcAuthentication().dataSource(securityDataSource);
        
}

我的问题是我已经在通过我的 DAO 接口访问数据库了。例如:

public interface UserRepository extends JpaRepository<User, Integer> {
    
    // method to sort by last name
    public List<User> findAllByOrderByLastNameAsc();

}

我的用户 table 有一个电子邮件和密码列,将用作 username/password。

是否也可以通过某种方式使用它来进行身份验证?我可以提供额外的信息,但我不愿意只 post 一切,希望有人能帮我写。

假设db table name 是用户和权限。数据源配置在application.yml.

    @Autowired
    private DataSource dataSource;
    
@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication()
                .dataSource(dataSource)
                .usersByUsernameQuery("select username,password,enabled from users WHERE username=?")
                .authoritiesByUsernameQuery("select username,authority from authorities where username=?")
                .passwordEncoder(new BCryptPasswordEncoder());
                }
    }

由于您已经创建了 DAO 接口,因此创建起来可能更容易 UserDetailsService 实施:

@Service
@NoArgsConstructor @ToString @Log4j2
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired private UserRepository userRepository = null;

    @Override
    @Transactional(readOnly = true)
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        org.springframework.security.core.userdetails.User user = null;

        try {
            Optional<User> optional = userRepository.findBy...(username);
            HashSet<GrantedAuthority> set = new HashSet<>();
            /*
             * Add SimpleGrantedAuthority to set as appropriate
             */
            user = new org.springframework.security.core.userdetails.User(username, optional.get().getPassword(), set);
        } catch (UsernameNotFoundException exception) {
            throw exception;
        } catch (Exception exception) {
            throw new UsernameNotFoundException(username);
        }

        return user;
    }
}

并将其连接到:

    @Autowired private UserDetailsService userDetailsService = null;
    ... private PasswordEncoder passwordEncoder = ...;

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
            .passwordEncoder(passwordEncoder);
    }

为了更加清楚,这里是我实施的完整上下文:

@Service
@NoArgsConstructor @ToString @Log4j2
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired private CredentialRepository credentialRepository = null;
    @Autowired private AuthorityRepository authorityRepository = null;

    @Override
    @Transactional(readOnly = true)
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = null;

        try {
            Optional<Credential> credential = credentialRepository.findById(username);
            Optional<Authority> authority = authorityRepository.findById(username);
            HashSet<GrantedAuthority> set = new HashSet<>();

            if (authority.isPresent()) {
                authority.get().getGrants().stream()
                    .map(Authorities::name)
                    .map(SimpleGrantedAuthority::new)
                    .forEach(set::add);
            }

            user = new User(username, credential.get().getPassword(), set);
        } catch (UsernameNotFoundException exception) {
            throw exception;
        } catch (Exception exception) {
            throw new UsernameNotFoundException(username);
        }

        return user;
    }
}