成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

簽發(fā)的用戶認證token超時刷新策略

e10101 / 1407人閱讀

摘要:簽發(fā)的用戶認證超時刷新策略這個模塊分離至項目權(quán)限管理系統(tǒng)與前后端分離實踐,感覺那樣太長了找不到重點,分離出來要好點。這樣在有效期過后的時間段內(nèi)可以申請刷新。

簽發(fā)的用戶認證token超時刷新策略

這個模塊分離至項目api權(quán)限管理系統(tǒng)與前后端分離實踐,感覺那樣太長了找不到重點,分離出來要好點。

對于登錄的用戶簽發(fā)其對應(yīng)的jwt,我們在jwt設(shè)置他的固定有效期時間,在有效期內(nèi)用戶攜帶jwt訪問沒問題,當過有效期后jwt失效,用戶需要重新登錄獲取新的jwt。這個體驗不太好,好的體驗應(yīng)該是:活躍的用戶應(yīng)該在無感知的情況下在jwt失效后獲取到新的jwt,攜帶這個新的jwt進行訪問,而長時間不活躍的用戶應(yīng)該在jwt失效后需要進行重新的登錄認證。

這里就涉及到了token的超時刷新問題,解決方案看圖:

在簽發(fā)有效期為 t 時間的jwt后,把jwt用("JWT-SESSION-"+appId,jwt)的key-value形式存儲到redis中,有效期設(shè)置為2倍的 t 。這樣jwt在有效期過后的 t 時間段內(nèi)可以申請刷新token。
還有個問題是用戶攜帶過期的jwt對后臺請求,在可刷新時間段內(nèi)返回了新的jwt,應(yīng)該在用戶無感知的情況下返回請求的內(nèi)容,而不是接收一個刷新的jwt。我們是不是可以在每次request請求回調(diào)的時候判斷返回的是不是刷新jwt,但是判斷是之后我們是否放棄之前的用戶請求,如果不放棄,那是不是應(yīng)該在最開始的用戶request請求前先保存這個請求,在之后的回調(diào)中如果是返回刷新jwt,我們再攜帶這個新的jwt再請求一次保存好的request請求?但對于前端這么大量的不同請求,這樣是不是太麻煩了?

這困擾了我很久哎,直到我用到了angualr的HttpInterceptor哈哈哈哈哈哈哈哈哈哈哈哈哈哈。

angualr的HttpInterceptor就是前端的攔截過濾器,發(fā)起請求會攔截處理,接收請求也會攔截處理。最大的好處對每次的原始request他都會完整的保存下來,我們向后臺發(fā)生的request是他的clone。next.handle(request.clone)
繼承HttpInterceptor的AuthInterceptor,攔截response判斷是否為refresh token,是則攜帶新token再次發(fā)起保存的request:

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  constructor(private authService: AuthService, private router: Router) {}

  intercept(req: HttpRequest, next: HttpHandler): Observable> {
    const authToken = this.authService.getAuthorizationToken();
    const uid = this.authService.getUid();
    let authReq: any;
    if (authToken != null && uid != null) {
      authReq = req.clone({
        setHeaders: {
          "authorization": authToken,
          "appId": uid
        }
      });
    } else {
      authReq = req.clone();
    }

    console.log(authReq);
    return next.handle(authReq).pipe(
      mergeMap(event => {
        // 返回response
        if (event instanceof HttpResponse) {
          if (event.status === 200) {
            // 若返回JWT過期但refresh token未過期,返回新的JWT 狀態(tài)碼為1005
            if (event.body.meta.code === 1005) {
              const jwt = event.body.data.jwt;
              // 更新AuthorizationToken
              this.authService.updateAuthorizationToken(jwt);
              // clone request 重新發(fā)起請求
              // retry(1);
              authReq = req.clone({
                setHeaders: {
                  "authorization": jwt,
                  "appId": uid
                }
              });
              return next.handle(authReq);

            }
          }
          if (event.status === 404) {
            // go to 404 html
            this.router.navigateByUrl("/404");
          }
          if (event.status === 500) {
            // go to 500 html
            this.router.navigateByUrl("/500");
          }
        }
        console.log(event);
        // 返回正常情況的可觀察對象
        return of(event);
      }),
      catchError(this.handleError)
    );
  }

  private handleError(error: HttpErrorResponse) {
    if (error.error instanceof ErrorEvent) {
      // A client-side or network error occurred. Handle it accordingly.
      console.error("An error occurred:", error.error.message);
    } else {
      console.error( `Backend returned code ${error.status}, ` +
        `body was: ${error.error}`);
    }
    repeat(1);
    return new ErrorObservable("親請檢查網(wǎng)絡(luò)");

  }
}

后端簽發(fā)jwt時所做的:

 /* *
     * @Description 這里已經(jīng)在 passwordFilter 進行了登錄認證
     * @Param [] 登錄簽發(fā) JWT
     * @Return java.lang.String
     */
    @ApiOperation(value = "用戶登錄",notes = "POST用戶登錄簽發(fā)JWT")
    @PostMapping("/login")
    public Message accountLogin(HttpServletRequest request, HttpServletResponse response) {
        Map params = RequestResponseUtil.getRequestParameters(request);
        String appId = params.get("appId");
        // 根據(jù)appId獲取其對應(yīng)所擁有的角色(這里設(shè)計為角色對應(yīng)資源,沒有權(quán)限對應(yīng)資源)
        String roles = accountService.loadAccountRole(appId);
        // 時間以秒計算,token有效刷新時間是token有效過期時間的2倍
        long refreshPeriodTime = 36000L;
        String jwt = JsonWebTokenUtil.issueJWT(UUID.randomUUID().toString(),appId,
                "token-server",refreshPeriodTime >> 2,roles,null, SignatureAlgorithm.HS512);
        // 將簽發(fā)的JWT存儲到Redis: {JWT-SESSION-{appID} , jwt}
        redisTemplate.opsForValue().set("JWT-SESSION-"+appId,jwt,refreshPeriodTime, TimeUnit.SECONDS);
        AuthUser authUser = userService.getUserByAppId(appId);

        return new Message().ok(1003,"issue jwt success").addData("jwt",jwt).addData("user",authUser);
    }

后端refresh token時所做的:

protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object mappedValue) throws Exception {
        Subject subject = getSubject(servletRequest,servletResponse);
        // 判斷是否為JWT認證請求
        if ((null == subject || !subject.isAuthenticated()) && isJwtSubmission(servletRequest)) {
            AuthenticationToken token = createJwtToken(servletRequest);
            try {
                subject.login(token);
//                return this.checkRoles(subject,mappedValue) && this.checkPerms(subject,mappedValue);
                return this.checkRoles(subject,mappedValue);
            }catch (AuthenticationException e) {
                LOGGER.info(e.getMessage(),e);
                // 如果是JWT過期
                if (e.getMessage().equals("expiredJwt")) {
                    // 這里初始方案先拋出令牌過期,之后設(shè)計為在Redis中查詢當前appId對應(yīng)令牌,其設(shè)置的過期時間是JWT的兩倍,此作為JWT的refresh時間
                    // 當JWT的有效時間過期后,查詢其refresh時間,refresh時間有效即重新派發(fā)新的JWT給客戶端,
                    // refresh也過期則告知客戶端JWT時間過期重新認證

                    // 當存儲在redis的JWT沒有過期,即refresh time 沒有過期
                    String appId = WebUtils.toHttp(servletRequest).getHeader("appId");
                    String jwt = WebUtils.toHttp(servletRequest).getHeader("authorization");
                    String refreshJwt = redisTemplate.opsForValue().get("JWT-SESSION-"+appId);
                    if (null != refreshJwt && refreshJwt.equals(jwt)) {
                        // 重新申請新的JWT
                        // 根據(jù)appId獲取其對應(yīng)所擁有的角色(這里設(shè)計為角色對應(yīng)資源,沒有權(quán)限對應(yīng)資源)
                        String roles = accountService.loadAccountRole(appId);
                        long refreshPeriodTime = 36000L;  //seconds為單位,10 hours
                        String newJwt = JsonWebTokenUtil.issueJWT(UUID.randomUUID().toString(),appId,
                                "token-server",refreshPeriodTime >> 2,roles,null, SignatureAlgorithm.HS512);
                        // 將簽發(fā)的JWT存儲到Redis: {JWT-SESSION-{appID} , jwt}
                        redisTemplate.opsForValue().set("JWT-SESSION-"+appId,newJwt,refreshPeriodTime, TimeUnit.SECONDS);
                        Message message = new Message().ok(1005,"new jwt").addData("jwt",newJwt);
                        RequestResponseUtil.responseWrite(JSON.toJSONString(message),servletResponse);
                        return false;
                    }else {
                        // jwt時間失效過期,jwt refresh time失效 返回jwt過期客戶端重新登錄
                        Message message = new Message().error(1006,"expired jwt");
                        RequestResponseUtil.responseWrite(JSON.toJSONString(message),servletResponse);
                        return false;
                    }

                }
                // 其他的判斷為JWT錯誤無效
                Message message = new Message().error(1007,"error Jwt");
                RequestResponseUtil.responseWrite(JSON.toJSONString(message),servletResponse);
                return false;

            }catch (Exception e) {
                // 其他錯誤
                LOGGER.warn(servletRequest.getRemoteAddr()+"JWT認證"+e.getMessage(),e);
                // 告知客戶端JWT錯誤1005,需重新登錄申請jwt
                Message message = new Message().error(1007,"error jwt");
                RequestResponseUtil.responseWrite(JSON.toJSONString(message),servletResponse);
                return false;
            }
        }else {
            // 請求未攜帶jwt 判斷為無效請求
            Message message = new Message().error(1111,"error request");
            RequestResponseUtil.responseWrite(JSON.toJSONString(message),servletResponse);
            return false;
        }
    }


效果展示




github:
bootshiro
usthe

碼云:
bootshiro
usthe



持續(xù)更新。。。。。。


分享一波阿里云代金券快速上云

轉(zhuǎn)載請注明 from tomsun28

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/69182.html

相關(guān)文章

  • 前后端分離——token超時刷新策略

    摘要:實現(xiàn)目標延長過期時間活躍用戶在過期時,在用戶無感知的情況下動態(tài)刷新,做到一直在線狀態(tài)不活躍用戶在過期時,直接定向到登錄頁登錄返回字段如何簽發(fā),請看上一篇推文,這里不做過多介紹。如果你有更好的做法,歡迎留言告知我,謝謝啦。 前言 記錄一下前后端分離下————token超時刷新策略! 需求場景 昨天發(fā)了一篇記錄 前后端分離應(yīng)用——用戶信息傳遞 中介紹了token認證機制,跟幾位群友討論了...

    hatlonely 評論0 收藏0
  • api權(quán)限管理系統(tǒng)與前后端分離實踐

    摘要:自己在前后端分離上的實踐要想實現(xiàn)完整的前后端分離,安全這塊是繞不開的,這個系統(tǒng)主要功能就是動態(tài)管理,這次實踐包含兩個模塊基于搭建的權(quán)限管理系統(tǒng)后臺編寫的前端管理。 自己在前后端分離上的實踐 要想實現(xiàn)完整的前后端分離,安全這塊是繞不開的,這個系統(tǒng)主要功能就是動態(tài)restful api管理,這次實踐包含兩個模塊,基于springBoot + shiro搭建的權(quán)限管理系統(tǒng)后臺bootshir...

    bawn 評論0 收藏0
  • api權(quán)限管理系統(tǒng)與前后端分離實踐

    摘要:自己在前后端分離上的實踐要想實現(xiàn)完整的前后端分離,安全這塊是繞不開的,這個系統(tǒng)主要功能就是動態(tài)管理,這次實踐包含兩個模塊基于搭建的權(quán)限管理系統(tǒng)后臺編寫的前端管理。 自己在前后端分離上的實踐 要想實現(xiàn)完整的前后端分離,安全這塊是繞不開的,這個系統(tǒng)主要功能就是動態(tài)restful api管理,這次實踐包含兩個模塊,基于springBoot + shiro搭建的權(quán)限管理系統(tǒng)后臺bootshir...

    tianlai 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<