自定义WebSecurityConfigurerAdapter

Kai*_*ugo 8 spring dependency-injection spring-security spring-boot

我有这个问题使用SpringBoot和SpringBoot-Security实现自定义登录身份验证.我创建了一个Bitbucket存储库作为该线程的参考(在CustomSecuringWeb分支中).在此之前,大多数评论都遵循" 保护Web应用程序安全"教程.

问题是,我很好奇,现在验证数据如何来自数据库,而不仅仅是内存数据(这在生产线应用程序中很常见).

在整个过程中,我做了两次尝试(虽然两次尝试都位于同一个分支上 - 我的不好).

  1. 创建了一个自定义UserDetailsService实现
  2. 创建了一个自定义AbstractUserDetailsAuthentictionProvider实现

我不知道问题出在哪里,但在检查控制台日志时,两者都返回每个自定义类的持久性(甚至存储库)DI为空.

问题是我怎样才能使两个尝试都有效.并且(可能)两次尝试中的哪一次比另一次更好.

mar*_*bog 11

首先,这两种方法用于不同目的而不可互换.

情况1:

UserDetailsService纯粹用作DAO来通过身份验证定位用户信息,并根据该信息验证用户,不需要在其中进行身份验证UserDetailsService,只需要数据访问.规格明确提到.这就是你要找的东西.

案例2:

AuthentictionProvider另一方面,用于提供自定义身份验证方法,例如,如果要在登录名和密码以外的字段上进行自定义身份验证,则可以通过实现AuthentictionProvider并将此对象提供给您的方法来实现AuthenticationManagerBuilder.我不认为这是你想要在你的项目中做的事情.您只是希望使用登录名和密码来实现基于存储在数据库中的用户的身份验证,这是默认方式.在上面刚刚实现的案例1中,由容器为您创建了UserDetailsService实例,因为您提供了UserDetailsS​​ervice,除了系统中用于检索用户身份验证的DAO之外别无其他.AuthentictionProviderAuthenticationManagerDaoAuthenticationProvider

现在,在您的配置中,而不是:

  @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        auth.userDetailsService(new AdminSecurityService());
        auth.authenticationProvider(new AdminSecurityAuthenticationProvider());
    }
Run Code Online (Sandbox Code Playgroud)

做这样的事情

@Autowired
private CustomUserDetailsService userDetailsService;

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

}
Run Code Online (Sandbox Code Playgroud)

CustomUserDetailsService必须实施org.springframework.security.core.userdetails.UserDetailsService

@Service
public class CustomUserDetailsService implements UserDetailsService {

    private final AdminRepository userRepository;

    @Autowired
    public CustomUserDetailsService(AdminRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Admin user = userRepository.findByLogin(username);
        if (user == null) {
            throw new UsernameNotFoundException(String.format("User %s does not exist!", username));
        }
        return new UserRepositoryUserDetails(user);
    }

    private final static class UserRepositoryUserDetails extends Admin implements UserDetails {

        private static final long serialVersionUID = 1L;

        private UserRepositoryUserDetails(User user) {
            super(user);
        }

        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            return AuthorityUtils.createAuthorityList("ROLE_USER");
        }

        @Override
        public String getUsername() {
            return getLogin();//inherited from user
        }

        @Override
        public boolean isAccountNonExpired() {
            return true;//not for production just to show concept
        }

        @Override
        public boolean isAccountNonLocked() {
            return true;//not for production just to show concept
        }

        @Override
        public boolean isCredentialsNonExpired() {
            return true;//not for production just to show concept
        }

        @Override
        public boolean isEnabled() {
            return true;//not for production just to show concept
        }
//getPassword() is already implemented in User.class
    }

}
Run Code Online (Sandbox Code Playgroud)

当然,实现取决于您,但您必须能够根据检索到的用户(在您的情况下为Admin.class)提供用户密码和该接口中的其余方法.希望能帮助到你.我没有运行这个例子,所以如果我做了一些拼写错误继续询问是否有什么不起作用.如果你不需要,我也会从你的项目中删除'AuthentictionProvider'.

这里有文档:http://docs.spring.io/spring-security/site/docs/4.0.0.RC1/reference/htmlsingle/#tech-userdetailsservice

评论之后: 您可以PasswordEncoder在配置方法中设置,而不会有太多麻烦:

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

    }
@Bean
    public PasswordEncoder passwordEncoder(){
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder;
    }
Run Code Online (Sandbox Code Playgroud)

您可以这样做,因为您可以获得AbstractDaoAuthenticationConfigurer返回的访问权限,auth.userDetailsService(userDetailsService)并且允许您进行配置DaoAuthenticationProvider,这是您选择使用时的首选提供商UserDetailsService.你是对PasswordEncoder的,AuthenticationProvider但是你不必实现AuthenticationProvider只使用从中返回的convineince对象auth.userDetailsService(userDetailsService)并在该对象上设置编码器,这将AuthenticationPriovider在你DaoAuthenticationProvider已经为你创建的情况下传递给它.就像评论中提到的走鹃一样,您很少需要实现自己的AuthenticationProvider通常大多数身份验证配置调整可以通过使用AbstractDaoAuthenticationConfigurer上面提到的返回来完成auth.userDetailsService(userDetailsService).

"如果我想添加密码加密.如果我想进行其他身份验证(例如检查用户是否已锁定,活动,用户是否仍然登录等等[不包括密码哈希])将使用AuthenticationProvider的".

作为标准身份验证机制的一部分,您无法做到这一点 http://docs.spring.io/autorepo/docs/spring-security/3.2.0.RELEASE/apidocs/org/springframework/security/core/userdetails/UserDetails. html 如果查看界面,UserDetails您会看到如果上述任何方法返回false,则身份验证将失败.AuthenticationProvider非常非标准的情况下确实需要实施.框架几乎涵盖了所有标准内容.