基于spring boot实现的Probe社区项目

基于spring boot实现的Probe社区项目 1,项目简介 probe社区项目是一套用于手机端的动态网站,后端基于SpringBoot+MyBatisPlus实现

本文包含相关资料包-----> 点击直达获取<-------

基于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 list = topicCategoryService.getTopicCategoryList(); return list==null?CommonResult.failed():CommonResult.success(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 fileList = new ArrayList<>(); Topic topic = null; MultipartFile video; CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext()); //接收前端参数的变量的初始化 ObjectMapper mapper = new ObjectMapper(); MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request; if (type==1){ //上传图片 //若请求中存在文件流,则取出相关的文件 try { if(multipartResolver.isMultipart(request)){ getTopicImgList(multiRequest,fileList); } } catch (Exception e) { return CommonResult.failed(e.getMessage()); } }else { try { //上传视频 if (multipartResolver.isMultipart(request)){ video = multiRequest.getFile("video"); if (video.getSize()>MAX_VIDEO_SIZE){ return CommonResult.failed("上传的视频最大不能超过500M"); }else if (video.getOriginalFilename().indexOf(".mp4")<0){ return CommonResult.failed("视频上传格式只支持mp4"); } fileList.add(video); } } catch (Exception e) { return CommonResult.failed(e.getMessage()); } } try { //尝试获取前端传过来的表单string流并将其转换成Topic实体类 topic = mapper.readValue(topicStr, Topic.class); } catch (IOException e) { return CommonResult.failed(e.getMessage()); } if (topic!=null&&!topic.getTopicName().equals("")){ //从redis中获取用户信息 PersonInfo person = redisService.getPersonByToken(token); //将正文的换行符转换成
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 TopicListInfo(int pageIndex,int pageSize,boolean type, Long topicCategoryId,String topicName){ HashMap modelMap = new HashMap<>(); Topic condition = new Topic(topicCategoryId,topicName); condition.setDeleted(0); CommonResult > 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 TopicInfo(Long topicId,String token){ return topicService.getTopicInfo(topicId,JwtTokenUtil.getUsernameFromToken(token)); }

/* * 由于点赞和收藏存在高并发的问题,因此先将对应的信息(即是点赞还是取消点赞)加入到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 personSet = redisService.getSet(personKey + userId); if (null==personSet||!personSet.contains(topicId)){ //说明该用户没有对该话题进行点赞(收藏) if (null==personSet){ personSet = new HashSet<>(); } //在对应列表中添加上该话题id personSet.add(topicId); //并且将该帖子的点赞(收藏)量加一 redisService.increment(topicKey+topicId,1); }else if (personSet.contains(topicId)){ //说明该用户之前已经对该话题进行了点赞(收藏) personSet.remove(topicId); //并且将该帖子的信息点赞(收藏)量减一 redisService.decrement(topicKey+topicId,1); } //重新添加到redis中 redisService.setSet(personKey + userId,personSet); } catch (Exception e) { return CommonResult.failed(e.getMessage()); } return CommonResult.success(null); }

/* * 当用户浏览当前话题时先从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 personSet = redisService.getSet(key); if (null==personSet){ //表示该用户是第一次访问该话题 personSet = new HashSet<>(); footPrintService.save(footPrint); personSet.add(userId+""); }else { //表示该用户不是第一次访问该话题 footPrintService.updateById(footPrint); } redisService.setSet(key,personSet); return CommonResult.success(null); }

private void getTopicImgList(MultipartHttpServletRequest request, List topicImgList) { MultipartHttpServletRequest multiRequest = request; //取出图片列表并构建List 列表对象,最多支持六张图片上传 for(int i=0;i<IMAGEMAXCOUNT;i++){ CommonsMultipartFile topicImgFile = (CommonsMultipartFile) multiRequest.getFile("topicImg"+i); if (topicImgFile!=null){ System.out.println("图片的大小:"+topicImgFile.getSize()); //若取出的第i个详情图片文件流不为空,则将其加入list集合中 topicImgList.add(topicImgFile); }else { break; } ```

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://m.bishedaima.com/yuanma/35573.html

相关推荐

  • 基于SpringBoot框架的医疗挂号管理系统

    这是一套采用Java编程语言,基于SpringBoot框架构建的医疗预约管理系统源代码,项目中融入了Vue技术,开发工具为Idea或Eclipse,此系统适用于毕业设计或课程实践
    2024年05月23日
    2 1 1
  • 基于Python设计的信息检索系统

    基于Python设计的信息检索系统 【实验目的】: 开发一款针对英文文本的信息检索系统,可以实现建立索引表,布尔查询,通配符查询, 短语查询等功能
    2024年05月14日
    4 1 1
  • 基于Python的信息检索课程设计

    基于Python的信息检索课程设计 sdu视点新闻 全站爬虫爬取+索引构建+搜索引擎查询练习程序 爬虫功能使用Python的scrapy库实现
    2024年05月14日
    3 1 1
  • 学生评奖评优管理系统

    这是一个🔥🔥基于SpringBoot框架的学生评奖评优管理系统设计与实现🔥🔥的项目源码,开发语言Java,框架使用的SpringBoot+vue技术,开发环境Idea/Eclipse
    2024年05月23日
    1 1 1
  • 基于Python实现书店销售管理系统

    书店销售管理管理子系统 一,设 计 总 说 明 现在社会随着计算机技术迅速发展与技术的逐渐成熟,信息技术已经使人们的生活发生深刻的变化,生活中的各种服务系统也使人们在生活中的联系日常销售活动方式发生了很大的变化
    2024年05月14日
    6 1 1
  • 网上摄影工作室

    这是一个🔥🔥基于SpringBoot框架的网上摄影工作室设计与实现🔥🔥的项目源码,开发语言Java,框架使用的SpringBoot+vue技术,开发环境Idea/Eclipse
    2024年05月23日
    2 1 1
  • java+swing实现推箱子小游戏课程设计源码

    在计算机科学领域,游戏设计一直是一个备受关注的话题,随着计算机技术的不断发展,人们对于游戏的需求也越来越高,《java+swing实现推箱子小游戏课程设计源码》是一个旨在使用Java语言和Swing框架实现的推箱子小游戏项目
    2024年05月07日
    5 1 1
  • 基于python + dlib实现人脸识别

    python 进行人脸识别 人脸识别的主要算法 其核心算法是 欧式距离算法使用该算法计算两张脸的面部特征差异,一般在 0,6 以下都可以被认为是同一张脸 人脸识别的主要步骤 获得人脸图片 将人脸图片转为 128D 的矩阵(这个也就是人脸特征的一种数字化表现) 保存人脸 128D 的特征到文件中 获取其他人脸转为 128D 特征通过欧式距离算法与我们保存的特征对比
    2024年05月14日
    2 1 1
  • 网上商城系统

    这是一个🔥🔥基于SpringBoot框架的网上商城系统设计与实现🔥🔥的项目源码,开发语言Java,框架使用的SpringBoot+vue技术,开发环境Idea/Eclipse
    2024年05月23日
    1 1 1
  • 基于python实现的CS通信和P2P通信

    基于python实现的CS通信和P2P通信 一,实验要求 C/S通信实现要求 两台计算机分别模拟服务器,客户端 通过编程实现服务器端
    2024年05月14日
    1 1 1

发表回复

登录后才能评论