牛客网高级实战项目2-1

222 阅读5分钟

开发注册功能

给template传值的两种方式

  • Controller中model.addAttribute("","");
  • Thymeleaf的Context对象,context.setVariable("",""),templateEngine.process("模板",context)

thymeleaf

引用路径用@{/index},引用变量用${}   
th:href引用路径
@{其中传入变量${}}

如果input标签里class带上了is-invalid,则下面的div就会显示出来
其中form-control这个样式一直有的,再合并一个变量
th:class="|form-control ${usermessage!=null? 'is-invalid':''}|"

注册的三层逻辑:

  • 空白post注册,必须和User对象的属性名匹配
nmae="username"
name="password"
name="email"
  • 有错误返回注册页面,要求有原来的值
th:value="${user!=null? user.passowrd:''}"
  • 对于条件判断,错误时返回错误信息
<input type="password"
 th:class="|form-control ${passwordMsg!=null? 'is-invalid':''}|"
 
 <div class="invalid-feedback" th:text="${passwordMsg}">
	密码长度不能小于8位!
</div>	

会话管理

  • HTTP标准cookie
    HTTP是无状态、有会话的
    一个cookie存一对key-value,只能存少量类型,只能存字符串
    cookie流量影响性能,不安全不能存密码;
    服务器端产生,浏览器下次访问时会带上cookie;

  • JavaEE标准session
    数据存到服务端内存中,session和cookie有关,cookie中会存一个JsessionId
    可以存很多数据,不止字符型,session可以从参数中获取

  • 分布式部署session方法

    • 同步session:当某个服务器创建session并保存后,会把这个session同步给其他服务器,当同一个C下次携带JSESSIONID的cookie到达另一台S时,也能根据JSESSIONID找到对应的session值;
    • 共享session,别的都向某一台S去获取session,也存在中心化问题
    • 敏感数据存到数据库,集群主从备份,得到客户端的数据,但传统关系型数据库,数据都是存到硬盘里的,从硬盘里取数据,性能更差。可以把会话数据存到redis中,推荐!

生成验证码

Kaptcha工具

  • 导入jar包,编写Kaptcha配置类
  • 生成随机字符、生成图片
  • 核心接口Producer,实现类DefaultKaptcha,config配置里传入属性properties,最后将config传到kaptcha中,返回值类型为Producer,由bean容器管理
  • DefaultKaptcha有两个方法createText()和createImage(text)
  • 会话
    • response带cookie(JSESSIONID)返回
    • session记录了验证码文本text,便于给该客户端返回的文本验证,用session记录会话状态
    • 将文本混淆成图片后,response带图片类型返回,response.setContentType("image/png")
    • response向浏览器响应,我们要获取它的输出流,response.getOutputStream()获取字节流,图片用字节流更好
    • 输出某张图片,用某个格式,用某个字节流ImageIO.write(image,"png",os)
  • js方法实现局部更新,如刷新验证码
    • js中的id选择器$("#kaptcha")选中id="kaptcha"
    • @{/kaptcha}相当于/community/kaptcha

开发登录、退出功能

头部根据登录与否,显示不同的顶部信息
当用户登陆成功后,每次以登录态去访问,也就是每次请求都会带上ticket。每个请求后台都应该判断登录态,处理相关逻辑,所以用拦截器批量处理请求
ThreadLocal线程隔离,数据安全,每个线程有自己的一份数据的拷贝

客户端本地不会存用户信息,不安全,只会存一个ticket这个string的值,cookie中的name就是ticket,value就是具体的取值

多个客户端访问一个S,S会为每一个C都创建一个线程,每个客户端要把user存到线程里,所以考虑线程隔离,各个user线程之间不干扰。
ThreadLocal主要就是set和get方法,内部是一个ThreadLocalMap,存的是当前线程,每个线程的ThreadLocalMap对象不同。

  1. 本质cookie只带ticket和其字符串值,这样安全。服务器收到后根据ticket的值获取LoginTicket对象,通过LoginTicket对象的userId,获取登录的用户user,并且要把这个user存到threadlocal对象中。
  2. 在模板执行前,即postHandler()中把这个用户添加到modelAndView,模板引擎根据传来的动态参数,即是不是有这个user,动态修改页面。
  3. 模板执行完,请求完成,线程结束,clear清理threadlocal里存的用户信息。即退出时,结束请求,user被清理掉了为空
  4. preHande()---controller执行相应的目标方法---postHandle---模板---afterCompletion()

注意:

  • @{url路径} @{/user/setting}
  • 对于响应的是图片,向浏览器响应二进制图片,通过流手动向浏览器输出
    设置response的响应类型 response.setContentType("image/png")
  • FileInputStream.read(byte[] byte)以字节数组读取输入流,将其缓存到数组b中,返回实际读取的字节数。若返回值为-1,表示流位于末尾,用循环while((b=fis.read(byte))!=-1){...}

账号设置

上传头像(头像数据存到服务器上)、获取头像(web访问路径)

  • post请求
  • 表单:enctype属性="multipart/form-data"
  • spring mvc通过MultipartFile处理上传的文件,model用于模型返回信息给模板
    • MultipartFile有一个getOriginalFileName()的方法,获取原来的文件名
  • controller的String,最后返回return "redirect:/login" ,底层其实也是response.sendRedirect(request.getContextPath()+"/login");
  • 关于重定向
    • redirect
    • forward

显示登录状态

自定义注解

  • @Target 作用目标、方法、类
  • @Retention 生效时间 运行时有效/编译时有效
  • @Document
  • @Inherited 子类是否继承父类注解

读取注解

  • Method.getDeclaredAnnotations() 获取方法上的所有注解
  • Method.getAnnotation(Class annotationClass) 获取指定的注解

拦截器拦截所有请求,只处理带有该注解的方法 对于需要登录后才能访问的方法,加上这个自定义注解,主要是个人设置界面和上传头像需要认可的登录状态

这里只对响应前进行处理,也就是只重写preHandle

首先判断拦截对象是不是一个方法,如果是的话讲Object对象转型成HandlerMethod对象。获取拦截的方法,查找该方法有没有我们写的@LoginRequired注解。有的话看hostHolder里有没有user,如果有注解没user就重定向到登录页面,这里不是在controller层,所以需要用respons重定向。