Springboot单点登录的实现2

129 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

  • 如何实现单点登录
    1. 实现jwt机制

      • 自己简单实现jwt。(实现MD5类型的举例,由于代码比较简单,所以不贴代码了)

        1. 准备header标头json字符串 {"typ":"JWT","alg":"HS256"}

        2. 准备好payload的自定义参数json字符串{"userId":"001"}

        3. 准备好MD5算法的盐值 String SING="123321"

        4. 生成JWT

          String headerBase64=Base64.encodeBase64(准备好的标头字符串)
          String payloadBase64=Base64.encodeBase64(准备好的payload字符串)
          String singnature=MD5(headerBase64+payloadBase64+准备好MD5算法的盐值)
          String jwt=headerBase64+"."+payloadBase64+"."singnature
          
        5. 验证JWT

           String jwtArray=jwt.split("."); //根据点.切割字符串
           if(jwtArray[2].equals(MD5(jwtArray[0]+jwtArray[1]+准备好MD5算法的盐值))){
                有效 
            }else{
                无效
            }
          //TODO 额外的payload中自定义的参数的判断,比如有效时间判断
          
        6. 将要超时主动续期和注销退出登录功能的额外实现(TODO)

      • 使用开源框架实现jwt。(这里选择使用 Auth0 Java JWT 实现举例)

        1. 添加依赖

        <dependency>
           <groupId>com.auth0</groupId>
           <artifactId>java-jwt</artifactId>
           <version>3.10.2</version>
        </dependency>
        <!-- 以下两个依赖非必须,当生成token失败的时候根据报错如果是缺失这个才需要加 -->
        <dependency>
         <groupId>com.fasterxml.jackson.core</groupId>
         <artifactId>jackson-core</artifactId>
        </dependency>
        <dependency>
         <groupId>com.fasterxml.jackson.core</groupId>
         <artifactId>jackson-databind</artifactId>
        </dependency>      
        
        1. 生成jwt
        // 生成 JWT Token
        Algorithm algorithm = Algorithm.HMAC256(SING);
        String token = JWT.create()
              .withHeader(new HashMap<>()) //自定义额外标头参数 map类型
              .withClaim("payloadData1", 1) //自定义额外负载参数 int类型
              .withClaim("payloadData2", "2") //自定义额外负载参数 String类型
              .withClaim("payloadData3", true) //自定义额外负载参数 Boolean类型
              .withIssuer("Self") // 预定义payload字段:签发者的名称,没设置则不会出现在负载中。取的时候也会返回null
              .withSubject("Test Auth0 JWT") // 预定义payload字段:jwt所面向的用户的值,没设置则不会出现在负载中。取的时候也会返回null
              .withAudience("Audience X", "Audience Y", "Audience Z") // 预定义payload字段:该jwt由谁接收,没设置则不会出现在负载中。取的时候也会返回null
              .withExpiresAt(DateUtil.getChangeDate(20, TimeUnit.MINUTES)) //jwt过期时间(某个时间点之后就失效了)
              .withNotBefore(DateUtil.getChangeDate(0, null)) // 定义在什么时间之前的时间(时间倒着走是不会过期的),该jwt都是不可用的.
              .withIssuedAt(DateUtil.getChangeDate(0, null)) //生成签名的时间
              .withJWTId("jwt-id-1")
              .sign(algorithm);
        
        1. 验证jwt。(JWT.require(algorithm)里面的algorithm要传上面对应的加密算法出来的algorithm)

    2. 实现各个子系统直接的单点登录

      • 如果子系统的盐值相同,就可以对同一个jwt进行相同的是否有效的判断。所以这个部分比较简单。每个子系统用相同的盐值实现上面第3不验证jwt即可。
    3. 主动注销和过期有效性的处理

      • 用户主动退出登录,使得jwt失效
        为了防止jwt在过期时间内一直有效,需要加上注销jwt的功能。

          由于我想要的是那种服务器或者redis挂了,功能也能用的方式。
          又会回到有状态的情况。暂时还没有想到好的解决办法(持久化、读数据库啥的就算了)。
          //暂时用客户端清除jwt实现 。。。。。。。。
        
      • 如果jwt快要过期,则对jwt进行续期
        为了防止jwt用着用着突然过期,需要加上续期功能

          if( decodedJWT.getExpiresAt()-now<3分钟){
                //生成新的jwt返回给客户端存储
          }