• 搞定了!OAuth2使用验证码进行授权


    现在验证码登录已经成为很多应用的主流登录方式,但是对于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

  • 相关阅读:
    Kubernetes DNS服务配置案例
    Dockerfile常用指令
    Docker常用命令
    Kubernetes常用命令
    阿里云ECS安装Kubernetes问题收集与解答
    712. Minimum ASCII Delete Sum for Two Strings
    845. Longest Mountain in Array
    815. Bus Routes
    204. Count Primes
    190. Reverse Bits
  • 原文地址:https://www.cnblogs.com/felordcn/p/16293399.html
Copyright © 2020-2023  润新知