仿牛客论坛项目03-登录功能,内容记录。
会话管理,cookie,session,验证码,hostHolder,拦截器
会话管理
Cookie
1 |
|
- 请求映射:当一个GET请求发送到服务器的”/cookie/set”路径时,Spring框架会根据
@RequestMapping
注解找到这个setCookie
方法来处理请求。 - 创建Cookie:在
setCookie
方法内部,首先创建了一个Cookie
对象。CommunityUtil.generateUUID()
方法(这个类和方法没有在代码中定义,可能是自定义的)被用来生成一个唯一的标识符(UUID),作为Cookie的值。 - 设置Cookie属性:
cookie.setPath("/community/alpha")
:设置了Cookie的路径,这意味着Cookie只会在域名下的”/community/alpha”路径及其子路径中有效。cookie.setMaxAge(600)
:设置了Cookie的最大年龄(以秒为单位)。在这个例子中,Cookie将在600秒(10分钟)后过期。
- 添加到响应:通过
response.addCookie(cookie)
方法,将创建的Cookie添加到HTTP响应中。这样,当响应发送回浏览器时,Cookie也会一起发送。 - 响应体:函数返回了一个字符串”set cookie”,这将作为HTTP响应的响应体发送回客户端。客户端浏览器会显示这个字符串,但更重要的是,浏览器会存储下发的Cookie。
- 客户端存储Cookie:浏览器接收到响应后,会根据响应头中的
Set-Cookie
指令存储Cookie。由于设置了路径,浏览器只会在访问指定路径时发送这个Cookie。 - 浏览器显示结果:浏览器会向用户显示返回的字符串”set cookie”,表示Cookie设置操作已经完成。
- Cookie的使用:从这一刻起,只要用户在浏览器中访问了指定路径,浏览器就会自动携带这个Cookie。服务器可以通过检查请求中的Cookie来识别用户或会话。
Session
存在服务器中,比Cookie更安全,响应时通过Cookie将session的id传给服务器,浏览器下次请求时会带上session的id。
缺点是会增加服务端的压力。
1 |
|
问题:若使用分布式服务器,中间件使用nginx负载均衡服务器,若浏览器发送了两次请求,第二次发送的请求,第二次请求的服务器上没有存这个session了
解决方法:粘性session (固定的id传给同一个服务器处理,缺点是负载不均衡)、同步session(当一个服务器创建了一个session就会同步给别的服务器,缺点是同步影响服务器性能、服务器之间耦合影响部署)、共享session(单独一台服务器存储session,缺点是这台服务器崩溃后其他服务器也不能用了)
主流方法:不存session了,尽量存cookie,敏感数据存在数据库(nosql数据库如Redis)中
生成验证码
引入Kaptcha
包用于生成验证码
1 | <dependency> |
逻辑是:用户发送一个请求,服务端生成一个验证码,用session保存文本,将图片响应给浏览器。
登录
刷新验证码
用JS写,因为用户刷新验证码不需要刷新整个页面,体验更好
1 | <div class="col-sm-4"> |
登录、退出
实现逻辑:
- 获取登录页面
- 验证账号、密码、验证码(成功,生成登录凭证给客户端,跳转到首页;失败,跳转到登录页面显示提示信息)
- 退出(登录凭证设置为失效,跳转到首页)
生成登录凭证:
Dao
层:与
login_ticket
数据库交互,查询、增加、更新,写完后在Test
类中进行测试Service
层:用户登录不成功有多种情况,账号不存在、账号为激活、账号为空、密码为空、密码不正确
登录成功后要把生成的
ticket
传给客户端Controller
层:需要的参数有表单中提交的参数用户名、账号、验证码、是否记住账号,还需要Session从中获取生成的验证码。登录成功后要把登录凭证
ticket
传给cookie,因此还需要HttpServletResponse
并将cookie保存到响应体reponse
。判断是否勾选记住密码,选择对于的登录凭证超时时间。
login.html
页面:- 表单上需要有
name
属性,提交给服务器才能获取 - 账号密码不正确需要回到这个页面上还有账号密码信息,
th:value="${param.username}"
作用是从request参数中取参数
- 表单上需要有
显示登录信息
功能:用户登录后,要显示用户的头像以及个人信息,头部显示首页和消息,不显示注册和登录。
拦截器
HandlerInterceptor
类有三阶段:
preHandle
: 在Controller之前执行postHandle
: 在Controller之后执行afterCompletion
: 在TemplateEngine之后执行
拦截器实现后,需要在WebMvcConfigurer
实现类中添加该拦截器,并设置排除对静态资源的拦截,以及需要拦截的页面
1 |
|
实现HostHolder
对象
从线程中保存user
、获取user
,在线程结束时进行清理
线程在服务器处理完本次浏览器的请求后,线程结束。
1 |
|
利用拦截器显示用户信息
在请求开始时,通过凭证找到用户,将用户存入
HostHolder
中在模板引擎执行之前,获取用户
在模板引擎执行之后,清理数据
WebMvcConfigurer
实现类中添加拦截器修改页面,未登录时看不到消息,没登陆时显示注册、登录(登录后不显示)
上传头像(文件)
上传文件:
- 必须是
POST
请求 - 表单要加上
enctype="multipart/form-data"
- Spring MVC: 提供
MultipartFile
处理上传文件
开发步骤:
- 访问账号设置页面
- 处理表单,上传头像,存储图像(本地服务器)
- 增加一个请求:获取头像
Service层
:增加更新用户头像的方法Controller层
:
上传流程: 用户选择并上传文件,后端验证文件格式,生成随机文件名并保存文件到服务器,更新数据库中头像的 URL。
获取流程: 用户请求头像文件,后端根据文件名读取服务器上的文件,设置合适的响应类型,并将图片文件传递给客户端。
更改密码(作业,自己写的)
开发步骤:
- 访问账号设置页面
- 处理表单,更改密码
Service层
:增加更新用户密码的方法、校验旧密码是否正确Controller层
:
获取当前用户的原密码 ->
校验用户输入的原密码是否与当前密码一致。如果不一致,添加错误信息并返回错误页面。 ->
如果新密码和确认密码为空,添加错误信息。如果新密码和确认密码不一致,添加错误信息。确保新密码至少为 8 个字符长。如果不满足条件,添加错误信息。 ->
校验完毕后,更新用户的密码。跳转登录页面。
检查登录状态
没登录前,在浏览器里输入一些的页面路径,要用拦截器拦截,不能访问。
使用拦截器方法:
- 在方法钱标注自定义注解
- 拦截所有请求,只处理带有该注解的方法
自定义注解:
常用元注解:
@Target(声明自定义注解可以作用在哪些类或方法上)
@Retention(声明自定义注解有效时间)
@Document(声明自定义注解在生成文档时是否要带)
@Inherited(指定子类是否要继承父类的注解)
举例:1
2
3
4
public LoginRequired {
}如何读取注解
用到反射
Method.getDeclaredAnnotations()
Method.getAnnotation(Class<T> annotationClass)
1 |
|
问题
为什么要把登录凭证保存到cookie
获取Cookie中的登录凭证,在每次请求时显示登录信息
为什么表单上需要有
name
属性,提交给服务器才能获取name
属性用于标识表单控件,使浏览器在提交表单时能够正确地传递用户输入的数据。- 服务器端依赖这些键值对来解析和处理表单数据。
- 如果表单控件没有
name
属性,控件的值不会被提交,导致服务器无法获取用户的输入数据。
根据cookie中的登录凭证获取到用户后,怎么存?
不能存在session中,因为用户信息是隐私信息。也不能存在类中,因为浏览器有很多请求,一个服务器并发处理多个请求,需要隔离存储。
使用一个线程本地存储(ThreadLocal),用于存储当前线程的用户信息。在一次请求处理中,它的作用流程如下:
- 存储用户信息: 在请求开始时,从cookie中获取登录凭证,根据凭证查找用户信息,并将用户信息存储在
HostHolder
中。 - 访问用户信息: 在整个请求处理过程中,业务逻辑可以随时从
HostHolder
中获取当前用户的信息,而无需再次查找或验证。 - 清理用户信息: 请求处理完毕后,清理
HostHolder
中的用户信息,防止内存泄漏和数据污染。
- 存储用户信息: 在请求开始时,从cookie中获取登录凭证,根据凭证查找用户信息,并将用户信息存储在
为什么要使用拦截器来设置
HostHolder
使用拦截器是为了在请求特定的阶段执行逻辑操作,拦截器有三个阶段,
preHandle
、postHandle
、afterCompletion
说说登录模块
• 用户点击首页上的 登录按钮,服务端响应 登录页面。
• 用户在 登录页面 上输入 用户名、密码、验证码,然后点击登录按钮,服务端接收到 POST 请求,验证账号和密码的正确性。
• 若 验证码错误,返回错误信息,提示 验证码不正确,跳转回登录页面。
• 若 账号不存在或密码错误,返回相关错误信息,跳转回登录页面。
• 若 账号和密码正确,则:
• 生成 登录凭证(ticket),存入数据库,并返回给客户端(Cookie 存储)。
• 若用户勾选 “记住我”,设置 较长的过期时间,否则使用默认的短时间登录状态。
• 服务端返回 首页,用户登录成功。