自定义WebSecurityConfigurerAdapter [英] Custom WebSecurityConfigurerAdapter

查看:116
本文介绍了自定义WebSecurityConfigurerAdapter的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有使用SpringBoot和SpringBoot-Security实现自定义登录身份验证的问题.我制作了一个 Bitbucket存储库作为该线程的参考(在保护Web应用程序教程.

问题是,我很好奇,现在认证数据怎么可能来自数据库,而不仅仅是存储数据(这在生产线应用程序中很常见).

在整个过程中,我进行了两次尝试(尽管两次尝试都位于同一分支上-对此我不好).

  1. 创建了自定义的 UserDetailsS​​ervice 实现
  2. 创建了自定义 AbstractUserDetailsAuthentictionProvider 实现

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

问题是我怎样才能使这两种尝试都起作用.并且(可能)两次尝试中的哪一次都比另一次更好.

解决方案

首先,这两种方法用于不同的目的,并且不能互换.

情况1:

UserDetailsS​​ervice 纯粹用作DAO,通过身份验证来定位用户信息,并基于该信息对用户进行身份验证,因此在 UserDetailsS​​ervice 中不应进行身份验证,而只能进行数据访问.规格中明确提到了这一点.这就是您要寻找的.

案例2:

另一方面,

AuthentictionProvider 用于提供自定义身份验证方法,例如,如果要在登录名和密码之外的其他字段上自定义身份验证,则可以通过实现 AuthentictionProvider ,并将此对象提供给您的 AuthenticationManagerBuilder .我认为这不是您要在项目中执行的操作.您只想使用默认的登录名和密码,基于存储在数据库中的用户来实现身份验证.在上面仅实现了 UserDetailsS​​ervice 案例1 中,容器在 AuthenticationManager 中为您创建了 AuthentictionProvider 的实例因为您提供了UserDetailsS​​ervice,所以它是 DaoAuthenticationProvider ,它仅是系统中用于检索用户进行身份验证的DAO.

现在,您可以实施了,在您的配置中,而不是:

  @Override受保护的void configure(AuthenticationManagerBuilder auth)引发异常{//auth.userDetailsS​​ervice(new AdminSecurityService());auth.authenticationProvider(new AdminSecurityAuthenticationProvider());} 

做这样的事情

  @Autowired私人CustomUserDetailsS​​ervice userDetailsS​​ervice;@Override受保护的void configure(AuthenticationManagerBuilder auth)引发异常{auth.userDetailsS​​ervice(userDetailsS​​ervice);} 

和您的 CustomUserDetailsS​​ervice 必须实现 org.springframework.security.core.userdetails.UserDetailsS​​ervice

  @Service公共类CustomUserDetailsS​​ervice实现UserDetailsS​​ervice {私有的最终AdminRepository userRepository;@Autowiredpublic CustomUserDetailsS​​ervice(AdminRepository userRepository){this.userRepository = userRepository;}@Overridepublic UserDetails loadUserByUsername(String username)抛出UsernameNotFoundException {管理员用户= userRepository.findByLogin(username);如果(使用者==空值){抛出新的UsernameNotFoundException(String.format(用户%s不存在!",用户名));}返回新的UserRepositoryUserDetails(user);}私有最终静态类UserRepositoryUserDetails扩展了Admin实现的UserDetails {私有静态最终长serialVersionUID = 1L;private UserRepositoryUserDetails(User user){超级用户}@Override公共收藏扩展GrantedAuthority>getAuthorities(){返回AuthorityUtils.createAuthorityList("ROLE_USER");}@Override公共字符串getUsername(){返回getLogin();//从用户继承}@Overridepublic boolean isAccountNonExpired(){返回true;//不用于生产只是为了显示概念}@Overridepublic boolean isAccountNonLocked(){返回true;//不用于生产只是为了显示概念}@Overridepublic boolean isCredentialsNonExpired(){返回true;//不用于生产只是为了显示概念}@Overridepublic boolean isEnabled(){返回true;//不用于生产只是为了显示概念}//getPassword()已在User.class中实现}} 

当然可以实现,但您必须能够提供用户密码以及该接口中基于检索到的用户的其他方法(在您的情况下为Admin.class).希望能帮助到你.我没有运行此示例,所以如果我进行一些拼写检查,然后询问是否有问题.如果您不需要它,我还将从您的项目中删除该"AuthentictionProvider".

您在这里获得了文档:

否,这是标准身份验证机制的一部分,无法为您完成 http://docs.spring.io/autorepo/docs/spring-security/3.2.0.RELEASE/apidocs/org/springframework/security/core/userdetails/UserDetails.html 如果查看接口 UserDetails ,您会发现,如果上述任何方法返回错误的身份验证,都将失败.在非常不标准的情况下,确实需要实现 AuthenticationProvider .框架几乎涵盖了所有标准内容.

I have this problem implementing a custom login authentication using SpringBoot and SpringBoot-Security. I made a Bitbucket repository as reference for this thread (within CustomSecuringWeb branch). Before anything else, most of the comments here follows the Securing a Web Application tutorial.

The thing is, I was curious as how could the authentication data is now from the database instead of just memory data (which is very common in production line applications).

Throughout the process I made two attempts (though both attempts are located on the same branch - my bad for that).

  1. Created a custom UserDetailsService implementation
  2. Created a custom AbstractUserDetailsAuthentictionProvider implementation

I don't know where the problem lies, but upon checking the console log both returns that the persistence(even the repository) DI on each custom class where null.

The question is how could I make both attempts working. And (possibly) which one of the two attempts is better than the other.

解决方案

First of all the two approaches are used for different purpose and not interchangeable.

Case 1:

UserDetailsService is used purely as DAO to locate user information by your authentication and based on that info authenticate user, no authentication should be done within UserDetailsService, just data access. Specifications clearly mention that. This is what you are looking for.

Case2:

AuthentictionProvider on the other hand is used for providing custom method of authentication, for example if you want to custom authenticate on fields other than login and password you may do that by implementing AuthentictionProvider and supplying this object to your AuthenticationManagerBuilder. I do not think this is what you want to do in you project. You are just looking to implement your authentication based on users stored in database using login and password which is default way. In above Case 1 where you implemented just UserDetailsService, instance of AuthentictionProvider was created for you in AuthenticationManager by the container and it was DaoAuthenticationProvider since you supplied UserDetailsService which is nothing else but DAO in your system that is used to retrive user for authentication.

Now to your implementation, in your configuration instead of :

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

do something like this

@Autowired
private CustomUserDetailsService userDetailsService;

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

}

and your CustomUserDetailsService has to implement 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
    }

}

of course implementation is up to you but you have to be able to provide user password, and rest of the methods in that interface based on the retrieved user (Admin.class in your case). Hope it helps. I did not run this example so if I made some typos go ahead and ask if something does not work. I would also get rid of that 'AuthentictionProvider' from your project if you don't need it.

Here you got documentation:http://docs.spring.io/spring-security/site/docs/4.0.0.RC1/reference/htmlsingle/#tech-userdetailsservice

After comments: You can set PasswordEncoder in your configure method without too much hassle just do:

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

    }
@Bean
    public PasswordEncoder passwordEncoder(){
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder;
    }

You can do that because you get access to AbstractDaoAuthenticationConfigurer returned from auth.userDetailsService(userDetailsService) and it allows you to configure DaoAuthenticationProvider, which is your provider of choice when you choose to use UserDetailsService. You are right PasswordEncoder is set in AuthenticationProvider but you do not have to implement AuthenticationProvider just use convineince object that is returned from auth.userDetailsService(userDetailsService) and set your encoder on that object which will pass it to AuthenticationPriovider in your case DaoAuthenticationProvider that was already created for you. Just like roadrunner mentioned in the comment you very rarely need to implement your own AuthenticationProvider usually most of authentication configuration adjustments can be done with the use of AbstractDaoAuthenticationConfigurer which as mentioned above is returned from auth.userDetailsService(userDetailsService).

"And if I ever wanted to add a password encryption. And if I ever wanted to do other authentication (like checking if the user is locked, active, user is still logged-in, etc. [excluding password hashing]) will use the AuthenticationProvider."

No this is done for you as part of standard authentication mechanism http://docs.spring.io/autorepo/docs/spring-security/3.2.0.RELEASE/apidocs/org/springframework/security/core/userdetails/UserDetails.html If you look at the interface UserDetails you will see that if any of the above methods returns false authentication will fail. Implementing AuthenticationProvider is really needed in very nonstandard cases. All standard stuff is pretty much covered by the framework .

这篇关于自定义WebSecurityConfigurerAdapter的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆