spring security結(jié)合jwt實(shí)現(xiàn)用戶重復(fù)登錄處理
背景
近日,客戶針對(duì)我司系統(tǒng)做一些列漏洞掃描,最后總結(jié)通用漏洞有如下:
- 用戶重復(fù)登錄
- 接口未授權(quán)
- 接口越權(quán)訪問(wèn)
針對(duì)以上漏洞,分三篇文章分別記錄解決方案,供后續(xù)回憶學(xué)習(xí),本文先處理用戶重復(fù)登錄漏洞
方案
系統(tǒng)采用spring boot搭建,spring security+ jwt 作為安全框架
- 用戶登錄成功 生成token給到用戶, 同時(shí)存儲(chǔ)到redis中(key值為用戶名(標(biāo)識(shí))) value為生成的token
- 用戶再次訪問(wèn)系統(tǒng)請(qǐng)求參數(shù)中帶有token信息 后臺(tái)通過(guò)過(guò)濾器進(jìn)行攔截進(jìn)行比對(duì)
- 如果token匹配成功 就放行 匹配不成功 說(shuō)明兩個(gè)token不一致 開(kāi)始比對(duì)對(duì)應(yīng)的時(shí)間戳 后者時(shí)間戳 大于前者就把當(dāng)前token覆蓋(如果舊的token請(qǐng)求再次進(jìn)來(lái) 期時(shí)間戳就晚于當(dāng)前redis中的token時(shí)間(token已經(jīng)更新)判斷其為被踢出的用戶提示重新登錄)
思路圖

核心代碼
- 配置過(guò)濾器

- 過(guò)濾器實(shí)現(xiàn)
RepeatLoginFilter.java
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String jwt = resolveToken(request);
//重復(fù)登錄只校驗(yàn)帶有合法jwt的 放過(guò)驗(yàn)證碼以及免鑒權(quán)的那些請(qǐng)求
if (null == jwt || "null".equalsIgnoreCase(jwt) || !this.tokenProvider.validateToken(jwt)) {
filterChain.doFilter(request, response);
return;
}
if (isAccessAllowed(jwt)) {
filterChain.doFilter(request, response);
return;
}
// 賬號(hào)重復(fù)登錄,跳轉(zhuǎn)登錄頁(yè)面 logout
logout(response);
}
/**
* 重復(fù)登錄核心校驗(yàn)
* @param token 當(dāng)前token
* @return true通過(guò)放行
*/
private boolean isAccessAllowed(String token) {
Authentication authentication = this.tokenProvider.getAuthentication(token);
String redisToken = redisTemplate.opsForValue().get(Constants.PREFIX_LOGIN_USER+authentication.getName());
//redisToken為空 說(shuō)明第一次登陸 放過(guò)并加入redis
if(!StringUtils.hasLength(redisToken)){
redisTemplate.opsForValue().set(Constants.PREFIX_LOGIN_USER+authentication.getName(),token,
86400, TimeUnit.SECONDS);
return true;
}
//redis token和當(dāng)前token一致 說(shuō)明是當(dāng)前登陸用戶訪問(wèn) 放過(guò)
if(token.equals(redisToken)){
return true;
}
//redis token和當(dāng)前token不一致,比較兩個(gè)token的創(chuàng)建時(shí)間,如果當(dāng)前token大于redistoken 就說(shuō)當(dāng)前是最新的,放入redis并放過(guò)
//否則就說(shuō)明redis里已有最新的token,當(dāng)前token應(yīng)該過(guò)期,不放行
Date date = this.tokenProvider.getIssueAt(token);
Date redisDate = this.tokenProvider.getIssueAt(redisToken);
if(date.after(redisDate)){
redisTemplate.opsForValue().set(Constants.PREFIX_LOGIN_USER+authentication.getName(),token,
86400, TimeUnit.SECONDS);
return true;
}else{
return false;
}
}
/**
* 獲取jwt
/
private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
/**
* 返回退出信息到前臺(tái)
* @param response
*/
private void logout(HttpServletResponse response){
response.setStatus(HttpStatus.FORBIDDEN.value());
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
try {
JSONObject resultObj = new JSONObject();
resultObj.putOnce("data","賬號(hào)已在別的地方登錄,請(qǐng)重新登錄");
response.getWriter().print(resultObj.toString());
} catch (IOException e) {
e.printStackTrace();
}
}到此這篇關(guān)于spring security結(jié)合jwt實(shí)現(xiàn)用戶重復(fù)登錄處理的文章就介紹到這了,更多相關(guān)spring security jwt重復(fù)登錄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于maven install 沒(méi)反應(yīng)的解決方法
下面小編就為大家?guī)?lái)一篇基于maven install 沒(méi)反應(yīng)的解決方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06
Spring Boot配置application.yml及根據(jù)application.yml選擇啟動(dòng)配置的操作
Spring Boot中可以選擇applicant.properties 作為配置文件,也可以通過(guò)在application.yml中進(jìn)行配置,讓Spring Boot根據(jù)你的選擇進(jìn)行加載啟動(dòng)配置文件,本文給大家介紹Spring Boot配置application.yml及根據(jù)application.yml選擇啟動(dòng)配置的操作方法,感興趣的朋友一起看看吧2023-10-10
hibernate關(guān)于session的關(guān)閉實(shí)例解析
這篇文章主要介紹了hibernate關(guān)于session的關(guān)閉實(shí)例解析,分享了相關(guān)代碼示例,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-02-02
java實(shí)現(xiàn)租車(chē)系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)租車(chē)系統(tǒng),以及遇到的兩個(gè)問(wèn)題解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01
IDEA2020如何打開(kāi)Run Dashboard的方法步驟
這篇文章主要介紹了IDEA2020如何打開(kāi)Run Dashboard的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
Java使用wait() notify()方法操作共享資源詳解
這篇文章主要為大家詳細(xì)介紹了Java使用wait() notify()方法操作共享資源,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10

