由于管理层的突发奇想,硬要把我负责的系统塞到其他的项目中去,而系统之间的用户系统又不同,如果要合并到一起,那改动将是非常大,于是就产生这个多用户系统登录的问题。
因为userDetailService是通过用户名来查找用户信息的,所以具体实现得通过多个userDetailService提供用户信息,每个用户系统定义一个userDetailService,在认证的时候根据策略选择相应的userDetailService。这里我们定义一个CustomUserDetailService将userDetailService包装一下,提供一个supports方法作为选择策略。
public interface CustomUserDetailService extends UserDetailsService { Boolean supports(String range);// 判断依据 }
两个系统的表定为user表与member表,接下来我们实现对应的userDetailService,并指定serveice的名称
@Slf4j @Service("adminUserDetailService") public class AdminUserDetailService implements CustomUserDetailService { private final String RANGE = "a"; @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { // ...... } @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 { // ...... } @Override public Boolean supports(String range){ return RANGE.equals(range); } }
接下来我们需要重写AuthenticationProvider类,让它能提供多个userDetailService,并根据策略选择相应的userDetailService进行处理。这里我们复制org.springframework.security.authentication.dao.DaoAuthenticationProvider这个实现类作为我们的实现类,然后定义一个getUserDetailsServices()方法接收多个userDetailService,然后重写retrieveUser()方法来选择应使用的userDetailService。
public class AuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { ·············· private List<CustomUserDetailService> userDetailsServices; protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { this.prepareTimingAttackProtection(); try { String detail = (String) authentication.getDetails(); UserDetails loadedUser = null; for (CustomUserDetailService userDetailsService : this.getUserDetailsServices()){ if(userDetailsService.supports(detail)){ 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); }
最后就是处理登录了,前面我们重写AuthenticationProvider中的retrieveUser()方法时是通过UsernamePasswordAuthenticationToken中的details来进行判断使用哪个userDetailService的,所以在创建UsernamePasswordAuthenticationToken后需要把"range"设置到details中。details是父类中的一个Object类型的成员变量。在登录服务中,创建authenticationToken后,我们设置好details,这里我是在用户实体中新增了一个flag参数,不同的系统传入不同值,当然,如果各个系统调用的登录接口不同,我们也可以不用新增参数直接在这里写死。
public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer { private final Collection<GrantedAuthority> authorities; private Object details; ...... }
public String login(User user) { String username = user.getUsername(); String password = user.getPassword(); // 用户验证 Authentication authentication = null; try { UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password); authenticationToken.setDetails(user.getFlag()); AuthenticationContextHolder.setContext(authenticationToken); // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername authentication = authenticationManager.authenticate(authenticationToken); } catch (Exception e) { }
这样就完成了多用户系统登录的实现。
引用文章:http://dexian.net.cn/archives/springsecurity-duo-ge-yong-hu-biao
标签:userDetailService,String,多用户,Spring,private,range,Security,public,CustomUserDeta From: https://www.cnblogs.com/qq545505061/p/17511591.html