美文网首页restful实践
Spring Security 多用户表

Spring Security 多用户表

作者: 指尖行动 | 来源:发表于2021-09-07 14:08 被阅读0次

    关键词:多userDetailService AuthenticationProvider

    场景:web后端登录(user表)与app用户登录(member表),字段相差大,不想合并成一张表。不管是在userDetailService里轮查,还是重写ProviderManager , 都存在一个问题, 那就是两张表用户名不能重复(包括 邮箱、电话、第三方平台的openId)

    目的:使用不同的userDetailService来处理登录信息

    方案: 密码模式与客户端模式共存== 请求oauth/token时增加range参数,来确定使用哪个userDetailService

    定义CustomUserDetailService

    public interface CustomUserDetailService extends UserDetailsService {
    
        Boolean supports(String range);// 判断依据
    }
    

    实现user表与member表的userDetailService

    @Slf4j
    @Service("adminUserDetailService")
    public class AdminUserDetailService implements CustomUserDetailService {
    
        private final String RANGE = "a";
    
            @Override
        public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
            // 处理 byLoginName byPhoneCode byOpenId byEmail 
             return new org.springframework.security.core.userdetails.User(略);
        }
    
        @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 {
            // 处理 byLoginName byPhoneCode byOpenId byEmail 
            return new org.springframework.security.core.userdetails.User(略);
        }
    
        @Override
        public Boolean supports(String range){
            return range.equals(RANGE);
        }
    }
    

    根据 org.springframework.security.authentication.dao.DaoAuthenticationProvider 重写AuthenticationProvider类,关键点在于:重写retrieveUser()方法,并提供多个userDetailService来处理不同的range请求 其他的方法照搬,只贴关键改动部分代码

    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 配置

        @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 测试链接:http://localhost:8000/oauth/token?scope=read&grant_type=password&range=a

    image.png

    结论: Spring Security 在整个授权过程中,运用了13个(忘记多少个了)filter,这些filter 是在一个filter chain里存放并加载的,比较麻烦的是,这些filter没有办法替代以及覆盖,所以为了达到这个场景需求,来了一次曲线救国。我个人非常抵制Spring系列,原因有几点,对新手不友好,很多新手脱离了Spring 体系(不看实现、不看源码的那种人,大多因为看不懂),什么都不会,容易把人干废。用Spring,确实是能省很多时间,但当业务场景不被支持的时候,找方法的过程,也许比你省的时间还要多。到底用不用Spring? 个人建议,用的时候,一定要理解实现的原理,并且学习Spring的设计模式,2样都达到之后,自己撸,目的就一个,可控。

    转载请注明出处

    相关文章

      网友评论

        本文标题:Spring Security 多用户表

        本文链接:https://www.haomeiwen.com/subject/kkzuwltx.html