最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java接口用戶(hù)上下文的設(shè)計(jì)與實(shí)現(xiàn)

 更新時(shí)間:2023年11月03日 08:55:01   作者:sum墨  
這篇文章主要為大家介紹了接口用戶(hù)上下文的設(shè)計(jì)與實(shí)現(xiàn)實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

作為一名從業(yè)已達(dá)六年的老碼農(nóng),我的工作主要是開(kāi)發(fā)后端Java業(yè)務(wù)系統(tǒng),包括各種管理后臺(tái)和小程序等。在這些項(xiàng)目中,我設(shè)計(jì)過(guò)單/多租戶(hù)體系系統(tǒng),對(duì)接過(guò)許多開(kāi)放平臺(tái),也搞過(guò)消息中心這類(lèi)較為復(fù)雜的應(yīng)用,但幸運(yùn)的是,我至今還沒(méi)有遇到過(guò)線(xiàn)上系統(tǒng)由于代碼崩潰導(dǎo)致資損的情況。這其中的原因有三點(diǎn):一是業(yè)務(wù)系統(tǒng)本身并不復(fù)雜;二是我一直遵循某大廠代碼規(guī)約,在開(kāi)發(fā)過(guò)程中盡可能按規(guī)約編寫(xiě)代碼;三是經(jīng)過(guò)多年的開(kāi)發(fā)經(jīng)驗(yàn)積累,我成為了一名熟練工,掌握了一些實(shí)用的技巧。

考慮到文字太過(guò)寡淡,我先上一張圖

在Spring Boot中,默認(rèn)情況下,每個(gè)請(qǐng)求到達(dá)時(shí)都會(huì)分配一個(gè)單獨(dú)的線(xiàn)程來(lái)處理,而且請(qǐng)求的發(fā)起人也不一定都是同一個(gè)人,所以一個(gè)請(qǐng)求對(duì)應(yīng)一個(gè)用戶(hù)上下文,并且要求線(xiàn)程隔離,即不同線(xiàn)程的用戶(hù)上下文互不影響,最后用戶(hù)上下文還需要隨著線(xiàn)程的結(jié)束而刪除。
本文我會(huì)從用戶(hù)上下文如何構(gòu)建、如何使用、如何刪除這三個(gè)方面解釋接口用戶(hù)上下文的設(shè)計(jì)與實(shí)現(xiàn)。

本文參考項(xiàng)目源碼地址:summo-springboot-interface-demo

一、接口用戶(hù)上下文的構(gòu)建、使用、清除

1. 利用Filter攔截到每一個(gè)請(qǐng)求

由于接口散落在各個(gè)Controller中,且絕大部分接口都是需要這個(gè)用戶(hù)上下文的(注:也不排除不需要用戶(hù)上下文的接口存在),所以這里需要統(tǒng)一入口進(jìn)行創(chuàng)建、銷(xiāo)毀。看起來(lái)可以使用AOP的方式來(lái)實(shí)現(xiàn),

不過(guò)這里有一個(gè)更合適的方案,利用SpringBoot自帶的Filter【javax.servlet.Filter】來(lái)實(shí)現(xiàn)。

實(shí)現(xiàn)起來(lái)非常簡(jiǎn)單,我這邊自定義了一個(gè)WebFilter,代碼如下:

WebFilter.java

package com.summo.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.summo.context.GlobalUserContext;
import com.summo.context.UserContext;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class WebFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
        throws IOException, ServletException {
        try {
            //獲取本次接口的唯一碼
            String token = java.util.UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
            MDC.put("requestId", token);
            //獲取請(qǐng)求頭
            HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest;
            HttpServletResponse httpServletResponse = (HttpServletResponse)servletResponse;
            log.info("當(dāng)前請(qǐng)求鏈接為:[{}]", httpServletRequest.getRequestURL());
            //設(shè)置用戶(hù)上下文
            UserContext userContext = new UserContext();
            userContext.setUserId(1L);
            GlobalUserContext.setUserContext(userContext);
            //執(zhí)行doFilter,這行一定要加,否則程序會(huì)中斷掉
            filterChain.doFilter(httpServletRequest, httpServletResponse);
        } catch (Exception e) {
            log.error("do doFilter exception", e);
        } finally {
            GlobalUserContext.clear();
            MDC.remove("requestId");
        }
    }
    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

 這段代碼的核心方法是:public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)我們可以在這個(gè)方法里面獲取到ServletRequest和ServletResponse,這兩個(gè)類(lèi)能獲取到代表著我們可以操作整個(gè)請(qǐng)求過(guò)程,這里如何確定當(dāng)前請(qǐng)求的用戶(hù)?下面有一張流程圖供大家參考:

還有一種做法是使用JWT來(lái)當(dāng)做用戶(hù)token,因?yàn)镴WT本身就可以存儲(chǔ)一些信息,所以我們就不需要去緩存用戶(hù)信息了,直接解析JWT即可,這種做法在分布式應(yīng)用中很常見(jiàn)。

2. 獲取當(dāng)前請(qǐng)求的線(xiàn)程

上面已經(jīng)獲取到用戶(hù)信息了,現(xiàn)在需要將用戶(hù)信息放入用戶(hù)上下文中,但由于請(qǐng)求的發(fā)起人不一定都是同一個(gè)人,所以一個(gè)請(qǐng)求對(duì)應(yīng)著一個(gè)用戶(hù)上下文,也即一個(gè)線(xiàn)程設(shè)置一個(gè)上下文。那么這里就需要獲取到當(dāng)前線(xiàn)程才能設(shè)置上下文。

獲取當(dāng)前線(xiàn)程有很多辦法,這里推薦使用阿里巴巴開(kāi)源的TTL框架(TransmittableThreadLocal)來(lái)實(shí)現(xiàn),功能強(qiáng)大且用法簡(jiǎn)單。

引入方法如下:

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>transmittable-thread-local</artifactId>
  <version>2.11.1</version>
</dependency>

使用方法如下:

private static final TransmittableThreadLocal<UserContext> USER_HOLDER = new TransmittableThreadLocal<>();

直接new一個(gè)對(duì)象就行,而且支持泛型。

3. 用戶(hù)上下文生命周期管理

對(duì)于用戶(hù)上下文的生命周期管理需要定義3個(gè)方法:

  • 設(shè)置上下文用戶(hù)信息;
  • 獲取上下文用戶(hù)信息

清除上下文用戶(hù)信息

以上方法均為靜態(tài)方法。

下面是一個(gè)簡(jiǎn)單的例子:

GlobalUserContext.java

package com.summo.context;
import com.alibaba.ttl.TransmittableThreadLocal;
public class GlobalUserContext {
    private static final TransmittableThreadLocal<UserContext> USER_HOLDER = new TransmittableThreadLocal<>();
    /**
     * 設(shè)置上下文用戶(hù)信息
     *
     * @param user 用戶(hù)信息
     */
    public static void setUserContext(UserContext user) {
        USER_HOLDER.set(user);
    }
    /**
     * 獲取上下文用戶(hù)信息
     */
    public static UserContext getUserContext() {
        return USER_HOLDER.get();
    }
    /**
     * 清除上下文用戶(hù)信息
     */
    public static void clear() {
        USER_HOLDER.remove();
    }
}

UserContext.java

package com.summo.context;
import lombok.Data;
@Data
public class UserContext {
    /**
     * 用戶(hù)ID
     */
    private Long userId;
}

調(diào)用方式如下:

設(shè)置上下文用戶(hù)信息:GlobalUserContext.setUserContext(userContext);
獲取上下文用戶(hù)信息:GlobalUserContext.getUserContext();
清除上下文用戶(hù)信息:GlobalUserContext.clear();

4. 用戶(hù)上下文的使用

獲取用戶(hù)上下文很方便,調(diào)用GlobalUserContext.getUserContext();就行了,這里我主要講一下用戶(hù)上下文的使用場(chǎng)景。

a. 身份認(rèn)證

可以將用戶(hù)的身份認(rèn)證信息(如用戶(hù)名、密碼、權(quán)限等)保存在用戶(hù)上下文中,在需要進(jìn)行鑒權(quán)的地方進(jìn)行驗(yàn)證。

b. 用戶(hù)日志記錄

正如《優(yōu)化接口設(shè)計(jì)的思路》系列:第三篇—在用戶(hù)使用系統(tǒng)過(guò)程中留下痕跡 的方法三.

c. 防止接口數(shù)據(jù)越權(quán)

舉個(gè)例子,比如有些業(yè)務(wù)需要獲取當(dāng)前登錄用戶(hù)的信息、當(dāng)前登錄用戶(hù)的收藏、當(dāng)前登錄用戶(hù)的瀏覽記錄,這樣的接口總不能在接口上傳一個(gè)userId吧?真要這樣干了,非得給安全罵死。。。

利用用戶(hù)上下文的話(huà),接口就可以不用傳遞任何參數(shù)獲取到當(dāng)前用戶(hù)的userId,實(shí)現(xiàn)你的需求啦。

d. 跨服務(wù)調(diào)用

在分布式系統(tǒng)中,可以將用戶(hù)上下文信息傳遞給其他服務(wù),以保持用戶(hù)的一致性和連貫性。

e. 監(jiān)控和統(tǒng)計(jì)

可以將用戶(hù)上下文中的信息用于系統(tǒng)的監(jiān)控和統(tǒng)計(jì),如請(qǐng)求的處理時(shí)間、請(qǐng)求的次數(shù)等。

5. 用戶(hù)上下文的刪除

刪除很簡(jiǎn)單,調(diào)用GlobalUserContext.clear();即可,詳情可見(jiàn)WebFilter.java內(nèi)容。

二. 用戶(hù)登錄&認(rèn)證

上面主要是說(shuō)怎么獲取到接口請(qǐng)求的用戶(hù)以及怎么設(shè)置用戶(hù)上下文,但沒(méi)說(shuō)用戶(hù)身份是什么時(shí)候確認(rèn)的以及怎么確認(rèn)的,這里說(shuō)一下常見(jiàn)做法。
想要確認(rèn)用戶(hù)信息就不得不提到用戶(hù)登錄&認(rèn)證這套東西了,登錄的方式非常多,簡(jiǎn)單的有賬號(hào)密碼登錄、手機(jī)驗(yàn)證碼登錄,復(fù)雜的就是單點(diǎn)登錄、三方授權(quán)登錄如微信掃碼、支付寶掃碼等。雖然方式多,但是結(jié)果都一樣的:確認(rèn)當(dāng)前用戶(hù)身份。

當(dāng)前用戶(hù)身份確認(rèn)好之后,系統(tǒng)一般會(huì)根據(jù)當(dāng)前用戶(hù)信息生成一個(gè)唯一的并帶有時(shí)效性的token,放入下一次請(qǐng)求的cookie中。等到下一次請(qǐng)求來(lái)的時(shí)候,我們就可以從cookie中獲取這個(gè)token,利用這個(gè)token獲取這個(gè)用戶(hù)的信息。

由于用戶(hù)認(rèn)證情況太多,這里我就不貼代碼了,上面是賬號(hào)密碼登錄用戶(hù)認(rèn)證的的時(shí)序圖,供大家參考。

以上就是接口用戶(hù)上下文的設(shè)計(jì)與實(shí)現(xiàn)的詳細(xì)內(nèi)容,更多關(guān)于接口用戶(hù)上下文的設(shè)計(jì)與實(shí)現(xiàn)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Spring MVC Mybatis多數(shù)據(jù)源的使用實(shí)例解析

    Spring MVC Mybatis多數(shù)據(jù)源的使用實(shí)例解析

    項(xiàng)目需要從其他網(wǎng)站獲取數(shù)據(jù),因?yàn)槭桥R時(shí)加的需求,這篇文章主要介紹了Spring MVC Mybatis多數(shù)據(jù)源的使用實(shí)例解析,需要的朋友可以參考下
    2016-12-12
  • java通過(guò)isAccessAllowed方法實(shí)現(xiàn)訪(fǎng)問(wèn)控制

    java通過(guò)isAccessAllowed方法實(shí)現(xiàn)訪(fǎng)問(wèn)控制

    在Web應(yīng)用開(kāi)發(fā)中,使用Apache Shiro框架的isAccessAllowed方法可以有效管理用戶(hù)的訪(fǎng)問(wèn)權(quán)限,本文詳細(xì)解析了該方法的實(shí)現(xiàn)過(guò)程,包括用戶(hù)身份驗(yàn)證、權(quán)限判斷和安全性分析,下面就一起來(lái)了解一下
    2024-09-09
  • 詳解spring boot集成RabbitMQ

    詳解spring boot集成RabbitMQ

    RabbitMQ作為AMQP的代表性產(chǎn)品,在項(xiàng)目中大量使用。結(jié)合現(xiàn)在主流的spring boot,極大簡(jiǎn)化了開(kāi)發(fā)過(guò)程中所涉及到的消息通信問(wèn)題。
    2017-03-03
  • Spring @CrossOrigin 注解原理實(shí)現(xiàn)

    Spring @CrossOrigin 注解原理實(shí)現(xiàn)

    這篇文章主要介紹了Spring @CrossOrigin 注解原理實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • Java實(shí)現(xiàn)Excel轉(zhuǎn)化為圖片的代碼詳解

    Java實(shí)現(xiàn)Excel轉(zhuǎn)化為圖片的代碼詳解

    在實(shí)際開(kāi)發(fā)里,常常存在將Excel表格或特定區(qū)域轉(zhuǎn)換為圖片,以便在其他軟件中使用的需求,在Java開(kāi)發(fā)中,借助報(bào)表插件能夠輕松實(shí)現(xiàn)將工作表、任意指定區(qū)域以及各種形狀類(lèi)型轉(zhuǎn)換為圖片,本文將詳細(xì)介紹如何使用Java達(dá)成Excel到圖片的轉(zhuǎn)化,需要的朋友可以參考下
    2025-08-08
  • java計(jì)算兩個(gè)日期之前的天數(shù)實(shí)例(排除節(jié)假日和周末)

    java計(jì)算兩個(gè)日期之前的天數(shù)實(shí)例(排除節(jié)假日和周末)

    下面小編就為大家?guī)?lái)一篇java計(jì)算兩個(gè)日期之前的天數(shù)實(shí)例(排除節(jié)假日和周末)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-07-07
  • IDEA調(diào)試小技巧之Evaluate調(diào)試工具詳解

    IDEA調(diào)試小技巧之Evaluate調(diào)試工具詳解

    這篇文章主要介紹了IDEA調(diào)試小技巧之Evaluate調(diào)試工具,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • Java ThreadLocal有什么作用你知道嗎

    Java ThreadLocal有什么作用你知道嗎

    這篇文章主要為大家詳細(xì)介紹了java ThreadLocal的作用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2021-09-09
  • 使用自定義Json注解實(shí)現(xiàn)輸出日志字段脫敏

    使用自定義Json注解實(shí)現(xiàn)輸出日志字段脫敏

    這篇文章主要介紹了使用自定義Json注解實(shí)現(xiàn)輸出日志字段脫敏,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • java LeetCode題解KMP算法示例

    java LeetCode題解KMP算法示例

    這篇文章主要為大家介紹了java LeetCode題解KMP算法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10

最新評(píng)論

瓦房店市| 慈利县| 湖北省| 左云县| 江永县| 邯郸市| 阜阳市| 古交市| 水富县| 安图县| 昌吉市| 黑水县| 邳州市| 上高县| 宁海县| 泰宁县| 鹤壁市| 岗巴县| 芮城县| 桦南县| 金溪县| 福泉市| 安阳市| 绍兴市| 杭州市| 枞阳县| 谷城县| 甘肃省| 靖远县| 海兴县| 井陉县| 金湖县| 大宁县| 平利县| 腾冲县| 准格尔旗| 西林县| 新津县| 宾川县| 唐河县| 临颍县|