Spring security multi user table

Time:2022-4-18

Key words: multi userdetailservice authenticationprovider

Scenario: the fields of Web backend login (user table) and app user login (member table) are quite different, so we don’t want to merge them into one table. There is a problem in both the round check in userdetailservice and the rewriting of providermanager, that is, the user names of the two tables cannot be duplicate (including email, phone and openid of the third-party platform)

Purpose: use different userdetailservices to process login information

Scheme: coexistence of password mode and client mode = = add the range parameter when requesting OAuth / token to determine which userdetailservice to use

Define customuserdetailservice

public interface CustomUserDetailService extends UserDetailsService {

    Boolean supports(String range);//  Judgment basis
}

Implement userdetailservice of user table and member table

@Slf4j
@Service("adminUserDetailService")
public class AdminUserDetailService implements CustomUserDetailService {

    private final String RANGE = "a";

        @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        //Process byloginname byphonecode byopenid byemail 
         return new org. springframework. security. core. userdetails. User (omitted);
    }

    @Override
    public Boolean supports(String range){
        return range.equals(RANGE);
    }
}
@Service("memberUserDetailService")
@Slf4j
public class MemberUserDetailService implements CustomUserDetailService {

    private final String RANGE = "m";

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        //Process byloginname byphonecode byopenid byemail 
        return new org. springframework. security. core. userdetails. User (omitted);
    }

    @Override
    public Boolean supports(String range){
        return range.equals(RANGE);
    }
}

According to org springframework. security. authentication. dao. Daoauthenticationprovider overrides the authenticationprovider class,The key point is to override the retrieveuser () method and provide multiple userdetailservices to handle different range requestsCopy other methods and paste only the code of key changes

public class AuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
     ··············
    private List<CustomUserDetailService> userDetailsServices;

    protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        this.prepareTimingAttackProtection();

        try {
            Map detail = (Map) authentication.getDetails();
            UserDetails loadedUser = null;
            for (CustomUserDetailService userDetailsService : this.getUserDetailsServices()){
                if(userDetailsService.supports(detail.get("range").toString())){
                    loadedUser = userDetailsService.loadUserByUsername(username);
                    break;
                }
            }
            if (loadedUser == null) {
                throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
            } else {
                return loadedUser;
            }
        } catch (UsernameNotFoundException var4) {
            this.mitigateAgainstTimingAttack(authentication);
            throw var4;
        } catch (InternalAuthenticationServiceException var5) {
            throw var5;
        } catch (Exception var6) {
            throw new InternalAuthenticationServiceException(var6.getMessage(), var6);
        }
    }

    public List<CustomUserDetailService> getUserDetailsServices() {
        return userDetailsServices;
    }

    public void setUserDetailsServices(List<CustomUserDetailService> userDetailsServices) {
        this.userDetailsServices = userDetailsServices;
    }
··········
}

Websecurityconfigureradapter configuration

    @Qualifier("adminUserDetailService")
    @Autowired
    private CustomUserDetailService adminUserDetailService;


    @Qualifier("memberUserDetailService")
    @Autowired
    private CustomUserDetailService memberUserDetailService;

    @Override
    protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        AuthenticationProvider authenticationProvider = new AuthenticationProvider();
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        List<CustomUserDetailService> userDetailServices = new ArrayList<>();
        userDetailServices.add(memberUserDetailService);
        userDetailServices.add(adminUserDetailService);
        authenticationProvider.setUserDetailsServices(userDetailServices);
        authenticationManagerBuilder.authenticationProvider(authenticationProvider);
    }


Postman test link:http://localhost:8000/oauth/token?scope=read&grant_type=password&range=a

Spring security multi user table

image.png

Conclusion: in the whole authorization process, spring security uses 13 (forgot how many) filters. These filters are stored and loaded in a filter chain. The trouble is that these filters cannot be replaced or covered. Therefore, in order to meet the needs of this scenario, a curve is used to save the country. I personally boycott the spring series for several reasons. It is not friendly to novices. Many novices are separated from the spring system (most of them don’t see the implementation and source code because they can’t understand it). They can’t do anything and are easy to waste people. Using spring can indeed save a lot of time, but when the business scenario is not supported, the process of finding methods may save more time than you do. Do you use spring or not? I suggest that when you use it, you must understand the principle of implementation and learn the design pattern of spring. After you have achieved both, you can achieve one goal and be controllable.

Please indicate the source for reprint