NutzBook - 用户邮箱验证

NutzBook - 用户邮箱验证

Tags: Nutz

摘要

NutzBook - 用户邮箱验证

添加mail相关的jar

javax.mail-1.4.jar
commons-email-1.3.3.jar

配置Mail相关的ioc文件

打开dao.js, 把conf的定义改一下, 使其能扫描到custom下面所有的配置文件

        conf : {
            type : "org.nutz.ioc.impl.PropertiesProxy",
            fields : {
                paths : ["custom/"]
            }
        },

新增一个文件,路径为 resources/custom/mail.properties 内容如下

mail.HostName=smtp.qq.com
mail.SmtpPort=465
mail.UserName=javacorecn@qq.com
mail.Password=qqqyhszaqprt #此处的密码不是邮箱登陆密码,是授权码
mail.SSLOnConnect=true
mail.From=javacorecn@qq.com
mail.charset=UTF-8

再新增一个文件, 路径为 resources/ioc/mail.js 内容如下

var ioc={
    emailAuthenticator : {
        type : "org.apache.commons.mail.DefaultAuthenticator",
        args : [{java:"$conf.get('mail.UserName')"}, {java:"$conf.get('mail.Password')"}]
    },
    htmlEmail : {
        type : "org.apache.commons.mail.ImageHtmlEmail",
        singleton : false,
        fields : {
            hostName : {java:"$conf.get('mail.HostName')"},
            smtpPort : {java:"$conf.get('mail.SmtpPort')"},
            authenticator : {refer:"emailAuthenticator"},
            SSLOnConnect : {java:"$conf.get('mail.SSLOnConnect')"},
            from : {java:"$conf.get('mail.From')"},
            charset : {java:"$conf.get('mail.charset', 'UTF-8')"}
        }
    }    
};

含义是, 声明一个非单例的HtmlEmail定义,配置信息均通过conf对象获取.

在MainSetup类的init方法末尾,加入下列测试代码

 // 测试发送邮件
        try {
            HtmlEmail email = ioc.get(HtmlEmail.class);
            email.setSubject("测试NutzBook");
            email.setMsg("This is a test mail ... :-)" + System.currentTimeMillis());
            email.addTo("zhushaolong321@qq.com");//请务必改成您自己的邮箱啊!!!
            email.buildMimeMessage();
            email.sendMimeMessage();
        } catch (Exception e) {
            e.printStackTrace();
        }

重启jetty后,很快就收到邮件了

添加新的Service

package net.javablog.service;

public interface EmailService {

    boolean send(String to, String subject, String html);

}
package net.javablog.service;

import org.apache.commons.mail.HtmlEmail;
import org.nutz.ioc.Ioc;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.log.Log;
import org.nutz.log.Logs;

@IocBean(name = "emailService")
public class EmailServiceImpl implements EmailService {

    private static final Log log = Logs.get();

    @Inject("refer:$ioc")
    protected Ioc ioc;

    public boolean send(String to, String subject, String html) {
        try {
            HtmlEmail email = ioc.get(HtmlEmail.class);
            email.setSubject(subject);
            email.setHtmlMsg(html);
            email.addTo(to);
            email.buildMimeMessage();
            email.sendMimeMessage();
            return true;
        } catch (Throwable e) {
            log.info("send email fail", e);
            return false;
        }
    }
}

添加发送验证邮件的方法

打开BaseModule类,先添加2个属性

@Inject protected EmailService emailService;

protected byte[] emailKEY = R.sg(24).next().getBytes();

打开UserProfileModule类,添加一个方法

    @At("/active/mail")
    @POST
    public Object activeMail(@Attr(scope = Scope.SESSION, value = "me") int userId,
                             HttpServletRequest req) {
        NutMap re = new NutMap();
        UserProfile profile = get(userId);
        if (Strings.isBlank(profile.getEmail())) {
            return re.setv("ok", false).setv("msg", "你还没有填邮箱啊!");
        }
        String token = String.format("%s,%s,%s", userId, profile.getEmail(), System.currentTimeMillis());
        token = net.javablog.util.Toolkit._3DES_encode(emailKEY, token.getBytes());
        String url = req.getRequestURL() + "?token=" + token;
        String html = "<div>如果无法点击,请拷贝一下链接到浏览器中打开<p/>验证链接 %s</div>";
        html = String.format(html, url, url);
        try {
            boolean ok = emailService.send(profile.getEmail(), "XXX 验证邮件 by Nutzbook", html);
            if (!ok) {
                return re.setv("ok", false).setv("msg", "发送失败");
            }
        } catch (Throwable e) {
            log.debug("发送邮件失败", e);
            return re.setv("ok", false).setv("msg", "发送失败");
        }
        return re.setv("ok", true);
    }

添加验证邮件的返回方法

    @Filters // 不需要先登录,很明显...
    @At("/active/mail")
    @GET
    @Ok("raw") // 为了简单起见,这里直接显示验证结果就好了
    public String activeMailCallback(@Param("token") String token, HttpSession session) {
        if (Strings.isBlank(token)) {
            return "请不要直接访问这个链接!!!";
        }
        if (token.length() < 10) {
            return "非法token";
        }
        try {
            token = Toolkit._3DES_decode(emailKEY, Toolkit.hexstr2bytearray(token));
            if (token == null)
                return "非法token";
            String[] tmp = token.split(",", 3);
            if (tmp.length != 3 || tmp[0].length() == 0 || tmp[1].length() == 0 || tmp[2].length() == 0)
                return "非法token";
            long time = Long.parseLong(tmp[2]);
            if (System.currentTimeMillis() - time > 10 * 60 * 1000) {
                return "该验证链接已经超时";
            }
            int userId = Integer.parseInt(tmp[0]);
            Cnd cnd = Cnd.where("userId", "=", userId).and("email", "=", tmp[1]);
            int re = dao.update(UserProfile.class, org.nutz.dao.Chain.make("emailChecked", true), cnd);
            if (re == 1) {
                return "验证成功";
            }
            return "验证失败!!请重新验证!!";
        } catch (Throwable e) {
            log.debug("检查token时出错", e);
            return "非法token";
        }
    }

改造profile.jsp

打开webapp/WEB-INF/jsp/user/profile.jsp, 在这段代码后面
邮箱验证状态:<c:out value="${obj.emailChecked}"></c:out><p />

加入

                <c:if test="${!obj.emailChecked}">
                    <script type="text/javascript">
                        function send_email_check() {
                            $.ajax({
                                url : base + "/user/profile/active/mail",
                                type : "POST",
                                dataType : "json",
                                success : function (data) {
                                    if (data.ok) {
                                        alert("发送成功");
                                    } else {
                                        alert(data.msg);
                                    }
                                }
                            });
                        }
                    </script>
                    <button type="button" onclick="send_email_check();return false;">发送验证邮件</button>
                </c:if>

页面测试

重启jetty,登陆一次 http://127.0.0.1:8080/
再访问 http://127.0.0.1:8080/user/profile

输入有效的邮箱,更新,再点击发送验证邮件。

收到邮件

点击邮件中的验证链接,提示验证成功

再次刷新http://127.0.0.1:8080/user/profile,可以看到邮箱已验证通过