【SpringBoot论坛项目实战】2、开发社区登录模块 您所在的位置:网站首页 springboot注册登录模块设计 【SpringBoot论坛项目实战】2、开发社区登录模块

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

2023-07-28 09:25| 来源: 网络整理| 查看: 265

文章列表: 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/ 为了防止版本不匹配我用的和老师一个版本(卑微)

org.springframework.boot spring-boot-starter-mail 2.1.5.RELEASE 1.2.2 邮箱参数配置

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

1.2.3 使用JavaMailsender发送邮件 1.3 模板引擎 DOCTYPE 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> 邮件示例 欢迎你,uam!

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

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)错误信息显示在框下

该账号不存在! 5.3 退出

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

退出登录 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里的头部

登录 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添加链接

账号设置 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"); }


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有