定制org.springframework.security.core.userdetails.User以处理User的自定义属性

时间:2018-11-08 14:53:21

标签: spring spring-boot spring-security

基于org.springframework.security.core.userdetails.UserenabledaccountNotExpiredcredentialsNotExpired定制处理认证的accountNotLocked的方式是什么?我的应用程序用户(DB)具有以下可能的状态:

PENDINGACTIVEDEACTIVATEDBLOCKEDSPAMDELETED

我写了一个自定义UserDetailsService

@Service
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    private UserDao userDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userDao.findByEmail(username)
                .orElseThrow(() -> new UsernameNotFoundException("No account found for " + username));

        return new org.springframework.security.core.userdetails.User(user.getEmail(),
                user.getPassword(),true , true, true, true, getAuthorities("ROLE_USER"));
    }

     private Collection<? extends GrantedAuthority> getAuthorities(String role) {
            return Arrays.asList(new SimpleGrantedAuthority(role));
     }

}

到目前为止,我被迫将true硬编码为Spring的User默认具有的所有属性。我希望自己实现这些属性。

编辑:我的自定义Accont状态:
对于spamdelete我希望用户决定(在登录点)是否要恢复其帐户。对于spamblocked,用户将能够访问主页,但除了一条消息外,其他任何内容均不可见。对于deactivated,我想通过单击收到的电子邮件中指定的链接向用户显示他们需要激活帐户。
如何实现呢?在什么级别?

2 个答案:

答案 0 :(得分:0)

只需扩展org.springframework.security.core.userdetails.User类即可添加新的字段和方法(或在需要时覆盖现有字段),这可能比创建实现UserDetails接口的新型用户对象更简单。 / p>

答案 1 :(得分:0)

  

首先:从User扩展您的UserDetails类,并添加必要的字段,例如用户名,密码等。

     

第二步:向其添加enabledaccountNotExpiredcredentialsNotExpiredaccountNotLocked字段,并进行   最后三个为transient,因为您不想保留它们,您   只需使用它们来检查这些条件是否符合方法。

public enum UserStatus{
    PENDING,
    ACTIVE,
    DEACTIVATED,
    BLOCKED,
    SPAM,
    DELETED
}


public class User implements UserDetails {

    //  ... Other field definitions 
    protected LocalDateTime expireDate;
    protected UserStatus status = UserStatus.ACTIVE;   // load from DB
    @Transient
    protected boolean accountNotExpired = true;

    @Transient
    protected boolean accountNotLocked = false;

    @Transient
    protected boolean credentialsNotExpired = true;

    // ...  getters & setters
}

  

通过实现以下方面的自定义方法来实现userService   UserService,更重要的是从UserDetailsService到   实现accountNotExpired的标准逻辑,   credentialsNotExpiredaccountNotLocked

     

例如,这里我仅实现accountNotExpired,您可以添加   您的credentialsNotExpiredaccountNotLocked

public class UserServiceImpl implements UserService, UserDetailsService {
    private final UserRepository userRepository;

    @Autowired
    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) {

        User user = userDao.findByEmail(username)
            .orElseThrow(() -> new UsernameNotFoundException("No account found for " + username));

        // check whether you account expired or not
        if ((user.getExpireDate() != null) && (LocalDateTime.now().isAfter(user.getExpireDate()))) {
            user.setAccountNotExpired(false);
        }

        //  Also decide for credentialsNotExpired and accountNotLocked here



        for (Role role: user.getRoles()) {
            for (Permission permission: role.getPermissions()) {
                user.getAuthorities().add(new SimpleGrantedAuthority(permission.getName()));
            }
        }

        return user;
    }
}

  

现在,根据上一步执行的条件,您将   现在处于通过实施来控制您的用户的位置   AuthenticationProvider为:

public class CustomAuthenticationProvider implements AuthenticationProvider {

    private final UserDetailsService userDetailsService;

    @Autowired
    public CustomAuthenticationProvider(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    public Authentication authenticate(Authentication authentication) {
        String username = authentication.getName();
        BCryptPasswordEncoder bcrypt = new BCryptPasswordEncoder();
        String password = (String) authentication.getCredentials();
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        if (userDetails == null) {
            throw new BadCredentialsException("username not found");
        }
        if (!bcrypt.matches(password, userDetails.getPassword())) {
            throw new BadCredentialsException("password incorrect");
        }
        if (!userDetails.isAccountNonExpired()) {
            throw new CredentialsExpiredException("account expired");
        }
        if (!userDetails.isCredentialsNonExpired()) {
            throw new CredentialsExpiredException("password expired");
        }
        if (!userDetails.isAccountNonLocked()) {
            throw new LockedException("account locked");
        }

        // decision point based on user status   
        if (userDetails.getUserStatus() != UserStatus.DEACTIVATED) {
            throw new DisabledException("account deactivated");
        }else if(userDetails.getUserStatus() != UserStatus.PENDING){
                ...
        }else if(userDetails.getUserStatus() != UserStatus.BLOCKED){
                ...
        }else if(userDetails.getUserStatus() != UserStatus.SPAM){
                ...
        }else if(userDetails.getUserStatus() != UserStatus.DELETED){
                ...
        }


        return new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
    }

    @Override
    public boolean supports(Class << ? > authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
}