使用 spring 攔截器和自定義注解進(jìn)行登錄攔截

為何要用自定義注解

有些方法我們想要它只能被特定的用戶訪問到令境,比如用戶登錄之后才能訪問。spring 的攔截器可以配置攔截的路由顾瞪,但在 restful 風(fēng)格的路由中舔庶,往往有重復(fù)的,根據(jù) http method 來指定功能陈醒,這樣子的話直接配置攔截器路由規(guī)則也不太方便惕橙。所以我們可以自定義一個(gè)注解,將它用在需要登錄的方法中钉跷,然后在攔截器中判斷要訪問的方法是否有我們自定義的注解弥鹦,如果有就判斷當(dāng)前用戶是否登錄了(判斷是否攜帶了登錄之后獲取到的 token ),從而決定是否攔截爷辙。

編寫一個(gè)自定義注解

這篇文章新增的文件如下


目錄結(jié)構(gòu)

新增 LoginRequired.java

/**
 * 在需要登錄驗(yàn)證的Controller的方法上使用此注解
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {
}

ElementType.MeTHOD 表示該自定義注解可以用在方法上
RetentionPolicy.RUNTIME 表示該注解在代碼運(yùn)行時(shí)起作用

編寫登錄攔截器

新增 AuthenticationInterceptor.java

public class AuthenticationInterceptor implements HandlerInterceptor {
    @Autowired
    private UserService userService;

    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws Exception {
        // 如果不是映射到方法直接通過
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();

        // 判斷接口是否需要登錄
        LoginRequired methodAnnotation = method.getAnnotation(LoginRequired.class);
        // 有 @LoginRequired 注解彬坏,需要認(rèn)證
        if (methodAnnotation != null) {
            // 執(zhí)行認(rèn)證
            String token = request.getHeader("token");  // 從 http 請求頭中取出 token
            if (token == null) {
                throw new RuntimeException("無token,請重新登錄");
            }
            int userId;
            try {
                userId = Integer.parseInt(JWT.decode(token).getAudience().get(0));  // 獲取 token 中的 user id
            } catch (JWTDecodeException e) {
                throw new RuntimeException("token無效膝晾,請重新登錄");
            }
            User user = userService.findById(userId);
            if (user == null) {
                throw new RuntimeException("用戶不存在栓始,請重新登錄");
            }
            // 驗(yàn)證 token
            try {
                JWTVerifier verifier =  JWT.require(Algorithm.HMAC256(user.getPassword())).build();
                try {
                    verifier.verify(token);
                } catch (JWTVerificationException e) {
                    throw new RuntimeException("token無效,請重新登錄");
                }
            } catch (UnsupportedEncodingException ignore) {}
            request.setAttribute("currentUser", user);
            return true;
        }
        return true;
    }

token 的驗(yàn)證過程和 token 的生成過程有關(guān)血当,在用戶登錄接口中幻赚,我使用的是用戶的密碼左右 token 的密鑰進(jìn)行加密(因?yàn)榉?wù)器并沒有對 token 進(jìn)行存儲(chǔ),所以加密的密鑰最好是一個(gè)用戶更改密碼之后會(huì)變的東西臊旭,我就直接用密碼了)落恼,還將 user id 存到了 JWT token 的 audience 中,因此我們能夠從 token 中知道用戶是誰巍扛。具體的JWT token 的生成和驗(yàn)證過程可以看看我們項(xiàng)目中使用的 jar 包的文檔

配置攔截器

spring boot 有很多默認(rèn)配置领跛,如果要添加攔截器之類的,就繼承 WebMvcConfigurerAdapter 類撤奸,Override 相應(yīng)的方法,來看看怎么添加我們剛剛編寫好的攔截器

新增 WebMvcConfigurer.java

@Configuration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authenticationInterceptor())
                .addPathPatterns("/**");    // 攔截所有請求喊括,通過判斷是否有 @LoginRequired 注解 決定是否需要登錄
        super.addInterceptors(registry);
    }

    @Bean
    public AuthenticationInterceptor authenticationInterceptor() {
        return new AuthenticationInterceptor();
    }
}

測試

在 userApi.java 里面添加一個(gè)臨時(shí)用的測試方法

@GetMapping("/test")
public Object testLogin() {
    return "success";
}

重啟項(xiàng)目
訪問 /api/user/test


不需要登錄

正常返回 “success” 字符串‰使希現(xiàn)在給 testLogin 方法加上自定義的 @LoginRequired 注解

@LoginRequired
@GetMapping("/test")
public Object testLogin() {
    return "success";
}

重啟項(xiàng)目,再次訪問 /api/use/test

無token

請求被登錄攔截器攔截了郑什,攔截器拋出異常府喳,由全局異常處理返回了錯(cuò)誤信息。
怎樣添加 token 呢蘑拯?訪問登錄接口钝满,復(fù)制返回的token兜粘,將它添加到 header 中
有 token

返回 success,請求成功弯蚜。測試完畢孔轴,將臨時(shí)添加的測試方法刪掉吧。

查看項(xiàng)目完整代碼

項(xiàng)目地址: https://github.com/hyrijk/spring-boot-blog
克隆項(xiàng)目到本地

git clone https://github.com/hyrijk/spring-boot-blog.git

checkout 到當(dāng)前版本

git checkout b7498954eba034b82b3619a3f07b62f48d390eb0

參考鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末碎捺,一起剝皮案震驚了整個(gè)濱河市路鹰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌收厨,老刑警劉巖晋柱,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異诵叁,居然都是意外死亡雁竞,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門拧额,熙熙樓的掌柜王于貴愁眉苦臉地迎上來浓领,“玉大人,你說我怎么就攤上這事势腮×罚” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵捎拯,是天一觀的道長泪幌。 經(jīng)常有香客問我,道長署照,這世上最難降的妖魔是什么祸泪? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮建芙,結(jié)果婚禮上没隘,老公的妹妹穿的比我還像新娘。我一直安慰自己禁荸,他們只是感情好右蒲,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著赶熟,像睡著了一般瑰妄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上映砖,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天间坐,我揣著相機(jī)與錄音,去河邊找鬼。 笑死竹宋,一個(gè)胖子當(dāng)著我的面吹牛劳澄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蜈七,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼秒拔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了宪潮?” 一聲冷哼從身側(cè)響起溯警,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎狡相,沒想到半個(gè)月后梯轻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡尽棕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年喳挑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滔悉。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡伊诵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出回官,到底是詐尸還是另有隱情曹宴,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布歉提,位于F島的核電站笛坦,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏苔巨。R本人自食惡果不足惜版扩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望侄泽。 院中可真熱鬧礁芦,春花似錦、人聲如沸悼尾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽诀豁。三九已至窄刘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間舷胜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留烹骨,地道東北人翻伺。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像沮焕,于是被迫代替她去往敵國和親吨岭。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理峦树,服務(wù)發(fā)現(xiàn)辣辫,斷路器,智...
    卡卡羅2017閱讀 134,654評(píng)論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,809評(píng)論 6 342
  • 標(biāo)記-清除算法(Mark-Sweep)1魁巩、標(biāo)記出所有需要回收的對象急灭,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對象2、在標(biāo)記...
    瘋狂的喵喵閱讀 360評(píng)論 0 6
  • 姑蘇,細(xì)雨肾扰,瘦馬畴嘶,黑衣,執(zhí)劍集晚,相望窗悯。 劍鳴泣,風(fēng)落雨偷拔,雙劍起蒋院,瞬分離,白紗去条摸,驚素顏悦污,卿羞赧,隱無蹤钉蒲。 竹林細(xì)溪切端,...
    魏幺九閱讀 991評(píng)論 33 7
  • 什么是記憶? 用心理學(xué)專業(yè)的話來說顷啼,記憶是過去經(jīng)驗(yàn)在頭腦里的反映踏枣。 具體來說,我們過去對事物的感知钙蒙、對問題的思考茵瀑、...
    叫我哆啦美閱讀 1,119評(píng)論 1 6