大三在学习了JavaWeb后,在学期末进行了课程设计(学习3个,其中第一个是JavaWeb,第二个基于springMVC,第3个springboot。并和舍友完成一个门户网站,负责后端编写)。在这个过程中学到了很多,限于本人水平,只用作记录和学习,如有不足,还请批评指出。
成果展示
问题总结与处理
1.登录注册时,希望注册时需要邮箱的验证码。
(1)生成验证码(这里截取了UUID的前六位,可自行调整)
import java.util.UUID;
public class CodeUtils {
public static String getUUID() {
String code= UUID.randomUUID().toString();
code=code.substring(0,6);
return code;
}
}
(2)邮箱判断
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class EmailJudge {
public static boolean isMail(String email){
String regEx1 = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
Pattern p;
Matcher m;
p = Pattern.compile(regEx1);
m = p.matcher(email);
if(m.matches()){
return true;
}
return false;
}
}
(3)发送邮件(下有授权码获取方法)
这里新建一个线程完成邮箱发送操作,避免响应请求过慢。
/***邮箱验证*/
@GetMapping("/getEmail/{email}")
public Result getEmail(@PathVariable String email) throws EmailException {
if (EmailJudge.isMail(email)) {//判断邮件是否正确
String code = CodeUtils.getUUID();//生成一个验证码
new Thread(new Runnable(){
@SneakyThrows
@Override
public void run() {
HtmlEmail email1 = new HtmlEmail();
email1.setHostName("smtp.qq.com");
email1.setCharset("utf-8");
email1.addTo(email);
email1.setFrom("【填写你的邮箱】", "【邮箱主题】");
email1.setAuthentication("【填写你的邮箱】", "【邮箱授权码】");
email1.setSubject("欢迎你注册本系统");//填写文案
email1.setMsg("您的验证码为: " + code);
email1.send();
}
}).start();
return new Result(true, "验证码已发送!", code);
} else {
return new Result(false, "您输入的邮箱有误!", null);
}
}
上述发送邮件其实存在安全问题,即前端请求立即返回验证码,这样即使注册用户不看邮箱,依然可以获得验证码,可以在数据库储存邮箱验证码,这样更安全,也能控制索取验证码时间间隔
点击 授权码获得方法
2.文件上传
(1)文件上传(非空,验证,是否重复)
这里上传的是png或jpg类型的,这里的验证也是不安全的,只通过后缀判断文件类型,可以查阅更加安全的方法。通过md5加密实现判断文件是否重复,可以为文件的字节流生成一个32位的码,如果在数据库发现相同的码,那这个文件就已经上传过了
/**添加网址*/
@PostMapping("/add")
public Result add(@RequestParam(required = false) MultipartFile picture)
throws IOException {
if (!(picture ==null)) {
String pictureName = picture.getOriginalFilename();//获取上传文件的名称
int begin = pictureName.indexOf(".");
int end = pictureName.length();
String fileType = pictureName.substring(begin, end);//截取文件类型
if (fileType.equals(".png") || fileType.equals(".jpg")) {
String md = DigestUtils.md5Hex(picture.getBytes());//生成md5码
Site site = this.siteMapper.fileJudge(md);//判断是否已上传
if (site == null) {//如果未上传
String local = "[文件名称]" + fileType;
picture.transferTo(new File("D:\\work\\shixun\\stepTab\\src\\main\\resources\\static\\" + local));//要存放的地址
return new Result(true,"添加成功",null);
} else {//文件已上传,获得数据库内的图片路径
pictureUrl = site.getPicture();
return new Result(true,"添加成功",null);
}
} else {
return new Result(false, "文件类型错误,请上传PNG或者JPG格式文件!", null);
}
} else {
return new Result(false, "文件不能为空!", null);
}
}
(2)上传后访问不到
在新上传后,路径下显示文件存在,但是通过服务器却访问不到,这时重启服务器发现又可以访问到,但是不能每次都重启,我们可以通过指定静态资源访问路径来解决这个问题,这样当服务器接收到要访问静态资源的请求就会到指定路径找到图片。
首先在yml指定访问静态资源的路由前缀
spring:
mvc:
static-path-pattern: /picture/**
添加配置类
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 静态资源设置
* */
@Configuration
public class ResourceConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/picture/**").//指定要处理的请求
addResourceLocations("file:D:\\work\\shixun\\stepTab\\src\\main\\resources\\static\\");//告知服务器资源的地址
WebMvcConfigurer.super.addResourceHandlers(registry);
}
}
3.redis的简单应用
因为本系统推荐列表可能会被频繁访问,所以用Redis来进行缓存。(当然实际上本系统根本不需要redis,只是通过这次简单应用和了解redis)
yml
spring:
redis:
host: ip地址
port: 6379
password: 密码
jedis:
pool:
max-active: 8 #最大数据库连接数
max-wait: -1 #等待时间
max-idle: 8 #最大空闲
配置类
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
/**
* 自定义Redis配置类,进行序列化以及RedisTemplate设置
*/
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
/**
* 定制Redis API模板RedisTemplate
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
// 使用JSON格式序列化对象,对缓存数据key和value进行转换
Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
// 解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jacksonSeial.setObjectMapper(om);
// 设置RedisTemplate模板API的序列化方式为JSON
template.setDefaultSerializer(jacksonSeial);
return template;
}
/**
* 定制Redis缓存管理器RedisCacheManager,实现自定义序列化并设置缓存时效
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
// 分别创建String和JSON格式序列化对象,对缓存数据key和value进行转换
RedisSerializer<String> strSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
// 解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jacksonSeial.setObjectMapper(om);
// 定制缓存数据序列化方式及时效
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofDays(7)) // 设置缓存有效期为1天
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(strSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jacksonSeial))
.disableCachingNullValues(); // 对空数据不进行缓存
RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(config).build();
return cacheManager;
}
}
==实际应用:当用户访问网址推荐列表时,首先查看redis中是否存在,如果存在,直接返回,如果不存在,则从数据库获取并存放到redis中 ==
/**推荐列表*/
@Override
public List<Site> recommendList() {
List<Site> sites=null;
Object o=redisTemplate.opsForValue().get("recommendList");//查找
if(o!=null){
sites=(List<Site>) o;
}else {
sites=this.siteMapper.recommendList();
if (!sites.isEmpty()) {
redisTemplate.opsForValue().set("recommendList", sites);//存放
}
}
return sites;
}
结语
希望我们之后都能理解底层,共同进步。
后端源码:
链接:https://pan.baidu.com/s/1uc4b_8ZgYvJKp1M9Ed9WBA
提取码:3c4d