教你使用springSecurity+jwt實現(xiàn)互踢功能
jwt介紹:
? ? ? ? JWT是一種用于雙方之間傳遞安全信息的簡潔的、URL安全的表述性聲明規(guī)范。JWT作為一個開放的標準( RFC 7519 ),定義了一種簡潔的,自包含的方法用于通信雙方之間以Json對象的形式安全的傳遞信息。
jwt認證流程介紹:
1. 用戶使用賬號和面發(fā)出post請求;?
2. 服務(wù)器使用私鑰創(chuàng)建一個jwt;?
3. 服務(wù)器返回這個jwt給瀏覽器;?
4. 瀏覽器將該jwt串在請求頭中像服務(wù)器發(fā)送請求;?
5. 服務(wù)器驗證該jwt;?
6. 返回響應(yīng)的資源給瀏覽器。

一.思路:
原來的實現(xiàn)用戶登錄態(tài)是:
1.后臺登陸成功后生成一個令牌(uuid)----JwtAuthenticationSuccessHandler
2.后臺把它包裝成jwt數(shù)據(jù),然后返回給前端—JwtAuthenticationSuccessHandler
3.后臺把它加入redis緩存中,并設(shè)置失效時間----JwtAuthenticationSuccessHandler
4.前端調(diào)用接口時,帶入jwt
5.后臺寫個攔截器或者過濾器,在前端調(diào)用接口的時候,從request的header中獲取jwt,在緩存中搜索,如果存在則處于登錄態(tài),并重置失效時間(這樣用戶在有效時間內(nèi)就處于登錄態(tài))—JwtSecurityContextRepository
6.解釋下:springSecurity是個過濾器璉,是由一個一個的過濾器組成的
現(xiàn)在的互踢:
1.后臺在登陸成功后,用用戶id組成一個key,查詢redis緩存中的value
2.和新的jwt比較,如果不一樣則把查到的jwt當做key從redis中刪掉,就是上面第三步存儲的
3.把用戶id組成一個key,把上面jwt當做value傳入緩存中
4.在上面的第5步,也重置下我們這里存儲的值
*上面 指的是原來的實現(xiàn)用戶登錄態(tài)
package com.lc.gansu.security.component.jwt;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lc.gansu.framework.core.ConstantOfReturnCode;
import com.lc.gansu.framework.core.RedisKey;
import com.lc.gansu.framework.core.ReturnObject;
import com.lc.gansu.security.component.SecurityConstants;
import com.lc.gansu.security.domain.User;
import com.lc.gansu.security.utility.JWTHS256;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Slf4j
public class JwtAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
//private final RequestCache requestCache = new HttpSessionRequestCache();
private final ObjectMapper jacksonObjectMapper;
private final RedisTemplate<String, Object> redisTemplate;
public JwtAuthenticationSuccessHandler(ObjectMapper jacksonObjectMapper, RedisTemplate<String, Object> redisTemplate) {
this.jacksonObjectMapper = jacksonObjectMapper;
this.redisTemplate = redisTemplate;
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
log.info("JwtAuthenticationSuccessHandler=success");
clearAuthenticationAttributes(request);
handle(response, authentication);
}
protected final void clearAuthenticationAttributes(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null) return;
session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
}
protected void handle(HttpServletResponse response, Authentication authentication) throws IOException {
if (response.isCommitted()) {
log.debug("Response has already been committed.");
return;
}
User sysUser = (User) authentication.getPrincipal();
sysUser.setClazz(authentication.getClass());
//AuthenticationAdapter authenticationAdapter=AuthenticationAdapter.authentication2AuthenticationAdapter(authentication);
String authOfjson = jacksonObjectMapper.writeValueAsString(sysUser);
String subject = UUID.randomUUID().toString();
String authOfjwt = JWTHS256.buildJWT(subject, authOfjson);
response.addHeader("jwt", authOfjwt);
//跨域時允許header攜帶jwt
response.addHeader("Access-Control-Expose-Headers" ,"jwt");
redisTemplate.boundValueOps(SecurityConstants.getJwtKey(subject)).set("w", 60, TimeUnit.MINUTES);
//---------互踢start-------------
// 在緩存中傳入key="R_V_USERIDLOGINKEY_" + userId + "_LOGIN",value=SecurityConstants.getJwtKey(subject),有新登錄時如果用戶一樣則把緩存里之前的jwt刪除,這個:(redisTemplate.boundValueOps(SecurityConstants.getJwtKey(subject)).set("w", 60, TimeUnit.MINUTES);)
log.info("設(shè)置jwt,并在緩存中傳入:{}",SecurityConstants.getJwtKey(subject));
String uuid = (String) redisTemplate.opsForValue().get(RedisKey.getUserIdKey(sysUser.getId()));
log.info("查詢原有jwt:{}",uuid);
if(!SecurityConstants.getJwtKey(subject).equals(uuid)&& Objects.nonNull(uuid)){
assert uuid != null;
redisTemplate.delete(uuid);
log.info("刪除原有jwt:{}",uuid);
}
redisTemplate.opsForValue().set(RedisKey.getUserIdKey(sysUser.getId()), SecurityConstants.getJwtKey(subject),60, TimeUnit.MINUTES);
log.info("在緩存里塞入新jwt:{}",SecurityConstants.getJwtKey(subject));
//---------互踢end----------------------
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
User returnSysUser = new User();
returnSysUser
.setName(sysUser.getName())
.setCurrentOrg(sysUser.getCurrentOrg())
.setOrgIdMapRoleList(sysUser.getOrgIdMapRoleList())
.setCurrentMenuList(sysUser.getCurrentMenuList())
.setOrgList(sysUser.getOrgList());
out.write(jacksonObjectMapper.writeValueAsString(new ReturnObject<>(this.getClass().getName(), ConstantOfReturnCode.GLOBAL_RESULT_SUCESS, "登錄成功", returnSysUser)));
out.flush();
out.close();
}
}
package com.lc.gansu.security.component.jwt;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lc.gansu.framework.core.RedisKey;
import com.lc.gansu.security.component.SecurityConstants;
import com.lc.gansu.security.domain.User;
import com.lc.gansu.security.utility.JWTHS256;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpRequestResponseHolder;
import org.springframework.security.web.context.SecurityContextRepository;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@Slf4j
public class JwtSecurityContextRepository implements SecurityContextRepository {
protected final Log logger = LogFactory.getLog(this.getClass());
private final RedisTemplate<String, Object> redisTemplate;
private final ObjectMapper jacksonObjectMapper;
public JwtSecurityContextRepository(RedisTemplate<String, Object> redisTemplate, ObjectMapper jacksonObjectMapper) {
this.redisTemplate = redisTemplate;
this.jacksonObjectMapper = jacksonObjectMapper;
}
@Override
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
HttpServletRequest request = requestResponseHolder.getRequest();
return readSecurityContextFromJWT(request);
}
@Override
public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
}
@Override
public boolean containsContext(HttpServletRequest request) {
return false;
}
private SecurityContext readSecurityContextFromJWT(HttpServletRequest request) {
SecurityContext context = generateNewContext();
String authenticationOfjwt = request.getHeader("jwt");
if (StringUtils.isNotBlank(authenticationOfjwt)) {
try {
Map<String, Object> map = JWTHS256.vaildToken(authenticationOfjwt);
if (Objects.nonNull(map) && map.size() == 2) {
String subject = (String) map.get("subject");
Boolean isExp = redisTemplate.hasKey(SecurityConstants.getJwtKey(subject));
if (Objects.nonNull(isExp) && isExp) {//redis key 未過期
redisTemplate.expire(SecurityConstants.getJwtKey(subject), 60, TimeUnit.MINUTES);//延期
String obj = (String) map.get("claim");
//AuthenticationAdapter authenticationAdapter=jacksonObjectMapper.readValue(obj, new TypeReference<>(){});
//Authentication authentication=AuthenticationAdapter.authenticationAdapter2Authentication(authenticationAdapter);
//Authentication authentication=jacksonObjectMapper.readValue(obj, new TypeReference<>(){});
//Authentication authentication=jacksonObjectMapper.readValue(obj,Authentication.class);
User sysUser = jacksonObjectMapper.readValue(obj, new TypeReference<User>() {
});
Authentication authentication = new UsernamePasswordAuthenticationToken(sysUser, null, sysUser.getAuthorities());
context.setAuthentication(authentication);
//-----互踢start-------
if(Objects.nonNull(RedisKey.getUserIdKey(sysUser.getId()))) redisTemplate.expire(RedisKey.getUserIdKey(sysUser.getId()), 60, TimeUnit.MINUTES);
//-----互踢end---------
//if(obj instanceof Authentication){
//context.setAuthentication((Authentication)obj);
//}else log.error("jwt包含authentication的數(shù)據(jù)非法");
} else log.error("jwt數(shù)據(jù)過期");
} else log.error("jwt數(shù)據(jù)非法");
} catch (Exception e) {
e.printStackTrace();
logger.error(e.getLocalizedMessage());
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("No JWT was available from the HttpServletRequestHeader!");
}
}
return context;
}
protected SecurityContext generateNewContext() {
return SecurityContextHolder.createEmptyContext();
}
}
package com.lc.gansu.framework.core;
/**
* TODO
*
* @author songtianxiong
* @version 1.0
* @date 2021/11/16 19:15
*/
public class RedisKey {
//線上投放計劃反饋結(jié)果催辦
public static String getOlpmIdAndUserIdRedisKey(Long OlpmId, Long userId) {
return "R_V_OLPMURGE_" + OlpmId + "_" + userId;
}
//催辦
public static String getOdpIdAndUserIdRedisKey(Long OdpId, Long userId) {
return "R_V_ODPMURGE_" + OdpId + "_" + userId;
}
//催辦
public static String getJwtAndUrl(String jwt, String url) {
return "R_V_REPEAT_" + jwt + "_" + url;
}
//用戶登錄互踢
public static String getUserIdKey(Long userId) {
return "R_V_USERIDLOGINKEY_" + userId + "_LOGIN";
}
}
---------------------------關(guān)鍵詞:互踢
關(guān)鍵代碼:
//---------互踢start-------------
// 在緩存中傳入key="R_V_USERIDLOGINKEY_" + userId + "_LOGIN",value=SecurityConstants.getJwtKey(subject),有新登錄時如果用戶一樣則把緩存里之前的jwt刪除,這個:(redisTemplate.boundValueOps(SecurityConstants.getJwtKey(subject)).set("w", 60, TimeUnit.MINUTES);)
log.info("設(shè)置jwt,并在緩存中傳入:{}",SecurityConstants.getJwtKey(subject));
String uuid = (String) redisTemplate.opsForValue().get(RedisKey.getUserIdKey(sysUser.getId()));
log.info("查詢原有jwt:{}",uuid);
if(!SecurityConstants.getJwtKey(subject).equals(uuid)&& Objects.nonNull(uuid)){
assert uuid != null;
redisTemplate.delete(uuid);
log.info("刪除原有jwt:{}",uuid);
}
redisTemplate.opsForValue().set(RedisKey.getUserIdKey(sysUser.getId()), SecurityConstants.getJwtKey(subject),60, TimeUnit.MINUTES);
log.info("在緩存里塞入新jwt:{}",SecurityConstants.getJwtKey(subject));
//---------互踢end----------------------
//-----互踢start-------
if(Objects.nonNull(RedisKey.getUserIdKey(sysUser.getId()))) redisTemplate.expire(RedisKey.getUserIdKey(sysUser.getId()), 60, TimeUnit.MINUTES);
//-----互踢end---------
//用戶登錄互踢
public static String getUserIdKey(Long userId) {
return "R_V_USERIDLOGINKEY_" + userId + "_LOGIN";
}
到此這篇關(guān)于springSecurity+jwt中實現(xiàn)互踢功能的文章就介紹到這了,更多相關(guān)springSecurity+jwt互踢內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java基礎(chǔ)詳解之集合框架工具Collections
這篇文章主要介紹了Java基礎(chǔ)詳解之集合框架工具Collections,文中有非常詳細的代碼示例,對正在學(xué)習(xí)java的小伙伴們有很好地幫助,需要的朋友可以參考下2021-04-04
JavaWeb中struts2實現(xiàn)文件上傳下載功能實例解析
這篇文章主要介紹了JavaWeb中struts2文件上傳下載功能的實現(xiàn),在Web應(yīng)用系統(tǒng)開發(fā)中,文件上傳和下載功能是非常常用的功能,需要的朋友可以參考下2016-05-05
Java VisualVM監(jiān)控遠程JVM(詳解)
下面小編就為大家?guī)硪黄狫ava VisualVM監(jiān)控遠程JVM(詳解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10

