【SpringBoot论坛项目实战】2、开发社区登录模块

文章列表:
1、初识Spring Boot,开发社区首页
2、开发社区登录模块
3、开发社区核心功能

开发社区登录模块

1.从产品的角度思考一个功能该怎么去实现 ,细节怎么完善
2.实践上一章学到的SSM知识,为后面做准备

1 发送邮件

在这里插入图片描述
登陆模块要实现的第一个功能是注册,注册要求网页像用户发验证码邮件

1.1 邮箱设置

启动客户端STMP服务:163邮箱-设置-更多设置-POP3/SMTP/IMAP-开启

1.2 Spring Email

1.2.1 导入jar包

我们的老朋友https://mvnrepository.com/
为了防止版本不匹配我用的和老师一个版本(卑微)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
    <version>2.1.5.RELEASE</version>
</dependency>

1.2.2 邮箱参数配置

注意:邮箱密码要写邮箱开通POP3/SMTP的授权码

1.2.3 使用JavaMailsender发送邮件

1.3 模板引擎

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>邮件示例</title>
</head>
<body>
<p>欢迎你,<span style="color:#ff0000" th:text="${username}"></span>!</p>

</body>
</html>
@Autowired
    private TemplateEngine templateEngine;
    @Test
    public void testHtmlMail(){
        Context context = new Context();
        context.setVariable("username","uam");

        String content = templateEngine.process("mail/demo", context);
        System.out.println(content);
        mailClient.sendMail("@qq.com","html",content);
    }

输出

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>邮件示例</title>
</head>
<body>
<p>欢迎你,<span style="color:red">uam</span>!</p>

</body>
</html>

模板引擎本身也可以被注入,深刻体会到了模板引擎的动态显示!

2 开发注册功能

在这里插入图片描述
复杂的功能要拆解成相对简单的功能,这样好开发
web项目可以按照请求去拆解
1.点注册,打开注册页面,就是一次请求
2.填信息,点立即注册,又把数据传给服务器
3.服务器保存账号之后会发邮件让点链接激活,点链接完成激活是第三个请求
每一次请求还是按照之前的思路:先开发数据访问层,再开发业务层,最后视图层
有些功能可能没有完整的三层。
记得加注解!

2.1 访问注册页面

2.1.1 LoginController

1.写Controller注释
2.写RequestMapping访问路径
3.在Controller里写方法返回注册页面
4.用thymeleaf改index与register,然后我们的首页和注册按钮就生效啦!
以后运行都是调试运行的,方便如果出错就打断点
在这里插入图片描述
鼠标放在按钮上看页面左下角的网址对不对(神奇)

2.2 提交注册数据

1.导包,好朋友https://mvnrepository.com/
2.配置,给网站整一个域名(目前是http://localhost:8080)
3.写一个工具类,提供两个方法(生成随机字符串+加密密码),后面注册时好用

2.2.1 UserService

注册:
1.返回错误信息:账号为空、密码为空、邮箱为空、账号已存在等(这些信息用map封装)
2.注册用户
3.发激活邮件:用thymeleaf生成动态网页,用上小节实现的mailClient发送动态网页邮件

2.2.2 LoginController

1.如果map为空,表示注册账号没有错误,转到操作成功界面
2.否则,在注册界面输出错误信息
3.修改完善操作成功界面和注册界面

2.3 激活注册账号

2.3.1 UserService

1.将状态常量放在接口里,要用到状态常量的类去继承该接口
2.激活方法:三种状态:若已激活;若相等;若不等

2.3.2 LoginController

要想在首页上访问login页面:
1.LoginController视图层写好映射和访问方法
2.login将静态页面改为动态页面
3.index里写好链接

3 会话管理

在这里插入图片描述

//cookie示例
    @RequestMapping(path = "/cookie/set",method = RequestMethod.GET)
    @ResponseBody
    public String setCookie(HttpServletResponse response){
        //创建Cookie
        Cookie cookie = new Cookie("code", CommunityUtil.generateUUID());
        //设置cookie生效的范围
        cookie.setPath("/community");
        //设置cookie生效时间,默认浏览器关掉cookie就无了
        cookie.setMaxAge(60 * 10);
        //发送cookie
        response.addCookie(cookie);
        return "set success";
    }

    @RequestMapping(path = "/cookie/get",method = RequestMethod.GET)
    @ResponseBody
    public String getCookie(@CookieValue("code") String code){
        System.out.println(code);
        return "get cookie";
    }

优:
cookie可以弥补HTTP无状态的特点
缺:
cookie存在客户端,没有安全性
cookie需要发送给服务端,占用流量

可以用session来解决cookie的问题。
缺点:session存在服务端,占用内存

    //session示例
    @RequestMapping(path = "session/set",method = RequestMethod.GET)
    @ResponseBody
    public String setSession(HttpSession session){
        session.setAttribute("id", 1);
        session.setAttribute("name","test");
        return "set session";

    }
    @RequestMapping(path = "session/get",method = RequestMethod.GET)
    @ResponseBody
    public String getSession(HttpSession session){
        System.out.println(session.getAttribute("id"));
        System.out.println(session.getAttribute("name"));
        return "get session";
    }

4 生成验证码

在这里插入图片描述
官网:https://code.google.com/archive/p/kaptcha/(需翻墙)

4.1 导入jar包

4.2 编写配置类

在Config目录下写Kaptcha配置类
1.注解
2.设置验证码的长、宽、大小等

4.3 生成随机字符

5 开发登陆、退出功能

在这里插入图片描述

5.1 访问登陆页面

使用thymeleaf动态链接到登陆页面,之前已实现

5.2 登陆

查看表的定义语句
在这里插入图片描述
id:主键
user_id
ticket:凭证,随机字符串,不重复,唯一的标识
status:状态,表示凭证有效还是无效(过期)
expired:凭证的有效期

1.先写数据访问层

1.1 实体类 名称和表名相对应
表里有什么字段,实体类里就增加几个属性与之对应
get/set方法
toString方法

1.2 dao里写Mapper类
声明增删改查的方法
用注解写sql语句,如 增:@Insert({" "," "}),双引号之间会自动拼在一起,每个双引号最后记得打一个空格,方便拼接

1.3 测试

2.再写业务层 UserService
实现登录功能:可能登陆失败,需要返回具体失败情况,可以返回Map来封装返回信息
2.1 处理空值
2.2 验证账户是否存在
2.3 验证账户是否激活
2.4 验证密码是否正确
2.5 登陆成功,生成凭证

3.最后写视图层 LoginController
3.1 判断验证码是否正确(验证码存入session里)
3.2 检查账号密码 - 调用业务层来实现
知识点:

  • 将对象转为字符串:obj.toString()
  • 用@Value将application.properties的值注入进来的方法:
    @Value("${server.servlet.context-path}")
    private String contextPath;
  • 错误的时候要将错误信息带给网页:往model里存
    model.addAttribute("usernameMsg", map.get("usernameMsg"));
    //如果不是usernameMsg的问题,得到的是none,没有影响
    model.addAttribute("passwordMsg", map.get("passwordMsg"));

4.处理页面 login.html
处理如图所示的表单
在这里插入图片描述

4.1 表单里每个框的name要与传入controller的参数名一致
4.2 错误信息的显示
(1)旧值显示在框里th:value="${param.password}"
(2)错误信息显示在框下

<input type="text" th:class="|form-control ${usernameMsg!=null?'is-invalid':''}|"
	th:value="${param.username}"
	id="username" name="username" placeholder="请输入您的账号!" required>
<div class="invalid-feedback" th:text="${usernameMsg}">
	该账号不存在!
</div>

5.3 退出

把凭证改为失效
1.业务层
2.视图层
3.配置“退出登录”按钮的链接 index.html

<a class="dropdown-item text-center" th:href="@{/logout}">退出登录</a>

6 显示登陆信息

在这里插入图片描述
登陆后显示:登陆头像、用户名称、消息数量
不登陆显示:注册、登陆按钮
不同的显示可以调用不一样的controller,但是一个网站都用这个方式处理的话可能有几百个上千个请求
拦截器:拦截浏览器访问过来的多个请求,在请求的开始或是结束部分插入代码,从而可以批量解决多个请求共有的内容。

6.1 拦截器示例

6.1.1 定义拦截器

1.加注解@Component
2.实现接口HandlerInterceptor

package com.nowcoder.community.controller.interceptor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@Component
public class AlphaInterceptor implements HandlerInterceptor {
    //实例化日志组件
    private static final Logger logger = LoggerFactory.getLogger(AlphaInterceptor.class);

    //在Controller之前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        logger.debug("preHandle: "+handler.toString());
        return true;
    }
    //在Controller之后,模板引擎之前执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        logger.debug("postHandle: "+handler.toString());
    }

    //在模板引擎之后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        logger.debug("afterCompletion: "+handler.toString());
    }
}

6.1.2 配置拦截器

1.写配置类
2.实现WebMvcConfigurer接口

package com.nowcoder.community.config;

import com.nowcoder.community.controller.interceptor.AlphaInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Autowired
    private AlphaInterceptor alphaInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(alphaInterceptor)
                .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg")
                .addPathPatterns("/register", "login");
    }
}

6.1.3 测试

注意以后启动服务器要用debug哈,养成好习惯
判断是否按注释顺序执行:在LoginController里进入login打断点,在AlphaInterceptor三个方法里打断点,刷新登陆页面

6.2 拦截器应用

在请求开始时查询登录用户
在本次请求中持有用户数据

    //在请求开始之初,通过凭证找到用户,并将用户存到hostHolder里
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //从cookie中获取凭证
        String ticket = CookieUtil.getValue(request, "ticket");
        if(ticket!=null){
            //查询凭证
            LoginTicket loginTicket = userService.findLoginTicket(ticket);
            //检查凭证是否有效:不为空、有效、超时时间晚于当前时间
            if(loginTicket!=null && loginTicket.getStatus()==0 && loginTicket.getExpired().after(new Date())){
                //根据凭证查询用户
                User user = userService.findUserById(loginTicket.getUserId());
                //在本次请求中持有用户
                hostHolder.setUser(user);
            }
        }
        return true;
    }

在模板视图上显示用户数据

	@Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        User user=hostHolder.getUser();
        if(user!=null || modelAndView!=null){
            modelAndView.addObject("loginUser", user);
        }
    }

在请求结束时清理用户数据

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        hostHolder.clear();
    }

写配置
改模板引擎 index里的头部

<li class="nav-item ml-3 btn-group-vertical" th:if="${logininUser==null}">
	<a class="nav-link" th:href="@{/login}">登录</a>
</li>

7 账号设置

在这里插入图片描述
账号设置主要有两个功能:一个是上传头像,一个是修改密码。修改密码作为课后作业

7.1 上传头像

7.1.1 访问账号设置页面

1.视图层UserController设置访问路径和页面

@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping(path = "/setting", method = RequestMethod.GET)
    public String getSettingPage(){
        return "/site/setting";
    }
}

2.配置/site/setting页面成动态页面
最开始加xmlns:th="http://www.thymeleaf.org"
改相对路径成thymeleaf语句th:href="@{/css/global.css}"
复用头部th:replace="index::header"

3.在index.html添加链接

<a class="dropdown-item text-center" th:href="@{/user/setting}">账号设置</a>

7.1.2 上传头像

还是按照三层来写

1.数据层:没啥事

2.业务层:提供改变用户头像的功能

    public int updateHeader(int userId,String headerUrl){
        return userMapper.updateHeader(userId, headerUrl);
    }

3.视图层:上传图片

@RequestMapping(path = "/upload",method = RequestMethod.POST)//上传时表单的提交方式为POST
    public String uploadHeader(MultipartFile headerImage, Model model){
        if(headerImage==null){
            model.addAttribute("error", "您还没有选择图片");
            return "/site/setting";
        }

        String fileName = headerImage.getOriginalFilename();
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        if(StringUtils.isBlank(suffix)){
            model.addAttribute("error", "文件格式不正确");
            return "/site/setting";
        }

        //生成随机文件名
        fileName = CommunityUtil.generateUUID()+suffix;
        //确定文件存放的路径
        File dest = new File(uploadPath+"/"+fileName);
        try {
            //存文件
            headerImage.transferTo(dest);
        } catch (IOException e) {
            logger.error("上传文件失败 "+e.getMessage());
            throw new RuntimeException("上传文件失败,服务器发生异常",e);
        }

        //更新当前用户的头像的路径(web访问路径)
        //http://local:8080/community/user/header/xxx.png
        User user = hostHolder.getUser();
        String headerUrl = domain+contextPath+"/user/header/"+fileName;
        userService.updateHeader(user.getId(), headerUrl);

        return "redirect:/index";

    }

7.1.3 获取头像

1.视图层

    //获取头像
    @RequestMapping(path = "/header/{fileName}",method = RequestMethod.GET)
    public void getHeader(@PathVariable("fileName") String fileName, HttpServletResponse response){
        //服务器存放的路径
        fileName=uploadPath+"/"+fileName;
        //输出图片
        //文件后缀
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        //响应图片
        response.setContentType("image/"+suffix);
        try(
                OutputStream os =  response.getOutputStream();
                FileInputStream fis = new FileInputStream(fileName);
                ) {
            byte[] buffer = new byte[1024];
            int b=0;
            while(( b=fis.read(buffer) ) != -1){
                os.write(buffer,0,b);
            }
        } catch (IOException e) {
            logger.error("读取头像失败"+e.getMessage());
        }
    }

2.页面配置
在这里插入图片描述

7.2 修改密码(课后作业)

8 检查登陆状态

在这里插入图片描述
想拦截哪个方法就在该方法上加个注解,加了注解就拦截,不加注解就不拦截
问题:1.注解怎么定义?2.怎么 识别当前方法有没有加这个注解?

想自己定义注解,就需要用元注解定义自定义注解常用的元注解有:
@Target:自定义注解可以作用在哪个类型上,类型如:类、方法、属性
@Retention:有效时间,是编译时有效还是运行时有效
@Document:自定义注解在生成文档的时候要不要把自定义注解带上去
@Inherited:子类继承父类,父类上有自定义注解,子类要不要继承
前两个一定要用

1.写自定义注解@LoginRequired

//有该注解的方法都是登陆后才能访问
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)//声明有效时期:程序运行时就有效
public @interface LoginRequired {
}

2.在Controller里需要登陆才能访问的方法前加上注解@LoginRequired
3.写拦截器:未登录且访问被写注解的方法时,需要拦截

@Component
public class LoginRequiredInterceptor implements HandlerInterceptor {
    @Autowired
    private HostHolder hostHolder;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if(handler instanceof HandlerMethod){//只拦截方法不拦截静态资源
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            LoginRequired loginRequired = method.getAnnotation(LoginRequired.class);
            if(loginRequired!=null && hostHolder.getUser()==null){
                response.sendRedirect(request.getContextPath()+"/login");
                return false;
            }
        }
        return true;
    }
}

4.写拦截器配置:不处理静态资源

	@Autowired
    private LoginRequiredInterceptor loginRequiredInterceptor;

	public void addInterceptors(InterceptorRegistry registry) {
       
        registry.addInterceptor(loginRequiredInterceptor)
                .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
    }
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【资源说明】 1、基于Spring Boot社区论坛项目源码+数据库+项目说明.zip 2、该资源包括项目的全部源码,下载可以直接使用! 3、本项目适合作为计算机、数学、电子信息等专业的课程设计、期末大作业和毕设项目,作为参考资料学习借鉴。 4、本资源作为“参考资料”如果需要实现其他功能,需要能看懂代码,并且热爱钻研,自行调试。 基于Spring Boot社区论坛项目源码+数据库+项目说明.zip # WeCommunity > 微博、微信、微X......各种微,我们也跟上潮流叫微社区,英文名是 WeCommunity!是不是和微信(WeChat)很像? 微社区是一个用户交流平台,类似论坛,提供有:帖子管理、评论、关注、点赞、搜索、私信、数据统计等功能。 项目目前部署在阿里云1核2G云服务器上,所有的组件都是单机模式运行。 ### 依葫芦画瓢的项目架构图 ![项目架构设计图](https://i.loli.net/2020/07/01/umXATrRW2PCLhEI.png "项目架构设计图") ### 功能描述 1. 用户管理   用户可以注册、登陆、退出,修改头像,查看某个用户的主页,其包括某个用户的关注、粉丝、获得的点赞数、发布的帖子,用户自己能查看自己发表的评论。 2. 帖子管理   普通用户可以发布帖子、修改帖子,管理员可以删除帖子、恢复删除的帖子,版主可以将帖子置顶/取消置顶、加精华/取消加精华。 3. 关注   用户可以关注/取消关注某个用户,假如A关注了B,那么A的关注列表中就有了B,B的粉丝列表中就有了A。 4. 评论   用户可以对帖子进行评论,还可以对评论进行回复。 5. 点赞   用户可以对帖子、评论进行点赞。 6. 系统通知   某个用户评论、点赞了帖子,或者关注了某个用户,那么被评论、点赞、关注的用户会收到一条通知。目前系统中有3种通知:评论通知、点赞通知、被关注通知。 7. 数据统计   管理员可以查看网站指定日期范围的UV(独立访客)、DAU(日活跃用户)数据。 8. 私信   用户可以对网站内的其他用户发送私信,双方互发消息,只有他们自己能看到自己的私信。 9. 定时调度   每个帖子都有个权重分,影响帖子的展示排名,定时调度主要是定时更新帖子的权重。 10. 搜索   搜索系统使用Elasticsearch实现,支持对帖子标题、帖子内容的搜索。 ### 技术选型 - Spring Boot - SpringMVC - Spring - MyBatis3、通用mapper - Spring Security:安全框架 - Redis:缓存及数据存储 - Kafka:消息队列 - Elasticsearch-6.3.0:分布式搜索引擎 - Quartz:定时调度框架 - Nginx - Thymeleaf:模板引擎 - Caffeine:Java本地缓存库 - MySQL - 七牛云:第三方文件存储服务 ### 各个功能模块所对应的技术点 ![功能模块对应的技术点](https://i.loli.net/2020/07/01/u3DRnvrxfUNKhtc.jpg) ### 界面设计 ![主页](https://i.loli.net/2020/07/01/VbQYPd9wvWzxjy8.jpg "主页") ![帖子发布](https://i.loli.net/2020/07/01/ANeDU75GaMB36ZT.jpg "帖子发布") ![私信列表_私信详情](https://i.loli.net/2020/07/01/p6HQtoPlJXNGdwz.jpg "私信列表_私信详情") ![系统通知_](https://i.loli.net/2020/07/01/mbvtnlCgZyYWqLS.jpg "系统通知_") ### 文件说明 - wecommunity.sql:数据库文件 - wecommunity文件夹:Maven 项目源码 - static文件夹:前端静态资源,需独立部署 ### 本地开发运行部署 - 下载zip直接解压或安装git后执行克隆命令 `git clone https://github.com/AatroxC/WeCommunity.git` - 安装各组件并启动:Redis、ZooKee

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值