【认证服务】验证码、社交登录、分布式session、单点登录


https://blog.csdn.net/hancoder/article/details/114242184

一.验证码

package com.xunqi.gulimall.auth.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class GulimallWebConfig implements WebMvcConfigurer {

    /**·
     * 视图映射:发送一个请求,直接跳转到一个页面
     * @param registry
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {

        // registry.addViewController("/login.html").setViewName("login");
        registry.addViewController("/reg.html").setViewName("reg");
    }
}

(1) 验证码倒计时js

$(function () {
    $("#sendCode").click(function () {
        if ($(this).hasClass("disabled")) {
            // 1.进入倒计时效果
        } else {
            $.get("/sms/sendcode?phone=" + $("#phoneNum").val(), function (data) {
                if (data.code != 0) {
                    layer.msg(data.msg)
                }
            });
            
            // 2.给指定手机号发送验证码
            timeoutChangeStyle()
        }
    })
})

// 外部变量计时
let num = 60;

function timeoutChangeStyle() {
    $("#sendCode").attr("class", "disabled")
    if (num == 0) {//可以再次发送
        num = 60;
        $("#sendCode").attr("class", "");//取消disabled
        $("#sendCode").text("发送验证码");
    } else {
        var str = num + "s 后再次发送";
        $("#sendCode").text(str);
        // 1s后回调
        setTimeout("timeoutChangeStyle()", 1000);
    }
    num--
}

(2) 阿里云-短信服务

@Data
@ConfigurationProperties(prefix = "spring.cloud.alicloud.sms")
@Component
public class SmsComponent {

    private String host;

    private String path;

    private String skin;

    private String sign;

    private String appCode;

    public String sendSmsCode(String phone, String code){
        String method = "GET";
        Map headers = new HashMap();
        //最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105
        headers.put("Authorization", "APPCODE " + this.appCode);
        Map querys = new HashMap();
        querys.put("code", code);
        querys.put("phone", phone);
        querys.put("skin", this.skin);
        querys.put("sign", this.sign);
        HttpResponse response = null;
        try {
            response = HttpUtils.doGet(this.host, this.path, method, headers, querys);
            //获取response的body
            if(response.getStatusLine().getStatusCode() == 200){
                return EntityUtils.toString(response.getEntity());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "fail_" + response.getStatusLine().getStatusCode();
    }
}
@Controller
@RequestMapping("/sms")
public class SmsSendController {

    @Autowired
    private SmsComponent smsComponent;

    /*** 提供给别的服务进行调用的
    该controller是发给短信服务的,不是验证的
     */
    @GetMapping("/sendcode")
    public R sendCode(@RequestParam("phone") String phone, @RequestParam("code") String code){
        if(!"fail".equals(smsComponent.sendSmsCode(phone, code).split("_")[0])){
            return R.ok();
        }
        return R.error(BizCodeEnum.SMS_SEND_CODE_EXCEPTION.getCode(), BizCodeEnum.SMS_SEND_CODE_EXCEPTION.getMsg());
    }
}

 发送验证码:

@ResponseBody
    @GetMapping(value = "/sms/sendCode")
    public R sendCode(@RequestParam("phone") String phone) {

        //1、接口防刷
        String redisCode = stringRedisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone);
        if (!StringUtils.isEmpty(redisCode)) {
            //活动存入redis的时间,用当前时间减去存入redis的时间,判断用户手机号是否在60s内发送验证码
            long currentTime = Long.parseLong(redisCode.split("_")[1]);
            if (System.currentTimeMillis() - currentTime < 60000) {
                //60s内不能再发
                return R.error(BizCodeEnum.SMS_CODE_EXCEPTION.getCode(),BizCodeEnum.SMS_CODE_EXCEPTION.getMessage());
            }
        }

        //2、验证码的再次效验 redis.存key-phone,value-code
        int code = (int) ((Math.random() * 9 + 1) * 100000);
        String codeNum = String.valueOf(code);
        String redisStorage = codeNum + "_" + System.currentTimeMillis();

        //存入redis,防止同一个手机号在60秒内再次发送验证码
        stringRedisTemplate.opsForValue().set(AuthServerConstant.SMS_CODE_CACHE_PREFIX+phone,
                redisStorage,10, TimeUnit.MINUTES);

        thirdPartFeignService.sendCode(phone, codeNum);

        return R.ok();
    }

4) 后端JSR303校验校验

前端也可以进行校验,此处是后端的验证

@Data
public class UserRegisterVo {// JSR303校验

    @Length(min = 6,max = 20,message = "用户名长度必须在6-20之间")
    @NotEmpty(message = "用户名必须提交")
    private String userName;

    @Length(min = 6,max = 20,message = "用户名长度必须在6-20之间")
    @NotEmpty(message = "密码必须提交")
    private String password;

    @NotEmpty(message = "手机号不能为空")
    @Pattern(regexp = "^[1]([3-9])[0-9]{9}$", message = "手机号格式不正确")
    private String phone;

    @NotEmpty(message = "验证码必须填写")
    private String code;
}

前面的JSR303校验怎么用:

JSR303校验的结果,被封装到BindingResult,再结合BindingResult.getFieldErrors()方法获取错误信息,有错误就重定向至注册页面

@PostMapping("/register")
public String register(@Valid UserRegisterVo registerVo, 
                       BindingResult result,
                       RedirectAttributes attributes) {

    if (result.hasErrors()){
        return "redirect:http://auth.gulimall.com/reg.html";