基于spring boot实现的Probe社区项目
1.项目简介
probe社区项目是一套用于手机端的动态网站,后端基于SpringBoot+MyBatisPlus实现,前端页面使用html+css+js实现包含动态搜索、动态内容展示、个人中心、反馈中心等模块,Probe社区意在成为一款给广大学生来进行相互分享动态的BBS社区平台
1.1 使用的技术
probe采用现阶主流技术实现,涵盖了一般项目中几乎所有使用的技术。
技术 | 版本 | 说明 |
---|---|---|
Spring Boot | 2.1.3 | 容器+MVC框架 |
Apache Shiro | 1.4.1 | Java安全框架,执行身份验证和授权 |
MyBatisPlus | 3.4.6 | ORM框架,MyBatis的增强工具 |
Elasticsearch | 7.6.1 | 搜索引擎 |
Redis | 2.9.0 | 分布式缓存 |
Swagger-UI | 2.7.0 | 文档生产工具 |
Druid | 1.1.10 | 数据库连接池 |
Lombok | 1.18.6 | 简化对象封装工具 |
OSS | 2.5.0 | 存储图片 |
Jwt | 3.2.0 | 与Shiro框架结合实现验证和授权 |
1.2 实现的功能概览
- 用户模块
- 个人信息编辑设置
- 用户注册和修改密码
- 手机号和邮箱绑定设置
- 用户反馈内容管理
- 发布动态内容和添加水印
- 对动态内容进行点赞、收藏和评论回复
-
记录用户浏览动态的足迹
-
动态模块
- 按不同种类分页显示相关动态
- 分页显示相关评论
-
动态图片查看
-
后台管理模块
- 使用折线图和饼图来显示统计相关数据
- 用户、话题、留言以及管理员列表的相关CRUD操作
- 回收站列表,即在话题列表中删除话题会在回收站中显示,并设置计时器在一个月后自动删除
- 管理员列表中可以修改相关管理员的角色权限
2.数据库设计
2.1 表结构
视频表
帖子表
反馈表
帖子图片表
用户表
访问记录表
分类表
评论表
2.2 E-R图
3.项目设计
3.1 常用工具类
JwtToken生成的工具类
因为要整合了 JWT ,我们需要自定义过滤器 JWTFilter。JWTFilter 继承了 BasicHttpAuthenticationFilter,并部分原方法进行了重写。
该过滤器主要有三步:
-
检验请求头是否带有 Token: ((HttpServletRequest) request).getHeader(“Token”)
-
如果带有 Token ,则执行 Shiro 中的 login() 方法,该方法将导致
- 将 Token 提交到 Realm 中进行验证(执行自定义的Reaml中的方法)
-
如果没有 Token,则说明当前状态为游客状态或者其他一些不需要进行认证的接口
-
如果在 Token 校验的过程中出现错误,如:Token 校验失败,那么我会将该请求视为认证不通过,则重定向到 /unauthorized/**
```java /* * JwtToken生成的工具类 * JWT token的格式:header.payload.signature * header的格式(算法、token的类型): * {"alg": "HS512","typ": "JWT"} * payload的格式(用户名、创建时间、生成时间): * {"sub":"wang","created":1489079981393,"exp":1489684781} * signature的生成算法: * HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret) * * @author sakura * @date 2018/4/26 / @Slf4j public class JwtTokenUtil { // 过期时间 1小时 private static final Long EXPIRE_TIME = 60 * 60 * 1000L; // 密匙 private static final String SECRET = "SHIRO+JWT";
/**
* 生成 token 时,指定 token 过期时间 EXPIRE_TIME 和签名密钥 SECRET,
* 然后将 expireDate 和 username 写入 token 中,并使用带有密钥的 HS256 签名算法进行签名
* @param username
* @return
*/
public static String createToken(String username){
String token = null;
try {
// 设置token的过期时间
Date expireDate = new Date(System.currentTimeMillis()+EXPIRE_TIME);
// 加密算法
Algorithm algorithm = Algorithm.HMAC256(SECRET);
token = JWT.create()
.withClaim("username", username)
.withExpiresAt(expireDate)
.sign(algorithm);
} catch (UnsupportedEncodingException e) {
log.error("Failed to create token.{}",e.getMessage());
}
return token;
}
/**
* 验证token,如果验证失败,便会抛出异常
* @param token
* @param username
* @return
*/
public static boolean verify(String token,String username){
boolean isSuccess = false;
try {
Algorithm algorithm = Algorithm.HMAC256(SECRET);
JWTVerifier verifier = JWT.require(algorithm)
.withClaim("username", username)
.build();
// 验证token
verifier.verify(token);
isSuccess = true;
} catch (UnsupportedEncodingException e) {
log.error("Token is invalid. {}", e.getMessage());
} catch (TokenExpiredException e){
log.error("Token is out of data");
}
return isSuccess;
}
/**
* 在 createToken()方法里,有将 username 写入 token 中。现在可从 token 里获取 username
* @param token
* @return
*/
public static String getUsernameFromToken(String token) {
try {
DecodedJWT decode = JWT.decode(token);
String username = decode.getClaim("username").asString();
return username;
} catch (JWTDecodeException e) {
log.error("Failed to Decode jwt. {}", e.getMessage());
return null;
}
}
```
发邮件的工具类
```java public class MailUtil { private static final String USER = " * @qq.com"; // 本人的发件人称号,同邮箱地址 private static final String PASSWORD = " * *"; // 如果是qq邮箱可以使户端授权码,或者登录密码
/**
*
* @param to 收件人邮箱
* @param text 邮件正文
* @param title 标题
*/
/* 发送验证信息的邮件 */
public static boolean sendMail(String to, String text, String title){
try {
final Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.host", "smtp.qq.com");
// 发件人的账号
props.put("mail.user", USER);
//发件人的密码
props.put("mail.password", PASSWORD);
// 构建授权信息,用于进行SMTP进行身份验证
Authenticator authenticator = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
// 用户名、密码
String userName = props.getProperty("mail.user");
String password = props.getProperty("mail.password");
return new PasswordAuthentication(userName, password);
}
};
// 使用环境属性和授权信息,创建邮件会话
Session mailSession = Session.getInstance(props, authenticator);
// 创建邮件消息
MimeMessage message = new MimeMessage(mailSession);
// 设置发件人
String username = props.getProperty("mail.user");
InternetAddress form = new InternetAddress(username);
message.setFrom(form);
// 设置收件人
InternetAddress toAddress = new InternetAddress(to);
message.setRecipient(Message.RecipientType.TO, toAddress);
// 设置邮件标题
message.setSubject(title);
// 设置邮件的内容体
message.setContent(text, "text/html;charset=UTF-8");
// 发送邮件
Transport.send(message);
return true;
}catch (Exception e){
e.printStackTrace();
}
return false;
}
/**
* 发送验证码到指定邮箱并且返回生成的验证码
* @param mail
* @return
*/
public static String sendMail(String mail){
//发送的验证码
String code = RandomUtil.randomNumbers(4);
String content = "【社团probe】您正在使用邮箱验证,验证码为"+
code+",此验证码仅用于修改密码验证。请勿泄露给他人,5分钟内有效!";
String title = "[社团probe]验证码";
MailUtil.sendMail(mail,content,title);
return code;
}
```
阿里云手机短信服务
```java public class SMSUtil {
/**
* 发送验证码到指定手机上
* @param phonenumbers 手机号
* @param code 验证码
* @return
*/
public static boolean sendcode(String phonenumbers,String code){
//连接阿里云
DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "accessKeyId", "secret");
IAcsClient client = new DefaultAcsClient(profile);
CommonRequest request = new CommonRequest();
request.setMethod(MethodType.POST);
request.setDomain("dysmsapi.aliyuncs.com");//不要动
request.setVersion("2017-05-25");//
request.setAction("SendSms");
//自定义的参数 (手机号、验证码、签名、模板)
request.putQueryParameter("RegionId", "cn-hangzhou");
request.putQueryParameter("PhoneNumbers", phonenumbers);
request.putQueryParameter("SignName", "社团probe");
request.putQueryParameter("TemplateCode", "SMS_189616534");
HashMap<String,Object> map = new HashMap<>();
map.put("code",code);
request.putQueryParameter("TemplateParam", JSON.toJSONString(map));
try {
CommonResponse response = client.getCommonResponse(request);
System.out.println(response.getData());
return response.getHttpResponse().isSuccess();
} catch (ServerException e) {
e.printStackTrace();
} catch (ClientException e) {
e.printStackTrace();
}
return false;
}
public static void main(String[] args) {
String s = RandomUtil.randomNumbers(4);
System.out.println(s);
//sendcode("15521003562","2233");
}
} ```
3.2 捕获异常
```java /* 捕获与Shiro相关的异常 / @ExceptionHandler(ShiroException.class) public CommonResult handle401(){ //您没有权限访问! return CommonResult.forbidden(null); }
/**捕获其他异常*/
@ExceptionHandler(Exception.class)
public CommonResult globalException(HttpServletRequest request, Throwable e){
return CommonResult.failed(getStatus(request).value(),
"访问出错,无法访问:"+e.getMessage());
}
private HttpStatus getStatus(HttpServletRequest request) {
Integer statusCode = (Integer) request.getAttribute("java.servlet.error.status_code");
if (null == statusCode){
return HttpStatus.INTERNAL_SERVER_ERROR;
}
return HttpStatus.valueOf(statusCode);
}
} ```
3.3 业务代码
```java //视频最大上传量 final long MAX_VIDEO_SIZE = 1024 1024 500;
//支持上传图片的最大数量 private static final int IMAGEMAXCOUNT = 6;
@ApiOperation("获取话题类型")
@ResponseBody
@GetMapping(value = "/getTopicCategory")
public CommonResult
> getTopicCategory(){
List
@ApiOperation("获取发布内容的信息以及图片,并存入数据库中")
@ResponseBody
@PostMapping(value = "/publishTopic")
@RequiresRoles(logical = Logical.OR,value = {"0","1","2","3"})
public CommonResult publishTopic(HttpServletRequest request,int type, String topicStr,String token,Integer head){
List
topic.setOwnerId(person.getUserId()).setPersonInfo(person)
.setTopicDesc(StringEncoder.inDatabase(topic.getTopicDesc()));
if (null!=head){
topic.setEnableHeadline(head);
}
log.info("topic="+topic.toString());
//调用service方法进行添加
return topicService.save(topic,fileList,type);
}else {
return CommonResult.failed("信息未填写完整");
}
}
@ApiOperation("返回话题列表页中对应的列表信息")
@ResponseBody
@GetMapping(value = "/topicListInfo")
public Map
> res;
if (type){
res = esSearchService.getTopicList(condition, pageIndex, pageSize);
}else {
res = topicService.listByCondition(condition, pageIndex, pageSize);
}
modelMap.put("result",res);
modelMap.put("count",topicService.countByCondition(condition));
modelMap.put("success",true);
return modelMap;
}
@ApiOperation("根据话题id获取帖子正文内容")
@ResponseBody
@GetMapping("/getTopicInfo")
public CommonResult
/*
* 由于点赞和收藏存在高并发的问题,因此先将对应的信息(即是点赞还是取消点赞)加入到redis中做为缓存
* 再设置一个定时器,每间隔一段时间就从redis中取出对应的数据
* 简单地来说就是redis 异步入库,就是点赞和取消都交给 redis,redis 记录了点赞人和被点赞人,
* 同时在另外记录点赞总数,然后通过定时任务进行异步落库并删除 redis 中的指定数据。
*
* 关于在不使用mysql存储用户点赞和收藏操作时,如何标识用户是否对该话题进行点赞和收藏的
* 这里我统一使用redis进行存储用户对话题的点赞和收藏信息
* key使用PersonThumb_userId , value使用set集合来存储用户点赞话题的id
* @param topicId 需要操作的话题id
* @param type true-点赞 false-收藏
/
@ApiOperation("为帖子(添加/取消)点赞或收藏")
@ResponseBody
@GetMapping("/thumbOrCollect")
@RequiresRoles(logical = Logical.OR,value = {"0","1","2","3"})
public synchronized CommonResult thumbOrCollect(String topicId,boolean type,
HttpServletRequest request){
String userId = JwtTokenUtil.getUsernameFromToken(request.getHeader("Token"));
try {
String topicKey = type?RedisKey.getThumbTopicIdList():RedisKey.getCollectTopicIdList();
String personKey = type?RedisKey.getPersonThumb():RedisKey.getPersonCollect();
//获取该用户点赞(收藏)的列表
Set
/*
* 当用户浏览当前话题时先从redis中取出当前用户的浏览记录
* 如果当前话题为第一次访问则将信息缓存到redis中并且将相关信息存储在mysql中
* redis在其中起到判断用户是否第一次浏览该话题
/
@ApiOperation("每当浏览当前话题时更新当前用户的浏览记录")
@ResponseBody
@GetMapping("/addFootPrint")
@RequiresRoles(logical = Logical.OR,value = {"0","1","2","3"})
public CommonResult addFootPrint(HttpServletRequest request,Long topicId){
String token = request.getHeader("Token");
Long userId = Long.valueOf(JwtTokenUtil.getUsernameFromToken(token));
FootPrint footPrint = new FootPrint(userId,topicId);
//先判断该用户是否是第一次浏览该话题
String key = RedisKey.getPerson_FootPrint()+userId;
Set
private void getTopicImgList(MultipartHttpServletRequest request, List
4.项目展示
4.1 前端
注册页面
登录后进入的页面
点击右边的侧边栏可以发布动态或进入个人中心
话题专区
搜索相关动态时使用elasticsearch进行查找,并将关键字进行高亮
动态内容
点击动态图片对图片进行浏览
个人中心
对信息进行编辑
在账号安全中心可以对个人中手机号和邮箱进行绑定
4.2 后台管理界面
登录界面
统计页面
用户列表界面
用户列表界面进行批量删除
轮播图列表
参考文献
- JavaEE多层架构Struts2+Spring3+Hibernate3+Ajax的整合(大连海事大学·王向兵)
- 基于Ajax和SSH框架的高校大型设备共享系统(湖南师范大学·欧阳玲)
- J2EE轻量级框架在预算管理系统中的应用研究(大连海事大学·车传文)
- 基于Web服务的社会标注系统的设计与实现(大连理工大学·史梦露)
- 基于SSH2的轻博客系统的研究与实现(吉林大学·杨雪梅)
- 基于SSH资源管理系统的设计及实现(西安电子科技大学·杨静涛)
- 基于Ajax和SSH框架的高校大型设备共享系统(湖南师范大学·欧阳玲)
- 基于J2EE平台的Spring框架分析研究与应用(武汉科技大学·刘行亮)
- 基于B/S架构的酷跑社区系统的设计与实现(内蒙古大学·张晓乐)
- 基于SSH资源管理系统的设计及实现(西安电子科技大学·杨静涛)
- 一种Web应用框架的设计与实现(·河北师范大学)
- 基于Web服务的社会标注系统的设计与实现(大连理工大学·史梦露)
- Spring框架技术分析及应用研究(中国科学院大学(工程管理与信息技术学院)·翟剑锟)
- 基于Web服务的社会标注系统的设计与实现(大连理工大学·史梦露)
- 基于OAuth2.0协议的企业分布式授权系统设计与实现(华中科技大学·支猛)
本文内容包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主题。发布者:代码客栈 ,原文地址:https://bishedaima.com/yuanma/35573.html