项目技术点

1、Redis+Lua脚本限流

1.1.定义切入点接口(小旗子),添加四个属性并赋初值,后续用到。

项目技术点_第1张图片

1.2.定义切面类,使用前置通知实现IP限流,其中

  1. MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature()----》获得修饰符+ 包名+组件名(类名) +方法名
  2. Method method = methodSignature.getMethod()----》获取方法名
  3. PugRateLimiter annotation = method.getAnnotation(PugRateLimiter.class)----》通过方法名上面的“小旗子”类,获取注解携带的限流参数
  4. Integer limit = annotation.limit()----》获取限制次数
    Integer timeout = annotation.timeout()----》获取限制时间
  5. ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes() ----》获取请求属性
  6. HttpServletRequest request = requestAttributes.getRequest()----》获取请求对象
  7. String methodNameKey = getRateLimiterKey(annotation, joinPoint, request)----》获取redis的key
  8. String userIp = IpUtils.getIpAddr(request)----》获取请求IP
  9. Boolean acquired = stringRedisTemplate.execute(ipLimitLua, Lists.newArrayList(methodNameKey), limit.toString(), timeout.toString())----》请求lua脚本实现限流,根据返回值进一步进行限流返回操作

项目技术点_第2张图片

方法 getRateLimiterKey ---- 获取Redis 的 key,最终Key返回值为:IP-声明的class对象名-方法名

项目技术点_第3张图片

stringRedisTemplate.execute(ipLimitLua, Lists.newArrayList(methodNameKey), limit.toString(), timeout.toString()) 方法,真正实现限流的方法

ipLimiLua----》用来读取lua脚本,序列化为java可以操作的语言

项目技术点_第4张图片

 stringRedisTemplate.execute方法:用来执行lua脚本的,返回的是lua脚本执行之后的返回值。

项目技术点_第5张图片

项目技术点_第6张图片

2、JWT 实现免登录

2.1 JWT 介绍

jwt是为了解决传统session+cookie的登录校验的缺点。

传统的session+cookies的方法,session存储在服务端,这样客户端每次发起请求的时候只能请求同一个服务器,且将校验数据存储在服务器端会占用大量内存,不利于维护和扩展。而且cookie一旦被拦截,则用户很容易遭受跨站请求伪造的攻击。

而基于jwt  token的鉴权机制,则是将认证信息(token)保存在客户端,服务端只需要保存用于加解密的secret(盐/密钥),每次客户端访问时,将首次访问服务端派发的token携带在请求头中,这样服务端拿到token,然后利用服务端存储的secret来进行解密,从而鉴权。服务器需要支持CORS(跨域资源共享)策略,以便于服务器密钥共享。

2.2 JWT组成

JWT 由三部分构成,分别是头部(header)、载荷(payload,类似于飞机上承载的物品)、签证(signature)。将这三部分用“ . ”连接起来,就构成了JWT字符串。

例如:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJVc2VySWQiOjEyMywiVXNlck5hbWUiOiJhZG1pbiJ9.Qjw1epD5P6p4Yy2yju3-fkq28PddznqRj3ESfALQy_U

header

header主要包含两部分信息:

  • 声明类型,这里是jwt
  • 声明加密的算法,通常直接使用HMAC SHA256

完整的头部如:

{ 'typ':'JWT', 'alg':'HS256' }
,然后经过based64加密(该加密是可以对称解密的),构成JWT第一部分:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

payload

载荷是用来存放有效信息的地方,它主要包含三部分:

  • 标准中注册的声明

    1.  iss:jwt签发者

    2. sub:jwt所面向的用户

    3. aud:接收jwt的一方

    4. exp:jwt的过期时间,这个过期时间必须大于签发时间

    5. nbf:定义在什么时间之前,该jwt都是不可用的

    6. iat:jwt的签发时间

    7. jti:jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击 

  • 公共的声明

       公共的声明可以添加任何的信息,一般添加用户的相关信息或其它业务需要的必要信息,但不建议添加敏感信息,因为该部分在客户端可解密 。

  • 私有的声明

      私有的声明是提供者和消费者功能定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为名文信息。

payload:

{ "sub": "1234567890", "name": "John Doe", "admin": true }
,经过based64加密:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

Signature

签证是JWT的第三个部分,这个部分需要base64加密后的header和base64加密后的payload使用“.”连接组成字符串,然后用header中声明的加密方式,然后进行加secret加密。

如:

var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload); var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

最终的JWT,就是由这三部分用“ . " 连接起来组成一个完整字符串:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

jwt 优点:

  • 因为json的通用性,所以JWT是可以跨语言支持的,像C#,JavaScript,NodeJS,PHP等许多语言都可以使用

  • 因为由了payload部分,所以JWT可以在自身存储一些其它业务逻辑所必要的非敏感信息

  • 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的

  • 它不需要在服务端保存会话信息,所以它易于应用的扩展

注意点:

  • 不应该在jwt的payload部分存储敏感信息,因为该部分是客户端可解密的部分

  • 保护好secret私钥。该私钥非常重要

项目分析:

签发token过程:登录接口根据传入用户名来判断是否访问过,没有则派发token

项目技术点_第7张图片

 项目技术点_第8张图片

 项目技术点_第9张图片

项目技术点_第10张图片

项目技术点_第11张图片

项目技术点_第12张图片

项目技术点_第13张图片

项目技术点_第14张图片

 token续期过程:

token

一般是在请求头里加入Authorization,并加上Bearer标注:

fetch('api/user/1', {
headers: { 'Authorization': 'Bearer ' + token
}
})

项目技术点_第15张图片

首先判断请求头中是否有token,token需要前缀Bearer

项目技术点_第16张图片

项目技术点_第17张图片

 若请求头带有token则判断是否过期。

项目技术点_第18张图片

项目技术点_第19张图片

项目技术点_第20张图片

项目技术点_第21张图片

项目技术点_第22张图片

项目技术点_第23张图片

项目技术点_第24张图片

 

你可能感兴趣的:(java)