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

项目中spring security与jwt.腾讯面试分享

写这篇文章是为了记录我面试pcg时平时没有留意或者钻研的地方。

面试是根据项目问的问题:

为什么采用jwt存储token?

        我的项目是微服务项目,里面部署了资源服务和认证服务,这里选择jwt作为token一方面是可以存储用户的信息,可以将用户身份信息存储在令牌中,用户认证通过后认证服务颁发令牌给用户,用户将令牌存储在客户端,去访问应用服务时携带令牌去访问,服务端从jwt解析出用户信息。也就是所说的无状态认证。如果是session认证的话存储在服务端会加大服务端的压力,不适合运用于分布式系统。

总结来说就是:灵活性,一致性,故障发生的可用性,以及多平台维护的代价。

        另一个目的也就是去实行资源的自认证,每次经过网关的时候,就判断令牌的有效性。以此来决定是否可以访问该资源。

jwt存储信息都存储在哪里,关键信息存储在里面么?

jwt的组成部分一般为三部分,头部,负载,和签名部分。我存储的信息一般放置于负载部分,当然,肯定是不建议将密码等私密信息存储在里面的。

spring security是如何和jwt进行结合的呢?

换句话说,如何设置springsecurity的令牌?

        这里我之前都是知道怎么去运用,并没有去看过源码,只知道这么配置一下就好了。但项目被拷打了,问了我意想不到的地方,但还是要好好去琢磨琢磨。

首先我们看令牌的配置在哪里

AuthorizationServerConfigurerAdapter这个类
ClientDetailsServiceConfigurer客户端详情的配置
AuthorizationServerSecurityConfigurer安全配置,谁能允许通过

说白了这三个配置都是需要你去实现的。但是我们这里只讲令牌。

这个bean是我们自己配置的,定义了bean并指定了名字。

配置一个TokenConfig

定义令牌服务:

可以看到这两个bean的名字是一样的。refreshToken解决的其实是令牌的续期问题。当jwt令牌快过期时使用刷新令牌可以再次生成jwt令牌。

jwt的加密方式是什么?

可以知道,jwt的整体可以用HMAC算法或使用RSA的公钥/私钥对来签名。我们的整理就是

  HMACSHA256(
    base64UrlEncode(header) + "." +
    base64UrlEncode(payload),
    secret)

这样的结构。使用base64编码对header和payload两个部分进行编码,再和secret一起加密。

base64:在参数传输的过程中经常遇到的一种情况:使用全英文的没问题,但一旦涉及到中文就会出现乱码情况。与此类似,网络上传输的字符并不全是可打印的字符,比如二进制文件、图片等。Base64的出现就是为了解决此问题,它是基于64个可打印的字符来表示二进制的数据的一种方法。 但是原理我个人认为看一遍就好了,不用去死记硬背。

数据库里的密码是明文存储的吗?前后端密码验证时如何比对的?

这个是我自己思考的问题,也是我重新看代码的时候看的。

说到这里,我们不妨从头开始顺一下spring security自带的流程。

我们输入的密码一般是变成表单提交,那么它会被封装到AUthentication里。

我们可以看到这里的调用关系AuthenticationManager调用DaoAuthenticationProvider,而DaoAuthenticationProvider里的UserDetailsService里又会调用loadUserByUsername来获取用户的信息,并将获取到的用户信息以springsecurity里的Userdetail方式返回。注意这里通过PasswordEncoder的BCryptPasswordEncoder方式,这个是暗文存储。我觉得可以记一下这个加密方式,因为被问了,我就没记住这个,因为我觉得这个不是重点但还真问了。

BCryptPasswordEncoder

配置这个来决定security框架自身的密码比对。密码的形式,这个是自带的!!

当然也可以手动的自行配置,不用框架自带的 UserDetailsService也就是可以重写loadUserByUsername方法和比对方式。

这里就会发现,encode里竟然发现了salt字样,没错,BCryptPasswordEncoder里面自带加盐操作,如果你不指定,那么他会按照自己的配置的一些值来给你加盐。

获取盐代码:

加密算法:

通过输入密码和根据密码特点获得的盐来进行加密

random.nextBytes(rnd);

因为每次的 salt 不同,因此每次的 hash 也不同。这样就可以使得相同的 明文 生成不同的 密文

那么如果这个数据库中的密码,我们是通过BCryptPasswordEncoder加密的存储在数据库当然中的。我们应该如何进行比较用户输入的是否正确呢?因为用户输入的密码也不是暗文的啊。

那么怎么知道用户输入的密码和数据库中的密码是否一致呢?

这里还是要看BCryptPasswordEncoder的源码

继续跟踪hashpw这个方法,我们会发现和加密时候的方法hashpw是一样的。

我们会发现,这个方法会将自己定义的盐取出来,并且base64解码,最后和我们的密码加密一下,然后和之前的数据库的暗文进行对比。

是否了解加盐,如何加盐,加盐的意义是什么?

个人理解我觉得可以和jwt的secret部分做类比。都是通过添加随机的一部分。安全的提高就是:随机数+混入形式。

        密码加盐里包含随机值和加密方式。随机值是随机产生的,并且以随机的方式混在原始密码里面,然后按照加密方式生成一串字符串保存在服务器。换言之,这个是单向的,电脑也不知道客户的原始密码,即使知道加密方式,反向推出的加密前的字符串也是真正密码与随机值混合后的结果,从而无法解析用户的真正密码。那么是如何验证密码的呢?当你再次输入密码,会以相同的加盐方式生成字符串,如果和之前的一致,则通过。而其它用户无法获得这种加密方式:即生成哪些随机数,以什么方式混入进去,自然就很安全。

如何生成随机字符串?

可以使用org.apache.commons.lang包下有一个RandomStringUtils类,其中有一个randomAlphanumeric(int length)函数,可以随机生成一个长度为length的字符串。 

RandomStringUtils.randomAlphanumeric(10);

加盐的意义用gpt解释的来看吧。 

前后端密码验证的时候是如何比对的?

jwt的密钥存储在哪里?

这里附属上gpt

jwt的负载部分内容是从哪获得的?

这个也是自己的思考,其实我也好奇为什么这么设置就可以改变jwt里负载的内容。

这个还得跟踪spring security里代码

点击一下

JwtAccessTokenConverter 

我们可一看到这个里面实现了两个类,我们可以通过tokenenhance里的enhance编写自己的enhance方法来增强扩展这个token信息。当然这里要看的是AccessTokenConverter

一直好奇这里的负载信息哪来的,这就有个信息提取方法

终于看到了这个username

这里其实也就是我们在返回userdetail信息里的username

而extractAutentication里的其他信息我画红色的也来自于我们的客户端配置

  public void configure(ClientDetailsServiceConfigurer clients)throws Exception {clients.inMemory().withClient()// client_id.secret()//客户端密钥.resourceIds()//资源列表.authorizedGrantTypes()// 该client允许的授权类型.scopes()// 允许的授权范围.autoApprove()//false跳转到授权页面//客户端接收授权码的重定向地址.redirectUris();}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • ARM GNU 汇编 “每日读书“
  • 2.1基本算法之枚举1978:生理周期
  • opencv人脸识别实战2:刷脸功能(PyCharm实现)
  • 测试开发(6)软件测试教程——自动化测试selenium(自动化测试介绍、如何实施、Selenium介绍 、Selenium相关的API)
  • python INI文件操作与configparser内置库
  • 零难度!台式电脑如何连接蓝牙耳机?简单几步完成
  • 推特API(Twitter API)V2 查询用户信息
  • vscode的c++开发环境配置
  • SDK集群模式数据库中,ETCD起什么作用?
  • GEE:计算一个遥感影像的空像素占比
  • 天府锋巢直播产业基地:直播带岗,成都直播基地奔向产业化
  • 2024 年 AI 辅助研发趋势
  • TimescaleDB 开源时序数据库
  • 第108讲:Mycat实践指南:枚举分片下的水平分表详解
  • [数据结构]OJ用队列实现栈
  • JavaScript-如何实现克隆(clone)函数
  • -------------------- 第二讲-------- 第一节------在此给出链表的基本操作
  • C++11: atomic 头文件
  • Consul Config 使用Git做版本控制的实现
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • ERLANG 网工修炼笔记 ---- UDP
  • export和import的用法总结
  • If…else
  • Java 23种设计模式 之单例模式 7种实现方式
  • linux学习笔记
  • MySQL Access denied for user 'root'@'localhost' 解决方法
  • 警报:线上事故之CountDownLatch的威力
  • 开源SQL-on-Hadoop系统一览
  • 开源地图数据可视化库——mapnik
  • 理清楚Vue的结构
  • 利用阿里云 OSS 搭建私有 Docker 仓库
  • 前嗅ForeSpider采集配置界面介绍
  • 使用Tinker来调试Laravel应用程序的数据以及使用Tinker一些总结
  • 腾讯优测优分享 | 你是否体验过Android手机插入耳机后仍外放的尴尬?
  • 微信开放平台全网发布【失败】的几点排查方法
  • Semaphore
  • 机器人开始自主学习,是人类福祉,还是定时炸弹? ...
  • (7) cmake 编译C++程序(二)
  • (TOJ2804)Even? Odd?
  • (企业 / 公司项目)前端使用pingyin-pro将汉字转成拼音
  • (转)可以带来幸福的一本书
  • .NET 4 并行(多核)“.NET研究”编程系列之二 从Task开始
  • .net core + vue 搭建前后端分离的框架
  • .net core 使用js,.net core 使用javascript,在.net core项目中怎么使用javascript
  • .net web项目 调用webService
  • .Net(C#)常用转换byte转uint32、byte转float等
  • .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉
  • .NET/C# 在代码中测量代码执行耗时的建议(比较系统性能计数器和系统时间)...
  • .NET/C# 阻止屏幕关闭,阻止系统进入睡眠状态
  • .net和jar包windows服务部署
  • @Async注解的坑,小心
  • @Transactional事务注解内含乾坤?
  • [ vulhub漏洞复现篇 ] Django SQL注入漏洞复现 CVE-2021-35042
  • [100天算法】-不同路径 III(day 73)
  • [ABC294Ex] K-Coloring