简体   繁体   English

Spring Security,Boot:替换默认 DaoAuthenticationProvider

[英]Spring Security, Boot: replace default DaoAuthenticationProvider

I am trying to add user ip verification during login process.我正在尝试在登录过程中添加用户 ip 验证。 If ip address of the user is not in the database the application should reject the authentication.如果用户的 IP 地址不在数据库中,则应用程序应拒绝身份验证。

The problem: Given the setup below it turns out that auth.authenticationProvider() is not replacing the default DaoAuthenticationProvider, but adds UserIpAuthenticationProvider as a first AuthenticationProvider in the list.问题:根据下面的设置,结果证明 auth.authenticationProvider() 并没有替换默认的 DaoAuthenticationProvider,而是将 UserIpAuthenticationProvider 添加为列表中的第一个 AuthenticationProvider。

In the case when username/password combination is incorrect the framework ends up calling UserDetailsService.loadUserByUsername() twice, once from UserIpAuthenticationProvider, another time from internal DaoAuthenticationProvider which throws the final BadCredentialsException().在用户名/密码组合不正确的情况下,框架最终调用 UserDetailsS​​ervice.loadUserByUsername() 两次,一次来自 UserIpAuthenticationProvider,另一次来自内部 DaoAuthenticationProvider,它抛出最终的 BadCredentialsException()。

The question: is there any setting that can be set in Spring Boot so that Spring Security does not add it's own internal instance DaoAuthenticationProvider, but only use my UserIpAuthenticationProvider, which already has all the necessary functionality (perhaps by somehow replacing AuthenticationManagerBuilder to be able to override userDetailsService() method?).问题:是否有任何可以在 Spring Boot 中设置的设置,以便 Spring Security 不添加它自己的内部实例 DaoAuthenticationProvider,而只使用我的 UserIpAuthenticationProvider,它已经具有所有必要的功能(也许通过某种方式替换 AuthenticationManagerBuilder 以便能够覆盖 userDetailsS​​ervice() 方法?)。

public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder,T> userDetailsService(
        T userDetailsService) throws Exception {
    this.defaultUserDetailsService = userDetailsService;
    return apply(new DaoAuthenticationConfigurer<AuthenticationManagerBuilder,T>(userDetailsService));
}

Configuration: In my understanding, UserDetailsService is supposed to provide all the necessary details about the user so that AuthenticationProvider can make a decision whether the authentication was successful or not.配置:根据我的理解,UserDetailsS​​ervice 应该提供有关用户的所有必要详细信息,以便 AuthenticationProvider 可以决定身份验证是否成功。

Since all the necessary information is loaded from the database, it seems natural to extend DaoAuthenticationProvider and add an additional verification in overriden additionalAuthenticationChecks() method (white-listed IP list is in the database, so they are loaded as part of the user object in IpAwareUser).由于所有必要的信息都是从数据库中加载的,因此扩展 DaoAuthenticationProvider 并在覆盖的 additionalAuthenticationChecks() 方法中添加额外的验证似乎很自然(白名单 IP 列表在数据库中,因此它们作为用户对象的一部分加载IpAware 用户)。

@Named
@Component
class UserIpAuthenticationProvider  extends DaoAuthenticationProvider {
    @Inject
    public UserIpAuthenticationProvider(UserDetailsService userDetailsService)
    {
        ...
    }

    @SuppressWarnings("deprecation")
    protected void additionalAuthenticationChecks(UserDetails userDetails,
                                                  UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        super.additionalAuthenticationChecks(userDetails, authentication);

        WebAuthenticationDetails details = (WebAuthenticationDetails) authentication.getDetails();
        IpAwareUser ipAwareUser = (IpAwareUser) userDetails;
        if (!ipAwareUser.isAllowedIp(details.getRemoteAddress()))
        {
            throw new DisabledException("Login restricted from ip: " + details.getRemoteAddress());
        }
    }
}

This is injected into SecurityConfiguration:这被注入到 SecurityConfiguration 中:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilter(authenticationFilter);

        http.authorizeRequests()
                .antMatchers("/", "/javascript/**", "/css/**").permitAll()
                .antMatchers("...").access("...")
                .anyRequest().authenticated()
                .and().formLogin().loginPage("/").permitAll()
                .and().logout().invalidateHttpSession(true).deleteCookies("JSESSIONID").permitAll()
                .and().csrf().disable()
        ;
    }

    @Inject
    private UserDetailsService userDetailsService;

    @Inject
    private UserIpAuthenticationProvider userIpAuthenticationProvider;


    @Inject
    private JsonUsernamePasswordAuthenticationFilter authenticationFilter;

    @Bean
    public JsonUsernamePasswordAuthenticationFilter authenticationFilter() {
        return new JsonUsernamePasswordAuthenticationFilter();
    }

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

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler() throws Exception {
        return new JsonAuthenticationSuccessHandler();
    }

    @Bean
    public AuthenticationFailureHandler authenticationFailureHandler() throws Exception {
        return new JsonAuthenticationFailureHandler();
    }
}

and application configuration:和应用程序配置:

@Configuration
@EnableAutoConfiguration
@ComponentScan(basePackageClasses = {SecurityConfiguration.class, DataController.class, DaoService.class})
public class Application extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application;
    }
}

Any guidance on this will be much appreciated.对此的任何指导将不胜感激。

Defining your own DaoAuthenticationProvider定义自己的 DaoAuthenticationProvider

@Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
    return new UserIpAuthenticationProvider();
}

should replace the Spring Boot default instance (not that the bean type is DaoAuthenticationProvider and not UserIpAuthenticationProvider)应该替换 Spring Boot 默认实例(不是 bean 类型是 DaoAuthenticationProvider 而不是UserIpAuthenticationProvider)

The comments on the question contained the answer:对该问题的评论包含以下答案:

@ArunM: your project gave me an idea: I do not need to call auth.userDetailsService(userDetailsService); @ArunM:你的项目给了我一个想法:我不需要调用 auth.userDetailsS​​ervice(userDetailsS​​ervice); in SecurityConfiguration.configure(), which will prevent creation of internal DaoAuthenticationProvider!在 SecurityConfiguration.configure() 中,这将阻止创建内部 DaoAuthenticationProvider! My UserIpAuthenticationProvider can get instance of UserDetailsService via injection.我的 UserIpAuthenticationProvider 可以通过注入获取 UserDetailsS​​ervice 的实例。

The AuthenticationManagerBuilder.userDetailsService method does not only set the default UserDetailsService but also applies a DaoAuthenticationConfigurer which registers the DaoAuthenticationProvider .AuthenticationManagerBuilder.userDetailsService方法不仅设置默认UserDetailsService ,但也适用于一个DaoAuthenticationConfigurer其注册DaoAuthenticationProvider

If you want a customized DaoAuthenticationProvider , pass the UserDetailsService to the provider in the constructor or inject it.如果您想要自定义DaoAuthenticationProvider ,请将UserDetailsService传递给构造函数中的提供者或注入它。 And to prevent the default DaoAuthenticationProvider from being registered, don't call AuthenticationManagerBuilder.userDetailsService .并且为了防止默认的DaoAuthenticationProvider被注册,不要调用AuthenticationManagerBuilder.userDetailsService

This is also mentioned in this Spring Security issue .这个Spring Security issue 中也提到了这一点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM