【任务9】注册与登录:拥有登录功能(初级)
打卡要求如下:
1、新建一个登录页面,命名为 login.html
2、新建一个校验登录的接口,命名为 Login,用户登录成功后跳转到首页。
3、你是如何保存用户登录态的?请把你的大致逻辑写一下。
本问题对应的项目链接:从0设计一个web项目
PS:每次在最后,都要给出你的GitHub链接哦,方便我打开
打卡要求如下:
1、新建一个登录页面,命名为 login.html
2、新建一个校验登录的接口,命名为 Login,用户登录成功后跳转到首页。
3、你是如何保存用户登录态的?请把你的大致逻辑写一下。
本问题对应的项目链接:从0设计一个web项目
PS:每次在最后,都要给出你的GitHub链接哦,方便我打开
共以下 7 个回答
使用session保持用户登录态
https://github.com/CurryisCoat/community/tree/dev
public String login(HttpServletRequest request,HttpServletResponse response) throws IOException {
String username = request.getParameter(“username”);
User user = userService.login(username);
response.setContentType(“text/html;charset=utf-8”);
PrintWriter writer = response.getWriter();
使用session保持用户登录状态
https://github.com/Egofir/ferry
保存用户登陆态的逻辑
在登陆时向session存入随机生成的16位id,在redis中存入随机生成的16位id和userName,在访问主页时检验session和redis中是否存在相应的数据,进行登陆态的判断。
之前自己实现的时候没有考虑拦截其他页面请求的问题,查看评论区之后对自己的代码进行了补充完善,加入拦截器。
github地址:https://github.com/lei342345795/FromZerotoExpert
专门去看了黑马Redis实战的前两节,借鉴了其思想来完善项目。
一开始使用的是 cookie + session 的模式。
tomcat 会将每个请求对应的 session 存到自己的内存中,并将 sessionId 返回给前端,自动存到 cookie,下次访问会自动携带 sessionId,
因此下次访问仍然可以获取到上次请求的 session,从而解决 http 无状态的问题
以上过程对开发者是透明的,由 tomcat 维护,直接用就可以。
接下来是逻辑流程:
用户登陆后,会将用户信息存储到 session 中。
编写配置拦截器,下一次访问需要验证登录态的接口时,会进入拦截器
拦截器负责获取用户信息,如果存在即登录态还在,反之则过期
同时如果没过期,用户访问时要刷新过期时间,防止登录后操作了,但是还是被判定为下线情况
同时为了保证线程安全,使用 ThreadLocal 存储用户信息(脱敏)
如果是单机模式,目前已经足以解决问题,但 session 无法在多台机器中共享(或者说很麻烦),因此引入分布式缓存 redis 来代替 cookie 和 session
更换成 redis 之后,有几个问题需要注意。
其实就是 redis 的 key 的选择,这边借鉴黑马 redis 的思路,用户登录后生成token,发送给前端
前端以后每次请求都携带 token,下次拦截器用 token 去 redis 里面拿
同时注意,要刷新数据过期时间
用户只有访问需要检测登录态的页面才能刷新过期时间
如果登录后访问了不需要登录态的页面,一段时间后仍然被判定为下线
因此要对所有的页面都能刷新登录态过期时间,所以再抽一层,做一个拦截器
github地址(点击即可)
你的项目,有一些小 Bug,比如我登录之后,自己手动修改了一下 token 的值,那么就会打不开首页了(打开时一片空白),而且也没有提示我自动跳转去登录。这样就会导致我一直无法打开(除非我记得登录的地址)
确实考虑的不严谨,谢谢帅地哥指正,已更新
我之前做的项目都是基于Vue加上SpringBoot,而黑马的教程好像是基于JSP的,看了Second和季两位的代码,对JSP构建Web应用有些了解,我还是给出Vue+SpringBoot实现的思路。我只实现了后端相关的,前端Vue的使用还有些不熟悉,后面再补补。
login接口实现思路:首先验证登录参数的合法性,后面根据登录账号查找数据库中的账号对应的加密密码,与加密后的登录密码对比,如果匹配成功,则登录成功,为了保持用户登录态,生成一个accountId+sessionId的键值对(accountId就是根据用户账号生成的,与用户账号对应,因为sessionId要与账号对应,但后面要将这个用户账号以cookie的形式返回给用户,直接返回账号可能有些不安全,就用个当前时间+随机数+账号组合下返回),用redis缓存(redis我之前了解了下原理,这次去网上搜了下怎么使用),设置过期时间为24小时,因为要保持用户登录态24个小时,然后返回键为”accountId”值为生成的accountId给用户。而这个cookie就是用户保证登录态的关键。
isLogin接口实现思路:这个接口就是判断用户是否位于登录态,用户第一次登录成功后,(同一次会话)下次请求中就会带上一个键为”accountId”的cookie,然后我们以这个cookie的值在redis缓存中找,如果找到了一个sessionId与当前请求的session的会话sessionId相同,说明用户位于登录态,不需要重新登录,否则需要重新登录。
登录接口代码
生成accountId代码
GitHub链接:https://github.com/shuaishuaixi03/FromZerotoExpert/tree/dev
我的实现方式如下:
1.设置拦截器,拦截所有路径的请求,只放行与登录注册相关的接口和静态资源
2.自定义登录拦截的拦截器,检测是否登录,若检测已登录过则刷新redis数据的存储时间,并放行访问,否则未登录过,跳转至登录页面进行登录。
在这里,我们利用session存储的sessionId和redis数据库访问用户信息,从而检测是否已登录过,具体细节在登录检测中会谈到。
3.登录检测(未进行登录过的访问请求只能跳转至登录界面进行登录,并进行登录检测)
在这里首先对登录的基本需求进行检测,如用户名是否存在,用户账号密码是否正确等。若登录失败,则返回给前端做出失败的提示。若检测能够进行登录成功,则生成一个UUID当作sessionId,然后将此sessionId和username互为键值存放在redis数据库中(存两个键值对,方便相互查询),这样拦截器通过查询session中的sessionId,并在redis中查询这个sessionId检测是否能获取到用户名,便可以检测是否登录成功过。然后因为看到了下节预告,所以在使用session和redis之前,先用redis数据库检测该用户是否在别的地方登陆过,若有,则将之前在redis中存放的相关键值对失效,然后再存入新的内容,这样就能简单的实现仅后者在线了。最后,将登录检测的结果,返回给前端,告诉前端登录成功/失败,前端进行处理。
注:在这里设置过期时间是60S,方便能体会到用户登录信息失效过期的场景,不然要等一天才过期,有点不好测试自己的代码。
4.前端检测登录状态
5.登录成功,后端在返回主页页面之前,将用户名存储在cookie中供前端调用,动态显示”xxx已登录”
gitHub地址:https://github.com/Second6s9/FromZerotoExpert/tree/dev
gitHub防覆盖地址:https://github.com/Second6s9/FromZerotoExpert/tree/day09
服务器地址:http://123.57.142.33:8088/FromZerotoExpert/
访问首页时的前端判断为:
对于用Cookie存储用户登录态我有点疑问: 不管我在 Cookie上怎么加密,假如有人通过一些手段获取到了我的 Cookie,那么他也可以在他的电脑上添加这个 Cookie 并实现自动登录。应该有更高级的存储用户登录态方式?
服务器链接:http://124.223.184.187:8088/FromZerotoExpert
Github仓库链接:https://github.com/jiangyklala/FromZerotoExpert/tree/dev
你的疑问:
1、一般不考虑cookie这些被人拿,如果被人打开了浏览器,并且把信息拿了,那别人确实可以登录了,我们不考虑这个,目前安全性较高的网站,cooki时间都比较短。如果考虑被人拿,那浏览器自动保存密码,也会被人拿了。
2、那有人会说,我能否暴力刷登录接口?有些网站是不提供api接口给你访问的,只能通过浏览器的前端网页,因为后端接口有网关限制,只对特定的IP开放。不过有些网站也会专门提供接口给别人用,你可以自己去生产token,比如腾讯云,阿里云这些都可以,这个时候的token泄漏了那就没办法了,这就如同你的账户密码也泄口了。
总的来说就是,关于session/cookie这些,我们不考虑安全性,安全性交给其他层面的去维护。
好嘞,谢谢地哥
你的程序有个Bug,比如我登录成功之后,自己在浏览器修改了cookie的值,那么就一直无法登录了。
已修复?