springsecurity通过策略模式设置统一认证接口

 还是回到这张图:我们想不止使用数据库查找的方法去实现认证,而是使用一个统一的认证方法,首先需要修改DaoAuthenticationProvider内实现功能7的方法

protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) 
//详细参见DaoAuthenticationProvider源码,本方法主要是用于判断凭据、匹配密码

因此我们需要重写DaoAuthenticationProvider,将该方法置空,这样后续使用微信扫码、短信验证的时候不需要进入这一环节

@Component
@Slf4j
public class DaoAuthenticationProviderCustom extends DaoAuthenticationProvider {
    @Autowired
    public void setUserDetailsService(UserDetailsServiceImpl userDetailsService) {
        super.setUserDetailsService(userDetailsService);//将自定义的UserDetailsService注入
    }
    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {

    }
}

 

接下来我们继续重构UserDetailsServiceImpl,让其调用不同认证方法实现认证:

创建AuthService接口,通过密码登录实现类,微信扫码实现类等实现接口(策略模式),这时一个接口有了多个实现方法,我们该如何使用具体方法?

1.注入ApplicationContext

@Autowired
ApplicationContext applicationContext;

2.通过getBean方法获取具体bean

AuthService authService = applicationContext.getBean(beanName, AuthService.class);
//authService.xxx()

2.5条悟:)如何实现了UserDetails

public UserDetails loadUserByUsername(String username) {
        AuthParamsDto authParamsDto=null;
        try {
            //将传入的json转成AuthParamsDto
            authParamsDto = JSON.parseObject(username, AuthParamsDto.class);
        } catch (Exception e) {
            throw new RuntimeException("请求认证参数不合要求");
        }

        String authType = authParamsDto.getAuthType();
        String beanName=authType+"AuthServiceImpl";
        AuthService authService = applicationContext.getBean(beanName, AuthService.class);
        XcUserExt xcUser = authService.execute(authParamsDto);
        if(Objects.isNull(xcUser)){
            return null;
        }
        String password= xcUser.getPassword();
        xcUser.setPassword(null);
        String stringXcUser = JSON.toJSONString(xcUser);
        List<SimpleGrantedAuthority> authorities=new ArrayList<>();

        return new User(stringXcUser, password, authorities);
    }

 

3.密码校验实现类

@Service("passwordAuthServiceImpl")
public class PasswordAuthServiceImpl implements AuthService {
    @Autowired
    XcUserMapper xcUserMapper;
    @Autowired
    PasswordEncoder passwordEncoder;
    @Autowired
    CheckCodeClient checkCodeClient;
    @Override
    public XcUserExt execute(AuthParamsDto authParamsDto) {
        if (StringUtils.isEmpty(authParamsDto.getCheckcode())||StringUtils.isEmpty(authParamsDto.getCheckcodekey())) {
             throw new RuntimeException("验证码失效了!再试试吧");
        }
        // feign来校验验证码
        Boolean verify = checkCodeClient.verify(authParamsDto.getCheckcodekey(), authParamsDto.getCheckcode());
        if(verify==null||!verify){
            throw new RuntimeException("验证码校验失败");
        }

        String username = authParamsDto.getUsername();
        XcUser xcUser = xcUserMapper.selectOne(new LambdaQueryWrapper<XcUser>().eq(XcUser::getUsername, username));
        if(Objects.isNull(xcUser)){
            throw new RuntimeException("用户不存在");
        }
        String password = authParamsDto.getPassword();
        if(!passwordEncoder.matches(password,xcUser.getPassword())){
            throw new RuntimeException("账号或密码不正确");
        }
        XcUserExt xcUserExt = new XcUserExt();
        BeanUtils.copyProperties(xcUser,xcUserExt);
        return xcUserExt;
    }
}

 

 

 

2024-9-21:

更新:

人话总结:

先把springsecurity原有的判断方法置空,由我们自己来管控判断是否放行。因为原有的判断方法是必须要验证密码的,而微信登录,验证码登录很明显不会输入密码。置空后,需要将逻辑在密码登录中复现一下。

而在原本的loadUserbyUsername中,我们需要动态获取service,这里就需要上下文容器applicationContext,通过传来的参数来确认使用哪一个serviceimpl,这样就实现了动态调用一个接口的不同实现类。

posted @ 2024-07-28 17:22  天启A  阅读(105)  评论(0)    收藏  举报