1. 发送邮件

  • 邮箱设置
    • 启用客户端SMTP服务
  • Spring Email
    • 导入 jar 包
    • 邮箱参数配置
    • 使用 JavaMailSender 发送邮件(MailClient类)
  • 模板引擎

    • 使用 Thymeleaf 发送 HTML 邮件

      2. 开发注册功能

  • 访问注册页面

    • 点击顶部区域内的链接,打开注册页面。
  • 提交注册数据
    • 通过表单提交数据。
    • 服务端验证账号是否已存在、邮箱是否已注册。
    • 服务端发送激活邮件。(用户表中有激活码)
  • 激活注册账号
    • 点击邮件中的链接,访问服务端的激活服务。(先判断状态,再对比激活码)

image.png

3. 会话管理

  • HTTP的基本性质
    • HTTP是简单的
    • HTTP是可扩展的
    • HTTP是无状态的,有会话的
      • 它不对之前发生过的请求和响应的状态进行管理。即,无法根据之前的状态进行本次的请求处理。
  • Cookie
    • 通过在请求和响应报文中写入Cookie信息来控制客户端的状态。
    • 其会根据从服务器端发送的响应报文内的一个叫做Set-Cookie的首部字段信息,通知客户端保存Cookie。当下次客户端再往该服务器发送请求时,客户端会自动在请求报文中加入Cookie值后发送出去。
    • 服务端接受客户端发送过来的Cookie后,会去检查 究竟是从一个客户端发来的连接请求,然后对比服务器上的记录,最后得到之前的状态信息。
  • Session

    • 是JavaEE的标准,用于在服务端记录客户端信息。
    • 数据存放在服务端更加安全,但是也会增加服务端的内存压力。
    • 分布式部署使用session存在问题:各个服务器中的session不同步,浏览器第一次访问的session在另一台服务器上没有存储
      • 粘性处理:固定ip需要分给同一个服务器处理,不能保证负载均衡
      • 同步session:对服务器的性能产生影响,服务器之间存在耦合
      • 共享session:单独一台服务器存放服务器,所有的服务器都向该服务器申请,该服务器如果挂了会产生很大影响,出现性能瓶颈
      • 主流解决方案:尽量使用cookie,敏感数据存储在数据库中,可以采用数据库集群储存备份,存在问题是访问硬盘比较慢,影响性能,也可以存储到NoSQL中,比如Redis

        4. 生成验证码

  • Kaptcha

    • 导入 jar 包
    • 编写 Kaptcha 配置类
      • KaptchaConfig类中,配置图片的大小颜色之类的
    • 生成随机字符、生成图片
      • 生成的字符需要存放到session中,便于在login中进行验证LoginController类中getKaptcha
    • jquery.min.js的请求资源地址需要修改

      5. 开发登录、退出功能

  • 访问登录页面

    • 点击顶部区域内的链接,打开登录页面。
  • 登录LoginController
    • 验证账号、密码、验证码。
      • 验证码从session中通过属性名称获得
    • 成功时,生成登录凭证,发放给客户端。
      • 登录表中有用户id,登录状态和令牌以及过期时间
      • 通过用户表验证密码,登录状态后,生成登录表所需的数据插入到登录表
      • 将当前的令牌和过期时间放入cookie,并设置cookie的有效路径
    • 失败时,跳转回登录页。
  • 退出

    • 将登录凭证修改为失效状态,即在登录表修改用户状态。(cookie中没有删除登录凭证)
    • 跳转至网站首页。

      6. 显示登录信息

  • 登录状态和非登录状态,同一个页面所展示的内容可能是不一样的。

  • 拦截器示例
    • 定义拦截器LoginTicketInterceptor,实现HandlerInterceptor
    • 配置拦截器WebMvcConfig,为它指定拦截、排除的路径
  • 拦截器应用LoginTicketInterceptor类
    • 在请求开始时(preHandle方法的作用)查询登录用户(到Cookie中通过令牌查询),并将用户存入HostHolder类中,类似于session的功能(解决第5节只更改页面和数据库登录状态的情况)
    • 在本次请求中持有用户数据HostHolder类,通过ThreadLocal实现线程隔离
      • 为什么要使用ThreadLocal?
        • 利用ThreadLocal管理登录用户信息实现随用随取
        • Java中的ThreadLocal通常是在什么情况下使用的? - Java3y的回答 - 知乎
        • 总结:通过session存储->service层、dao层不好通过Servlet api获得用户信息->采用ThreadLocal实现->切换线程会导致ThreadLocal中没有用户信息->HandleInterceptor类的preHandle方式实现登录时,从redis获取相应的用户信息进行存储
          • 通常在项目中,用户登录后,我们会将用户的信息存到session,如果想在其它地方获取session中的用户信息,我们需要先获取HttpServletRequest,再通过request.getSession得到HttpSession,从而获取到我们想要的用户信息。
          • 通常在一个大型项目中,service层和dao层都是和web层分离开来,都是单独的工程,不依赖servlet api,大家也不会为了在service层或者dao层获取登录用户信息而这么做
          • 采取一种新的方法来存储用户信息——ThreadLocal。ThreadLocal就是本地线程,它是本地线程局部变量的意思,我们每个请求都会对应一个线程,这个ThreadLocal就是这个线程使用过程中的一个变量,该变量为其所属线程所有,各个线程互不影响。在一个请求中,所有调用的方法都在同一个线程中去处理,这样就实现了在任何地方都可以获取到用户信息了
          • treadLocal为各个线程所私有,各线程间不共享,也互不影响,那么问题来了,我们只是在登录的时候,查询用户信息并将其放进当前线程的ThreadLocal,而后续其它请求一旦切换到别的线程,我们的功能就玩不转了,所以我们需要借助一个方法来拦截所有的后台请求(排除非必须登录才能访问的url)
          • 在springMVC中,我们可以通过HandlerInterceptor来实现,定义一个类去实现这个HandlerInterceptor接口,在preHandle中去调用SessionLocal中的setUser(user)来设置用户信息
    • 在模板视图上显示用户数据
    • 在请求结束时(即视图渲染完毕后)清理用户数据

image.png

7. 账号设置

  • 上传文件
    • 请求:必须是POST请求
    • 表单:enctype=“multipart/form-data”
    • Spring MVC:通过 MultipartFile 处理上传文件
  • 开发步骤UserController类
    • 访问账号设置页面
    • 上传头像,需要修改用户表中的头像地址
    • 获取头像:通过读取当前user的请求图片路径获取图片,因此在Controller层需要定义一个get请求,实现图片的回显
  • 修改密码
    • 请求:POST请求
    • 需要实现登录拦截
  • 开发步骤

    • 访问账号设置页面
    • 判断当前输入的原密码和数据库中是否一致
    • 若一致则判断新密码和确认密码是否一致,一致后加密修改数据库

      8. 检查登录状态

  • 使用拦截器

    • 在方法前标注自定义注解LoginRequired注解
    • 拦截所有请求,只处理带有该注解的方法LoginRequiredInterceptor类
      • 在preHandle层获取到带有自定义注解的方法(通过反射),判断有没有登录,没有登录需要重定向到登录
    • 配置拦截器WebMvcConfig
  • 自定义注解
    • 常用的元注解: @Target、@Retention、@Document、@Inherited
  • 如何判断方法有该注解:(通过反射获取)
    • Method.getDeclaredAnnotations ()
    • Method.getAnnotation (Class annotationClass)