验证服务

图片验证码

1、编写工具类

public class CaptchaUtils {
    private static char mapTable[] = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
'A', 'B', 'C', 'D', 'E', 'F','G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };

    // 获取随机颜色
    static Color getRandColor(int fc, int bc) {
        Random random = new Random();
        if (fc > 255)
            fc = 255;
        if (bc > 255)
            bc = 255;
        int r = fc + random.nextInt(bc - fc);
        int g = fc + random.nextInt(bc - fc);
        int b = fc + random.nextInt(bc - fc);
        return new Color(r, g, b);
    }

    public static Map<String, Object> getImageCode(int width, int height) {
        Map<String, Object> returnMap = new HashMap<String, Object>();
        if (width <= 0) width = 60;
        if (height <= 0) height = 20;
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = image.getGraphics();
        Random random = new Random();
        g.setColor(getRandColor(200, 250));
        g.fillRect(0, 0, width, height);
        g.setFont(new Font("Times New Roman", Font.PLAIN, 18));

        g.setColor(getRandColor(160, 200));
        for (int i = 0; i < 146; i++) {
            int x = random.nextInt(width);
            int y = random.nextInt(height);
            int x1 = random.nextInt(12);
            int y1 = random.nextInt(12);
            g.drawLine(x, y, x + x1, y + y1);         // 干扰线
        }

        String captchaCode = "";  // 验证码
        for (int i = 0; i < 4; ++i) {
            captchaCode += mapTable[(int) (mapTable.length * Math.random())];
            g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
            String str = captchaCode.substring(i, i + 1);
            g.drawString(str, 13 * i + 6, 16);
        }

        g.dispose();
        returnMap.put("image", image);
        returnMap.put("value", captchaCode);
        return returnMap;
    }
}

2、编写Controller

    // 获取验证码图片
    @RequestMapping("/captchaImage")
    public void captchaImage(HttpServletRequest request, HttpServletResponse response) throws Exception {
        Map<String, Object> map = CaptchaUtils.getImageCode(60, 20);
        request.getSession().setAttribute( "captchaCode", map.get("value").toString().toLowerCase());
        request.getSession().setAttribute("captchaTime", new Date().getTime());
        ImageIO.write((BufferedImage) map.get("image"), "JPG", response.getOutputStream());
    }

    // 验证验证码
    @RequestMapping("/checkCaptcha")
    @ResponseBody
    public String checkCaptcha(HttpServletRequest request, HttpSession session) {
        String testCode = request.getParameter("captchaCode");
        Object realObj = session.getAttribute("captchaCode");
        if (realObj == null) {
            return "验证码已失效,请重新输入!";
        }
        String realCode = realObj.toString();
        Date now = new Date();
        Long captchaTime = Long.valueOf(session.getAttribute("captchaTime") + "");
        if (StringUtils.isEmpty(testCode) || !(testCode.equalsIgnoreCase(realCode))) {
            return "验证码错误";
        } else if ((now.getTime() - captchaTime) / 1000 / 60 > 5) {
            return "验证码已失效,请重新输入";
        } else {
            session.removeAttribute("captchaCode");
            return "1";
        }
    }

图片验证码第三方组件:jcaptcha、kaptcha

腾讯滑动验证码

官网:https://007.qq.com
后端接入组成
1、添加Maven依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId> <!-- 用于JSON转换 -->
    <version>1.2.58</version>
</dependency>

2、用于接收验证响应的POJO

public class Captcha implements Serializable {
    private Integer response;
    @JsonProperty("evil_level")
    private String evilLevel;
    @JsonProperty("err_msg")
    private String errMsg;
}

3、过滤器

@Component
public class CaptchaFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 针对指定接口,进行验证
        if ("/client/submit".equals(request.getRequestURI()) && "post".equalsIgnoreCase(request.getMethod())) {
            verify(request);
        }
        filterChain.doFilter(request, response);
    }

    private void verify(HttpServletRequest request) throws RestfulException {
        String ticket = request.getParameter("ticket"); 
        String randstr = request.getParameter("randstr");  // ticket 和 randstr 通过 URL参数传给后端
        String userIp = request.getRemoteAddr();
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getMessageConverters().add(new FastJsonHttpMessageConverter());  // 允许多种 content-type
        String url = String.format("https://ssl.captcha.qq.com/ticket/verify?aid=00&AppSecretKey=00&Ticket=%s&Randstr=%s&UserIP=%s", ticket, randstr, userIp);
        Captcha response = restTemplate.getForObject(url, Captcha.class);
        if (response.getResponse() != 1) {
            throw new RestfulException("验证失败");
        }
    }
}

邮箱验证

SMTP:Simple Mail Transfer Protocol,简单邮件传输协议
POP3:Post Office Protocol - Version 3,邮局协议,用于客户端访问邮件服务器,客户端需要把所有邮件下载到本地
IMAP:Internet Mail Access Protocol,Internet邮件访问协议,POP3的替代协议,客户端无需下载所有邮件

JavaMail应用组成
1、配置Maven依赖

<!-- 整合SpringMVC -->
<dependency>
    <groupId>javax.mail</groupId>
    <artifactId>mail</artifactId>
    <version>1.4.7</version>
</dependency>
<!-- 整合SpringBoot -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

2、配置Bean
整合SpringMVC

    <bean id="javaMailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
        <property name="host" value="smtp.126.com" />
        <property name="username" value="saoraozhe3hao@126.com" />
        <property name="password" value="*********" />
        <property name="defaultEncoding" value="utf-8" />
        <property name="port" value="465" /> <!-- 使用SSL加密发送邮件的端口 -->
        <property name="javaMailProperties">
            <props>
                <prop key="mail.smtp.auth">true</prop>
                <!-- 使用SSL加密发送邮件时,需配置 -->
                <prop key="socketFactory.class">javax.net.ssl.SSLSocketFactory</prop>
                <prop key="mail.smtp.timeout">20000</prop>
            </props>
        </property>
    </bean>

整合SpringBoot

spring:
  mail:
    host: smtp.exmail.qq.com
    username: hong@xx.com
    password: xxx
    default-encoding: UTF-8
    port: 465  # 阿里云上封禁25端口,必须用465端口
    properties.mail.smtp:
      # SSL证书Socket工厂
      socketFactory.class: javax.net.ssl.SSLSocketFactory
      # 登录服务器是否需要认证
      auth: true

3、编写工具类

public class MailUtils {
    public static String sendMail(String text, String subject, String emailAddress, JavaMailSenderImpl javaMailSender, Boolean type) {
        MimeMessage mMessage = javaMailSender.createMimeMessage(); // 邮件对象
        MimeMessageHelper mMessageHelper;
        try {
            mMessageHelper = new MimeMessageHelper(mMessage, true, "UTF-8");
            // MimeMessageHelper的from必须和JavaMailSender的username一致
            mMessageHelper.setFrom(javaMailSender.getUsername());  
            mMessageHelper.setTo(emailAddress);
            mMessageHelper.setSubject(subject);   //  标题
            mMessageHelper.addAttachment("文件名", new ByteArrayDataSource(bytes, "application/octet-stream"));  // 附件

            if (type) {
                mMessageHelper.setText(text, true);  // HTML格式
            } else {
                mMessageHelper.setText(text, false);
            }
            javaMailSender.send(mMessage); // 发送邮件
        } catch (MessagingException e) {
            e.printStackTrace();
            return "failure";
        }
        return "success";
    }
}

4、编写Controller

    @Autowired
    private JavaMailSenderImpl javaMailSender;

    // 发送文本
    @RequestMapping("sendText")
    @Async  // 用异步方式发送邮件,得配合@EnableAsync使用
    public String sendTextEmail() {
        return MailUtils.sendMail("邮件内容", "邮件标题", "1002691232@qq.com", javaMailSender, false);
    }

    // 发送 html
    @RequestMapping("sendHtml")
    public String sendHtmlEmail() {
        String html = "<a href='http://baidu.com'>链接</a>"
        return MailUtils.sendMail(html, "邮件标题",  "1002691232@qq.com", javaMailSender, true);
    }

可以使用Velocity、FreeMarker等模板引擎,发送复杂内容

阿里云滑动验证码

1、前端

<script type="text/javascript" src="https://o.alicdn.com/captcha-frontend/aliyunCaptcha/AliyunCaptcha.js"></script>

<template>
            <el-form ref="form" :model="form">
                <el-form-item>
                    <div id="captcha-element"></div>
                    <div id="button"></div>
                    <el-button type="primary" @click="validateForm">登录</el-button>
                </el-form-item>
            </el-form>
</template>
<script>
export default {
    data() {
        return {
            form: { }
        };
    },
    methods: {
        validateForm() {
            this.$refs.form.validate((valid) => {
                if (valid) {
                    document.getElementById('button').click();
                }
            });
        },
        getInstance(instance) {
            window.captcha = instance;
        },
        async captchaVerifyCallback(captchaVerifyParam) {
            const response = await this.submit(captchaVerifyParam);
            const result = response.data;
            const verifyResult = {
                captchaResult: result.captchaResult,  // 验证码校验结果,必须为 布尔值
                bizResult: result.bizResult  // 业务校验结果,必须为 布尔值
            };
            return verifyResult;
        },
        onBizResultCallback(bizResult) {
            if (bizResult) {
                window.location.href = 'home.html';
            }
        },
        submit(captchaVerifyParam) {
            return this.$axios({
                method: 'post',
                url: '/login',
                data: this.$qs.stringify({
                    username: this.form.username,
                    password: this.form.password,
                    captchaVerifyParam
                })
            });
        }
    },
    mounted: function() {
        window.initAliyunCaptcha({
            SceneId: '**',   // 场景ID
            prefix: '**',      // 身份标
            mode: 'embed',
            element: '#captcha-element',
            button: '#button',
            captchaVerifyCallback: this.captchaVerifyCallback,
            onBizResultCallback: this.onBizResultCallback,
            getInstance: this.getInstance,
            slideStyle: {
                width: 360,
                height: 40
            },
            language: 'cn'
        });
    }
};
</script>

2、JAVA后端
pom.xml

<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>captcha20230305</artifactId>
    <version>1.0.1</version>
</dependency>
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>tea-openapi</artifactId>
    <version>0.2.8</version>
</dependency>
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>tea</artifactId>
    <version>1.1.14</version>
</dependency>
    private void validate(HttpServletRequest request) throws Exception {
        Config config = new Config();
        config.accessKeyId = accessKeyId;
        config.accessKeySecret = accessKeySecret;
        config.endpoint = "captcha.cn-shanghai.aliyuncs.com";
        config.connectTimeout = 5000;
        config.readTimeout = 5000;
        com.aliyun.captcha20230305.Client client = new com.aliyun.captcha20230305.Client(config);
        VerifyCaptchaRequest captchaRequest = new VerifyCaptchaRequest();
        captchaRequest.captchaVerifyParam = request.getParameter("captchaVerifyParam");

        VerifyCaptchaResponse resp = client.verifyCaptcha(captchaRequest);
        if (!resp.body.result.verifyResult) {
            throw new CaptchaException("验证失败");
        }
    }

阿里云短信服务

1、申请签名,添加短信模板
2、pom.xml

        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>alibabacloud-dysmsapi20170525</artifactId>
            <version>2.0.24</version>
        </dependency>

3、发送短信

        /* 生成验证码 */
        Random random = new Random();
        String code = String.valueOf(1000 + random.nextInt(8999));
        /* 发送短信 */
        String templateParam = String.format("{\"code\":\"%s\"}", code);
        StaticCredentialProvider provider = StaticCredentialProvider.create(
                Credential.builder().accessKeyId(accessKeyId).accessKeySecret(accessKeySecret).build()
        );
        AsyncClient client = AsyncClient.builder().region(阿里云region).credentialsProvider(provider)
                .overrideConfiguration(
                        ClientOverrideConfiguration.create().setEndpointOverride("dysmsapi.aliyuncs.com")
                ).build();
        SendSmsRequest sendSmsRequest = SendSmsRequest.builder().signName(签名名称)
                .templateCode(模板CODE).templateParam(templateParam).phoneNumbers(目标手机号).build();
        CompletableFuture<SendSmsResponse> response = client.sendSms(sendSmsRequest);
        SendSmsResponse resp = response.get();
        System.out.println(new Gson().toJson(resp));
        client.close();

阿里云

短信服务、语言服务
流量服务:向手机充值流量,用于营销
号码隐私保护:把一个临时号码绑定到客户的手机号,以保密客户的手机号
号码认证服务:直接验证客户的手机号,以免除短信验证
邮件推送

敏感数据保护
内容安全:敏感内容识别
实人认证:人脸比对、OCR等

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容