最近為認(rèn)證系統(tǒng)添加了忘記密碼功能猛拴,使用了Spring提供的郵件API和Ehchace緩存驗(yàn)證碼羹铅。整個(gè)過(guò)程還是挺有趣,值得寫一下愉昆。
Spring郵件API
Sprin提供了一個(gè)強(qiáng)大方便的郵件API,簡(jiǎn)化了發(fā)送郵件的工作职员。可以發(fā)送富文本郵件跛溉,添加附件焊切,使用模板渲染郵件內(nèi)容。推薦看Spring實(shí)戰(zhàn)(第三版)芳室,這里只簡(jiǎn)單講一下如何發(fā)送富文本郵件专肪,其他的就不細(xì)講了。
配置郵件發(fā)送器
Spring郵件API的核心是MailSender
接口堪侯,Spring自帶JavaMailSenderImpl
實(shí)現(xiàn)了MailSender
接口嚎尤,所以需要將JavaMailSenderImpl
裝配到Bean中。
Mail.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--讀取外部屬性文件-->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:Mail.properties</value>
</list>
</property>
</bean>
<!--配置了mailSender-->
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="${mail.host}" />
<property name="port" value="${mail.port}"></property>
<property name="username" value="${mail.username}" />
<property name="password" value="${mail.password}" />
<property name="defaultEncoding" value="UTF-8"></property>
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.auth">${mail.smtp.auth}</prop>
<prop key="mail.smtp.timeout">${mail.smtp.timeout}</prop>
</props>
</property>
</bean>
</beans>
Mail.properties
使用QQ的郵件服務(wù)器伍宦,需要在QQ郵箱設(shè)置中開(kāi)啟STMP服務(wù)
mail.from=dai.dongliang@foxmail.com
mail.host=smtp.qq.com
mail.port=25
mail.username=675742730
mail.password=*********
mail.smtp.auth=true
mail.smtp.timeout=25000
Main.Java
這只是簡(jiǎn)單的構(gòu)造了一個(gè)帶有鏈接的郵件芽死,其他更復(fù)雜的用法就不介紹了。
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext( "Mail.xml");
JavaMailSenderImpl sender = (JavaMailSenderImpl)context.getBean("mailSender");
//構(gòu)建郵件
MimeMessage message=sender.createMimeMessage();
try {
//使用MimeMessageHelper構(gòu)建Mime類型郵件
MimeMessageHelper helper= new MimeMessageHelper(message,true);
helper.setFrom("dai.dongliang@foxmail.com");
helper.setTo("xxxxxxx@foxmail.com");
message.setSubject("Spring Mail Test");
//第二個(gè)參數(shù)true表明信息類型是multipart類型
helper.setText("<a href=\"http://www.magicalwolf.com\">你好</a>",true);
sender.send(message);
} catch (MessagingException e) {
throw new RuntimeException("郵件構(gòu)造失敗");
}
}
}
至此一封郵件就發(fā)送出去了次洼,可以坐等收件人查看了关贵。
Ehcache緩存
Spring中內(nèi)置了對(duì)Ehcache的支持,封裝了EhCacheCacheManager
卖毁,可以很方便的使用Ehcache揖曾。
配置CacheManager
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--配置cacheManager-->
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"
p:cache-manager-ref="ehcache" />
<!-- EhCache library setup -->
<bean id="ehcache"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
p:config-location="classpath:ehcache.xml" />
</beans>
ehcache.xml
Ehcache的配置文件,這里配置一個(gè)名為CodeCache的chahe用于保存驗(yàn)證碼。
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd">
<diskStore path="java.io.tmpdir" />
<!-- 配置自定義緩存
name:Cache的名稱炭剪,必須是唯一的(ehcache會(huì)把這個(gè)cache放到HashMap里)练链。
maxElementsInMemory:內(nèi)存中保持的對(duì)象數(shù)量。
maxElementsOnDisk:DiskStore中保持的對(duì)象數(shù)量念祭,默認(rèn)值為0兑宇,表示不限制。
eternal:是否是永恒數(shù)據(jù)粱坤,如果是隶糕,則它的超時(shí)設(shè)置會(huì)被忽略。
overflowToDisk:如果內(nèi)存中數(shù)據(jù)數(shù)量超過(guò)maxElementsInMemory限制站玄,是否要緩存到磁盤上枚驻。
timeToIdleSeconds:對(duì)象空閑時(shí)間,指對(duì)象在多長(zhǎng)時(shí)間沒(méi)有被訪問(wèn)就會(huì)失效株旷。只對(duì)eternal為false的有效再登。默認(rèn)值0,表示一直可以訪問(wèn)晾剖。
timeToLiveSeconds:對(duì)象存活時(shí)間锉矢,指對(duì)象從創(chuàng)建到失效所需要的時(shí)間。只對(duì)eternal為false的有效齿尽。默認(rèn)值0沽损,表示一直可以訪問(wèn)。
diskPersistent:是否在磁盤上持久化循头。指重啟jvm后绵估,數(shù)據(jù)是否有效。默認(rèn)為false卡骂。
diskExpiryThreadIntervalSeconds:對(duì)象檢測(cè)線程運(yùn)行時(shí)間間隔国裳。標(biāo)識(shí)對(duì)象狀態(tài)的線程多長(zhǎng)時(shí)間運(yùn)行一次。
diskSpoolBufferSizeMB:DiskStore使用的磁盤大小全跨,默認(rèn)值30MB缝左。每個(gè)cache使用各自的DiskStore。
memoryStoreEvictionPolicy:如果內(nèi)存中數(shù)據(jù)超過(guò)內(nèi)存限制浓若,向磁盤緩存時(shí)的策略盒使。默認(rèn)值LRU,可選FIFO七嫌、LFU。
-->
<cache name="CodeCache"
maxElementsInMemory="10000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="300"
timeToLiveSeconds="300"
memoryStoreEvictionPolicy="LFU" />
</ehcache>
Main.java
Ehcache的基本用法苞慢。
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext( "auth-cache.xml");
//得到緩存管理器
EhCacheCacheManager cacheManager = (EhCacheCacheManager)context.getBean("cacheManager");
//得到Cache
Cache cache = cacheManager.getCache("CodeCache");
//存入緩存诵原,這里是驗(yàn)證碼對(duì)應(yīng)用戶名
cache.put("123","magicwolf");
//取出緩存
System.out.println(cache.get("123",String.class));
//刪除緩存
cache.evict("123");
}
}
忘記密碼功能
上面已經(jīng)把關(guān)鍵點(diǎn)介紹了,剩余的就是如何組織代碼,設(shè)計(jì)密碼找回流程绍赛。代碼很簡(jiǎn)單就不貼出了蔓纠。
第一步:顯示忘記密碼頁(yè)面
一個(gè)簡(jiǎn)單的表單頁(yè)面,輸入用戶名和一個(gè)60秒刷新一次的驗(yàn)證碼吗蚌。
- 需要驗(yàn)證用戶名是否存在腿倚,郵箱是否已填寫。
- 60秒刷新的驗(yàn)證碼防止惡意重置密碼蚯妇。
- 60秒刷新的驗(yàn)證碼實(shí)現(xiàn)方式有很多敷燎,可以把時(shí)間信息存在session或cookie或Ehcache中。
第二步:發(fā)送郵件箩言,緩存重置密碼令牌硬贯。
- 生成一個(gè)5分鐘內(nèi)有效的令牌,將令牌和用戶id映射保存在Ehcache中陨收。
- 用令牌值組成重置郵件鏈接饭豹。
- 從數(shù)據(jù)庫(kù)取出郵件地址并發(fā)送郵件。
第三步:重置密碼
- 用戶點(diǎn)擊鏈接進(jìn)入重置密碼界面务漩。
- 驗(yàn)證令牌值拄衰,并得到用戶Id,定位到具體用戶。
- 用戶修改密碼饵骨。
總結(jié)
忘記密碼功能實(shí)現(xiàn)起來(lái)比較簡(jiǎn)單翘悉,但是如何設(shè)計(jì)一個(gè)嚴(yán)密的密碼找回功能很麻煩。現(xiàn)在這個(gè)流程就很薄弱宏悦,容易受到攻擊镐确。等后面有時(shí)間了再來(lái)仔細(xì)研究一下,添加一些驗(yàn)證條件饼煞,比如密保問(wèn)題源葫,手機(jī)號(hào)驗(yàn)證這些。郵箱密碼配置文件是保存在服務(wù)器上砖瞧,明文保存會(huì)有風(fēng)險(xiǎn)息堂,建議加密保存,在通過(guò)Spring的PropertyPlaceholderConfigurer
讀取加密配置块促。