摘要:
首先你要知道spring security的一些常识,登录验证由AuthenticationManager管理,但是它并不处理实际业务逻辑,它里面包含若干个Authenticati...
首先你要知道spring security的一些常识,登录验证由AuthenticationManager管理,但是它并不处理实际业务逻辑,它里面包含若干个Authenticationprovider,由provider来处理具体逻辑,只要我们实现多种Authenticationprovider,就能实现多种方式登录。
首先是常规的代码,先来看两个不同的provider
/** * @Description : 管理员密码验证器 * @Author : wzkris * @Version : V1.0.0 * @Date : 2022/11/28 11:06 */ @Component @Slf4j public class SysUserAuthenticationProvider implements AuthenticationProvider { @Resource(name = "sysUserDetailsServiceImpl") private UserDetailsService userDetailsService; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { UserDetails userDetails = userDetailsService.loadUserByUsername(authentication.getName()); return new UsernamePasswordAuthenticationToken(userDetails, authentication.getCredentials().toString(), userDetails.getAuthorities()); } @Override public boolean supports(Class<?> authentication) { return authentication.equals(SysUserAuthenticationToken.class); } }
/** * @Description : 用户密码验证器 * @Author : wzkris * @Version : V1.0.0 * @Date : 2022/11/28 12:47 */ @Component public class UserAuthenticationProvider implements AuthenticationProvider { @Resource(name = "userDetailsServiceImpl") private UserDetailsService userDetailsService; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { UserDetails userDetails = userDetailsService.loadUserByUsername(authentication.getName()); return new UsernamePasswordAuthenticationToken(userDetails, authentication.getCredentials().toString(), userDetails.getAuthorities()); } @Override public boolean supports(Class<?> authentication) { return authentication.equals(UserAuthenticationToken.class); } }
provider的authentication方法处理具体的验证逻辑,那么supports方法是做什么用的呢?没错,在调用authentication方法之前,manager会先去调用supports方法,判断是否给当前provider处理,根据这一点,我们就能够无线扩展自己的逻辑。
后面的authentication方法会去调用UserDetailService,所以这里也需要两个实现类。
/** * @Description : 移动用户验证处理 * @Author : wzkris * @Version : V1.0.0 * @Date : 2022/11/28 12:48 */ @Service @Slf4j public class UserDetailsServiceImpl implements UserDetailsService { @Resource private BusinessUserService businessUserService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { BusinessUser user = businessUserService.queryByUsername(username); if (StringUtils.isNull(user)) { log.info("登录用户:{} 不存在.", username); throw new ServiceException("登录用户:" + username + " 不存在"); } else if (user.getStatus().equals(Constants.FALSE)) { log.info("登录用户:{} 已被停用.", username); throw new ServiceException("对不起,您的账号:" + username + " 已停用"); } return createLoginUser(user); } public UserDetails createLoginUser(BusinessUser user) { return new MobileUser(user); } }
/** * @Description : 后台用户登录验证处理 * @Author : wzkris * @Version : V1.0.0 * @Date : 2022/11/29 12:38 */ @Service @Slf4j public class SysUserDetailsServiceImpl implements UserDetailsService { @Autowired private ISysUserService userService; @Autowired private SysPasswordService passwordService; @Autowired private SysPermissionService permissionService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { SysUser user = userService.selectUserByUserName(username); if (StringUtils.isNull(user)) { log.info("登录用户:{} 不存在.", username); throw new ServiceException("登录用户:" + username + " 不存在"); } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { log.info("登录用户:{} 已被删除.", username); throw new ServiceException("对不起,您的账号:" + username + " 已被删除"); } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { log.info("登录用户:{} 已被停用.", username); throw new ServiceException("对不起,您的账号:" + username + " 已停用"); } passwordService.validate(user); return createLoginUser(user); } public UserDetails createLoginUser(SysUser user) { return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user)); } }
我这里做的就是后台用户才有权限,前台用户一视同仁
然后拿出来另外两个类
/** * @Description : 后台用户 * @Author : wzkris * @Version : V1.0.0 * @Date : 2022/11/28 14:18 */ public class SysUserAuthenticationToken extends UsernamePasswordAuthenticationToken { /** * 该方法只是用来选择对应的provider处理器 */ public SysUserAuthenticationToken(Object principal, Object credentials) { super(principal, credentials); } }
/** * @Description : 前台用户 * @Author : wzkris * @Version : V1.0.0 * @Date : 2022/11/28 14:19 */ public class UserAuthenticationToken extends UsernamePasswordAuthenticationToken { /** * 该方法只是用来选择对应的provider处理器 */ public UserAuthenticationToken(Object principal, Object credentials) { super(principal, credentials); } }
可以看到,我只是实现了UsernamePasswordAuthenticationToken类,里面的逻辑没有做任何的改造,也就是说,这两个类只是拿来给manager去判断,然后调用相应的provider去处理逻辑。
至此,最初想解决的不同的登录方式对应不同的表的问题迎刃而解,我们还可以利用这一点,去扩展别的登录方式,例如对接微信登录、支付宝登录,或者是邮箱登录、手机验证码登录等等。