现在验证码登录已经成为很多应用的主流登录方式,但是对于OAuth2授权来说,手机号验证码处理用户认证就非常繁琐,很多同学却不知道怎么接入。
认真研究胖哥Spring Security OAuth2专栏的都会知道一个事,OAuth2其实不管资源拥有者是如何认证的,只要资源拥有者在授权的环节中认证了就可以了,至于你是验证码、账密,甚至是什么指纹虹膜都无所谓。
Id Server实现
因此胖哥好像找到了将验证码接入Id Server的方式,前面胖哥开源了一个Spring Security的登录扩展包spring-security-login-extension,可以一键接入验证码登录和小程序登录,利用这个应该就能实现。因此我就改造了一番成功实现了这一功能。看下效果:
和之前相比,用户在授权过程中可以选择账密登录或者手机验证码登录。
这里你变通一下,是不是只要是验证码登录都可以兼容进去了呢?
大致原理
这里需要前后端协同实现。
后端
核心还是扩展包的用法,给HttpSecurity
加入LoginFilterSecurityConfigurer
配置,这里我改动了一下和原来包中的不太一样。这里登录成功后不能再返回JWT
了,需要和账密登录保持一致,核心代码如下:
httpSecurity.apply(new LoginFilterSecurityConfigurer<>()) // 手机号验证码登录模拟 .captchaLogin(captchaLoginConfigurer -> // 验证码校验 1 在此处配置 优先级最高 2 注册为Spring Bean 可以免配置 captchaLoginConfigurer.captchaService(this::verifyCaptchaMock) // 根据手机号查询用户UserDetials 1 在此处配置 优先级最高 2 注册为Spring Bean 可以免配置 .captchaUserDetailsService(this::loadUserByPhoneMock) // 两个登录保持一致 .successHandler(loginAuthenticationSuccessHandler) // 两个登录保持一致 .failureHandler(authenticationFailureHandler);
其中loadUserByPhoneMock
是模拟CaptchaUserDetailsService
接口,根据手机号加载UserDetails
:
private UserDetails loadUserByPhoneMock(String phone) throws UsernameNotFoundException { return // 用户名 User.withUsername(phone) // 密码 .password("password") .passwordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder()::encode) .roles("user", "mobile") .build(); }
verifyCaptchaMock
是验证码校验逻辑接口CaptchaService
的模拟,这里写死为1234
,实际开发中应该用缓存实现,从发码接口中存入缓存,在CaptchaService
中调用缓存接口取出验证码进行校验:
private boolean verifyCaptchaMock(String phone, String code) { //todo 自己实现缓存校验逻辑 return code.equals("1234");}
验证码发送的接口自由实现,这里不需要去定义规范,记得接入缓存就行了。
前端
前端只需要接入一个可以切换登录方式的登录页就行了。然后把验证码登录接口和发送验证码接口配进去就行了,授权登录页面为oauth2_login.html
,通过其控制器,胖哥甚至加了一个开关enableCaptchaLogin
来决定是否使用验证码认证方式。
@GetMapping("/login")public String oauth2LoginPage(Model model, @CurrentSecurityContext(expression = "authentication") Authentication authentication, @Value("${spring.security.oauth2.server.login.captcha.enabled:true}") boolean enableCaptchaLogin, @RequestAttribute(name = "org.springframework.security.web.csrf.CsrfToken", required = false) CsrfToken csrfToken) { if (!(authentication instanceof AnonymousAuthenticationToken)){ return "redirect:/"; } if (csrfToken != null) { model.addAttribute("_csrfToken", csrfToken); } SystemSettings systemSettings = new SystemSettings(); model.addAttribute("enableCaptchaLogin",enableCaptchaLogin); model.addAttribute("systemSettings", systemSettings); return "oauth2_login";}
前端和验证码相关的JS处理:
if ([[${enableCaptchaLogin}]]){ form.on('submit(mobile-login)', function (data) { let loader = layer.load(); let btn = button.load({elem: '.login'}); $.ajax({ url: '/login/captcha', data: data.field, type: "post", dataType: 'json', success: function (result) { layer.close(loader); btn.stop(function () { if (result.code === 200) { popup.success(result.msg, function () { location.href = result.data.targetUrl; }) } else if (result.code === 401) { popup.failure(result.msg); } }) } }); return false; }); $('#captcha-btn').click(function (){ //TODO 这里接入验证码接口 popup.success('验证码已发送'); }) }
关于Id Server
Id Server是一个基于Spring Authorization Server的开源的授权服务器,大大降低OAuth2授权服务器的学习使用难度,提供UI控制台,动态权限控制,方便OAuth2客户端管理,可以一键生成Spring Security配置,开箱即用,支持集成Spring Boot、Spring Cloud等java生态的框架,甚至支持其它语言,少量配置就可部署,代码开源,方便二次开发,支持OAuth2四种客户端认证方式和三种授权模式,支持账密认证和验证码认证。欢迎学习使用并参与代码贡献。
总结
OAuth2使用验证码进行授权已经实现了,适用于所有Id Server提供的DEMO。如果有兴趣可以从以下仓库地址获取最新的验证码授权代码,记得给个Star哦:
https://github.com/NotFound403/id-server
另外还有人问Id Server和胖哥Spring Security OAuth2专栏的关系,Id Server是一个开源项目,底层的逻辑支撑来自对Spring Authorization Server的分析,掌握专栏的知识可以帮助你对Id Server的自定义改造,Id Server目标是打造一个生产可用的OAuth2授权服务器,降低OAuth2的学习使用成本,希望大家多多支持。
关注公众号:Felordcn获取更多资讯
个人博客:https://felord.cn
原文:https://juejin.cn/post/7099761823456428045