当前位置: 首页 > news >正文

验证用户登录的两种方式

目录

前言

一、Cookie 和 Session

1. 保存用户信息

2. 校验用户信息

3. 局限性

二、令牌

1. 令牌机制的原理

2. 令牌

3. 令牌的生成

4. 令牌的发放

5. 令牌的校验

6. 令牌的优势


前言

用户登录可以通过两种方式实现,一种使用 Cookie 和 Session 的方式,另外一种可以通过令牌的方式,下面分别介绍这两种方式的实现;


一、Cookie 和 Session

1. 保存用户信息

Cookie 是浏览器保存用户信息的机制,Session 是会话,是服务器保存用户信息的机制;

Cookie 中存储的信息通常是 Session ID,通过 Session ID 可以在服务器端找到 Session;

Cookie 和 Session 判断用户是否登录的过程:

用户登录时,可以将用户信息保存到 Session 中;

客户端每次访问服务器时,都会带上 Cookie;

服务器通过 Cookie 中保存的 Session ID,找到相应的 Session;

服务器从 Session 中拿到保存的用户信息:

  • 如果保存的用户信息不为空,证明用户已经登录过,可以访问其它资源;
  • 如果保存的用户信息为 null,证明用户没有登录,可以跳转到用户界面,提示用户登录;

登录时使用 Cookie 和 Session 保存用户信息:

@Slf4j
@RestController
@RequestMapping("/user")
public class UserInfoController {@Autowiredprivate UserInfoService userInfoService;@RequestMapping("/login")public Result<Object> login(String userName, String password, HttpSession session){// 1. 校验参数log.info("/user/login接收到参数 userName = {}, password = {}", userName, password);if(!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)){log.error("用户名或密码为空");return Result.failure("用户名或密码为空");}// 2. 根据用户名获取用户信息UserInfo userInfo = userInfoService.selectUserInfoByName(userName);if(userInfo == null){log.error("用户不存在");return Result.failure("用户不存在");}// 3. 校验用户密码if(!password.equals(userInfo.getPassword())){log.error("密码错误");return Result.failure("密码错误");}// 4. 存储用户信息到会话中userInfo.setPassword("");session.setAttribute(Constant.SESSION_USERINFO_KEY, userInfo);return Result.success(true);}
}

提交用户名和密码的时候,生成一个 Cookie:

使用 Fiddler 抓包,服务器响应时,响应头会带上 Set-Cookie:

2. 校验用户信息

访问其它资源时,每次先判断登录状态:

@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1. 获取会话HttpSession session = request.getSession();// 2. 从会话中获取用户信息UserInfo userInfo = (UserInfo) session.getAttribute(Constant.SESSION_USERINFO_KEY);// 3. 判断用户是否登录if(userInfo == null){return false;}return true;}
}

3. 局限性

由于 Session 是保存在服务器的内存中;

当服务器不是单台服务器,而是一个集群的情况下,客户端请求到达负载均衡装置时,会被随机分配给集群中的任意一台服务器;

这台服务器有可能不是当初登录的那一台服务器,因此 Session 中并没有保存用户信息,就会拦截用户请求,登录就会失败;

因此使用 Cookie 和 Session 的机制解决判断用户是否登录,就需要解决在集群环境下的问题。

二、令牌

为了避免 Cookie 和 Session 在集群环境下的判断用户登录问题,可以使用令牌机制;

下面以 JWT 令牌为例进行演示。

1. 令牌机制的原理

客户端登录时,服务器生成一个令牌,即一个字符串;

服务器将生成的令牌发送给客户端;

客户端保存这个令牌,后续每次登录都带上这个令牌;

服务器收到客户端请求后,先校验这个令牌,校验成功后表示已经登录,校验失败时表示未登录;

2. 令牌

令牌通常分为 3 个部分:

  • 第一部分为 header,保存的是令牌的类型等信息;
  • 第二部分为 body,保存的是用户的信息(非敏感信息),例如用户名,用户编号等;
  • 第三部分为用户的签名,用于校验用户令牌是否为真,而不是伪造的;

3. 令牌的生成

先生成一个密钥,后续生成令牌时,需要使用这个密钥进行签名,验证令牌时,也需要使用这个密钥验证令牌是否为正;

生成的密钥通常是一个对象,是一个结构化的二进制数据,为了方便保存,可以将这个对象通过 base64 编码成一个字符串,这个字符串也是密钥;

    public void genKey(){// 1. 生成 keyKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256);// 2. 对 key 进行 Base64 编码String secretKey = Encoders.BASE64URL.encode(key.getEncoded());System.out.println(secretKey);}

生成令牌时,使用这个字符串密钥,通过 base64 解码,还原成原来的对象密钥;

再结合密钥的类型,用户信息,使用对象密钥进行签名,生成一个令牌;

@Slf4j
public class JwtUtil {// 过期时间private static final long EXPIRATION_TIME = 60 * 60 * 1000;// 生成的字符串private static final String secretKey = "Lgo2v38vK8lNfzwbDW7ZmlZ9Sps5TF98JOcnjbs7f4g";// 字符串再解码成 keyprivate static final Key key = Keys.hmacShaKeyFor(Decoders.BASE64URL.decode(secretKey));public static String genJwtToken(Map<String, Object> claim){// 1. 调用 jwt 的 api,生成 token - 生成 token 需要密钥,用于生成签名String jwtToken = Jwts.builder().setClaims(claim).setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)).signWith(key).compact();return jwtToken;}
}

4. 令牌的发放

用户登录成功,将令牌作为结果返回给客户端,客户端可以将密钥保存在 Cookie 中,也可以将密钥保存在本地内存中;

    @RequestMapping("/login")public Result<Object> login(String userName, String password, HttpSession session){// 1. 校验参数log.info("/user/login接收到参数 userName = {}, password = {}", userName, password);if(!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)){log.error("用户名或密码为空");return Result.failure("用户名或密码为空");}// 2. 根据用户名获取用户信息UserInfo userInfo = userInfoService.selectUserInfoByName(userName);if(userInfo == null){log.error("用户不存在");return Result.failure("用户不存在");}// 3. 校验用户密码if(!SecurityUtil.verify(password, userInfo.getPassword())){log.error("密码错误");return Result.failure("密码错误");}// 4. 生成 tokenMap<String, Object> claim = new HashMap<>();claim.put(Constant.TOKEN_USER_ID, userInfo.getId());claim.put(Constant.TOKEN_USER_NAME, userInfo.getUserName());String token = JwtUtil.genJwtToken(claim);// 5. 每次登录后都更新数据库 password 列的值Integer updateResult = userInfoService.updatePssword(SecurityUtil.encrypt(password), userInfo.getId());return Result.success(token);}

浏览器保存令牌:

5. 令牌的校验

客户端后续访问时,都会带上令牌,服务器收到请求后,使用相同的对象密钥,对令牌进行校验;

如果校验不通过,表示用户没有登录,校验通过表示用户已经登录过了;

    public static Claims verifyJwt(String token){log.info("接收到 token: {}", token);// 1. 生成一个用于校验的对象JwtParser parser = Jwts.parserBuilder().setSigningKey(key).build();// 2. 调用 api 进行校验Claims claims = null;try{claims = parser.parseClaimsJws(token).getBody();if(claims == null){log.error("token 不正确");return null;}}catch(Exception e){log.error("解析 token 失败");return null;}return claims;}

每次访问保护的资源时,都需要进行校验:

@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1. 从请求中拿到 tokenString token = request.getHeader(Constant.USER_LOGIN_TOKEN);// 2. 校验 tokenClaims claims = JwtUtil.verifyJwt(token);if(claims == null){response.setStatus(401);return false;}return true;
}

6. 令牌的优势

使用令牌判断用户登录状态时,不需要引入额外的资源,在原有的机器上实现即可;

在集群环境中使用令牌,不管是哪个服务器给发方的令牌,其余的服务器,都具备校验用户 token的能力,因为都能获取生成的字符串密钥,解码生成对象密钥;


http://www.dtcms.com/a/504148.html

相关文章:

  • 笔试-精准核酸检测
  • 知识就是力量——制作一个红外计数器
  • 做网站如何大众汽车网站建设
  • 【Linux笔记】网络部分——应用层自定义协议与序列化
  • 上海招聘网站排名米方科技网站建设
  • 佛山网站建设企业推荐房地产交易网站模版
  • 江苏和住房建设厅网站深圳网站关键词
  • Qt--命名,快捷键及坐标系
  • 容器:软件世界的标准集装箱
  • 音乐网站系统源码百度引擎搜索引擎入口
  • 门户网站如何制作想学习做网站
  • 建设项目安监备案网站深圳公司贷款
  • 企业网站关键词应如何优化网站建设公司swot分析
  • 09_AI智能体开发环境搭建之Redis安装配置完整指南
  • Oracle RMAN三种不完全恢复实战详解:归档序号、时间点与SCN恢复对比
  • 公司网站托管网站做5级分销合法吗
  • 记事本做网站如何添加图片开发公司空置房物管费归口什么费用
  • 新网站建设渠道打开网页链接
  • Python 爬虫常用库:requests 与 BeautifulSoup 详解
  • 什么是MySQL JOIN查询的驱动表和被驱动表?
  • 网站推广服务费计入什么科目自适应网站开发文字大小如何处理
  • minio 数据库迁移
  • 佛山网站设计实力乐云seo规划电子商务网站建设方案
  • 大文件分片上传:简单案例(前端切割与后端合并)
  • 门户网站是网络表达吗山东国舜建设集团网站
  • dw网站建设字体颜色app网页设计网站
  • C++ vector类的模拟实现
  • 踏云wordpress主题移动建站优化
  • 做网站通过什么挣钱手机微网站建设方案
  • 达梦数据库的命名空间