面经---Java--01

1,java中的数组这种数据结构的优缺点

优点:查询或者检索某个下标上的元素时效率极高。
原因:
1.每个元素的内存地址在空间存储上是连续的。
2.每一个元素类型相同,所以占用空间大小相同。
3.首元素地址即数组地址,知道首元素地址,知道每个元素占用的空间大小,又知道下标,所以通过计算可以知道某个下标上元素的内存地址。
4.数组中存储100个元素,或存储100万个元素,在元素查询或检索方面,效率是相同的。因为数组中元素查找的时候不会一个一个找,是通过数学表达式计算出来的(算出一个内存地址,直接定位)
缺点:
1.为了保证元素的内存地址连续,所以随机增删效率较低,会涉及到之后的元素前移或者后移的操作
2.数组不能存储大数据量,因为很难在内存空间找到一块特别大的连续的空间。
3.对于数组最后一个元素的增删是没有效率影响的。
4.扩容麻烦,只能新建一个更大的数组,将元素一个一个移进去,效率低。

2,什么是JWT

起源
JWT 主要用于用户登录鉴权,所以我们从最传统的 session 认证开始说起。

session认证
众所周知,http 协议本身是无状态的协议,那就意味着当有用户向系统使用账户名称和密码进行用户认证之后,下一次请求还要再一次用户认证才行。因为我们不能通过 http 协议知道是哪个用户发出的请求,所以如果要知道是哪个用户发出的请求,那就需要在服务器保存一份用户信息(保存至 session ),然后在认证成功后返回 cookie 值传递给浏览器,那么用户在下一次请求时就可以带上 cookie 值,服务器就可以识别是哪个用户发送的请求,是否已认证,是否登录过期等等。这就是传统的 session 认证方式。

session 认证的缺点其实很明显,由于 session 是保存在服务器里,所以如果分布式部署应用的话,会出现session不能共享的问题,很难扩展。于是乎为了解决 session 共享的问题,又引入了 redis,接着往下看。

token认证
这种方式跟 session 的方式流程差不多,不同的地方在于保存的是一个 token 值到 redis,token 一般是一串随机的字符(比如UUID),value 一般是用户ID,并且设置一个过期时间。每次请求服务的时候带上 token 在请求头,后端接收到token 则根据 token 查一下 redis 是否存在,如果存在则表示用户已认证,如果 token 不存在则跳到登录界面让用户重新登录,登录成功后返回一个 token 值给客户端。

优点是多台服务器都是使用 redis 来存取 token,不存在不共享的问题,所以容易扩展。缺点是每次请求都需要查一下redis,会造成 redis 的压力,还有增加了请求的耗时,每个已登录的用户都要保存一个 token 在 redis,也会消耗 redis 的存储空间。

有没有更好的方式呢?接着往下看。

什么是JWT
JWT (全称:Json Web Token)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为 JSON 对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

流程图
![在这里插入图片描述](https://img-blog.csdnimg.cn/eedfc81cc1404255a79a3ba54eae4382.png
用户使用账号、密码登录应用,登录的请求发送到 Authentication Server。
Authentication Server 进行用户验证,然后创建 JWT 字符串返回给客户端。
客户端请求接口时,在请求头带上 JWT。
Application Server 验证 JWT 合法性,如果合法则继续调用应用接口返回结果。
流程描述一下:

  1. 用户使用账号、密码登录应用,登录的请求发送到 Authentication Server。
  2. Authentication Server 进行用户验证,然后创建 JWT 字符串返回给客户端。
  3. 客户端请求接口时,在请求头带上 JWT。
  4. Application Server 验证 JWT 合法性,如果合法则继续调用应用接口返回结果。

可以看出与token方式有一些不同的地方,就是不需要依赖 redis,用户信息存储在客户端。所以关键在于生成 JWT 和解析 JWT 这两个地方。

JWT的数据结构
在这里插入图片描述

Header
JWT 第一部分是头部分,它是一个描述 JWT 元数据的 Json 对象
Payload
JWT 第二部分是 Payload,也是一个 Json 对象,还有七个默认的字段供选择。

iss (issuer):签发人/发行人
sub (subject):主题
aud (audience):用户
exp (expiration time):过期时间
nbf (Not Before):生效时间,在此之前是无效的
iat (Issued At):签发时间
jti (JWT ID):用于标识该 JWT
使用 Base64 URL 算法转换为字符串后保存
Signature
WT 第三部分是签名。是这样生成的,首先需要指定一个 secret,该 secret 仅仅保存在服务器中,保证不能让其他用户知道。这个部分需要 base64URL 加密后的 header 和 base64URL 加密后的 payload 使用 . 连接组成的字符串,然后通过header 中声明的加密算法 进行加盐secret组合加密,然后就得出一个签名哈希,也就是Signature,且无法反向解密

Application Server 如何进行验证呢?
可以利用 JWT 前两段,用同一套哈希算法和同一个 secret 计算一个签名值,然后把计算出来的签名值和收到的 JWT 第三段比较,如果相同则认证通过。

JWT的优点
json格式的通用性,所以JWT可以跨语言支持,比如Java、JavaScript、PHP、Node等等。
可以利用Payload存储一些非敏感的信息。
便于传输,JWT结构简单,字节占用小。
不需要在服务端保存会话信息,易于应用的扩展。
生成JWT

@Component
public class JwtUtil {

    @Value("${jwt.secretKey}")
    private String secretKey;

    public String createJWT(String id, String subject, long ttlMillis, Map<String, Object> map) throws Exception {
        JwtBuilder builder = Jwts.builder()
                .setId(id)
                .setSubject(subject) // 发行者
                .setIssuedAt(new Date()) // 发行时间
                .signWith(SignatureAlgorithm.HS256, secretKey) // 签名类型 与 密钥
                .compressWith(CompressionCodecs.DEFLATE);// 对载荷进行压缩
        if (!CollectionUtils.isEmpty(map)) {
            builder.setClaims(map);
        }
        if (ttlMillis > 0) {
            builder.setExpiration(new Date(System.currentTimeMillis() + ttlMillis));
        }
        return builder.compact();
    }


    public Claims parseJWT(String jwtString) {
        return Jwts.parser().setSigningKey(secretKey)
                .parseClaimsJws(jwtString)
                .getBody();
    }
}

service层

@Service
public class UserServiceImpl implements UserService {

    @Resource
    private JwtUtil jwtUtil;

    @Resource
    private UserMapper userMapper;

    @Override
    public String login(String userName, String passWord) throws Exception {
        //登录验证,password存于数据库中,最好别存明文,可通过mad5加密转换
        User user = userMapper.findByUserNameAndPassword(userName, passWord);
        if (user == null) {
            return null;
        }
        //如果能查出,则表示账号密码正确,生成jwt返回
        String uuid = UUID.randomUUID().toString().replace("-", "");
        HashMap<String, Object> map = new HashMap<>();
        map.put("name", user.getName());
        map.put("age", user.getAge());
        //调用jwtUtil生成token
        return jwtUtil.createJWT(uuid, "login subject", 0L, map);
    }
}

解析JWT
反序列化获取jwt,并获取jwt中的用户数据

@RestController
@RequestMapping("/jwt")
public class TestController {

    @Resource
    private JwtUtil jwtUtil;

    @RequestMapping("/test")
    public Map<String, Object> test(@RequestParam("jwt") String jwt) {
        //这个步骤可以使用自定义注解+AOP编程做解析jwt的逻辑,这里为了简便就直接写在controller里
        Claims claims = jwtUtil.parseJWT(jwt);
        String name = claims.get("name", String.class);
        String age = claims.get("age", String.class);
        HashMap<String, Object> map = new HashMap<>();
        map.put("name", name);
        map.put("age", age);
        map.put("code", "0");
        map.put("msg", "请求成功");
        return map;
    }
}

这个 jwt 字符串能不能被伪造呢?
除非你知道 secretKey,否则是不能伪造的
原理是根据前面两部分(Header、Payload)加上 secretKey 使用 Header 指定的哈希算法计算出第三部分(Signature),所以可以看出最关键就是 secretKey。secretKey只有服务端自己知道,所以客户端不知道 secretKey 的值是伪造不了jwt字符串的。

总结
最后讲讲 JWT 的缺点,因为任何技术都不是完美的,所以我们得用辩证思维去看待任何一项技术。
安全性没法保证,所以 jwt 里不能存储敏感数据。因为 jwt 的 payload 并没有加密,只是用 Base64 编码而已。
无法中途废弃。因为一旦签发了一个 jwt,在到期之前始终都是有效的,如果用户信息发生更新了,只能等旧的 jwt 过期后重新签发新的 jwt。
续签问题。当签发的 jwt 保存在客户端,客户端一直在操作页面,按道理应该一直为客户端续长有效时间,否则当 jwt有效期到了就会导致用户需要重新登录。那么怎么为 jwt 续签呢?最简单粗暴就是每次签发新的 jwt,但是由于过于暴力,会影响性能。如果要优雅一点,又要引入 Redis 解决,但是这又把无状态的 jw t硬生生变成了有状态的,违背了初衷。
所以印证了那句话,没有最好的技术,只有适合的技术。

3,jwt常见的加密算法

在这里插入图片描述
1、可逆加密算法
加密后,密文可以解密得到原文
一般用于签名和认证。私钥服务器保存, 用来加密, 公钥客户拿着用于对于令牌或 者签名的解密或者校验使用.
常见的非对称加密算法有: RSA、DSA(数字签名用)、ECC(移动设备用)、RS256 (采用 SHA‐256 的 RSA 签名)
2、不可逆加密算法
加密后,不能反向解密得到原文
一般用于敏感信息,密码,卡号等不可解密的信息
常见的不可逆加密算法有:MD5、SHA、HMAC 4.3.Base64编码
3、对称加密
加明文分成N个组,然后使用密钥对各个组进行加密,形成各自的密文,然后将密文合并,形成最终的密文
优势:加密速度快,加密效率高
缺点:加密解密密钥一样,不安全
4、非对称加密
加密和解密使用不一样的密钥
优势:安全性高
缺点:效率低
jwt采用的是非对称加密,

base64是网络上最常见的用于传输8bit字节代码的编码方式之一。Base64编码可用于在 HTTP环境下传递较长的标识信息。采用Base64编码解码具有不可读性,即所编码的数据 不会被人用肉眼所直接看到。
注意:Base64只是一种编码方式,不算加密方法。

4,对称加密和非对称加密的区别
1、区别:加密一般分为两种,对称加密和非对称加密。对称加密就是加密解密都用同一个秘钥,比如DES、3DES(TripleDES)和AES等。
非对称加密就是加密和解密不是用的同一种秘钥,比如RSA算法、DSA算法、ECC算法、DH算法等。
在非对称加密中,用来加密的秘钥叫公钥,用来解密的秘钥叫私钥。公钥和私钥都是成对生成的,公钥分发给其他人用来加密,私钥用来解密。
2、优缺点:
对称加密:解密速度快,但保密性差。
非对称加密:加密算法保密性好,它消除了最终用户交换密钥的需要。但是加解密速度要远远低于对称加密。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
java面经-百度准入职老哥整理.pdf》是一份关于百度准入职面试的Java面经整理。这份面经是由百度准入职的老哥整理而成,其中记录了一些面试时可能会遇到的问题以及解答方法。 这份面经对于准备参加百度准入职面试的人来说非常有价值。首先,它列出了一些常见的面试问题,涵盖了Java语言的各个方面,包括基础知识、数据结构与算法、设计模式、多线程、网络编程等等。通过仔细研究和复习这些问题的答案,可以帮助面试者全面了解Java语言的特性和应用。 其次,这份面经还提供了问题的解答思路和方法,帮助面试者理清思路,正确回答问题。这对于很多面试者来说特别有帮助,因为在面试时有时会遇到一些棘手的问题,有了这份面经的指导,面试者可以更好地掌握应对策略。 不过需要注意的是,面经作为一份参考资料,不能完全依赖于它来准备面试。面试官可能会问一些不在面经中列出的问题,因此考生还是需要自己对Java语言有充分的了解,并能够熟练运用。同时,面试官还会关注考生的沟通能力、解决问题的能力以及对新技术的学习和掌握能力。 总体来说,《java面经-百度准入职老哥整理.pdf》是一份非常宝贵的资料,可以帮助面试者对Java面试中可能会遇到的问题有更深入的了解,提供了解答思路和方法。但记住,面试准备还需要多方面的知识积累和实践经验的积累,才能在面试中展现自己的优势。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值